978 lines
64 KiB
Lua
978 lines
64 KiB
Lua
local deflate = (function () local a;do local b="1.0.2-release"local c="LibDeflate"local d=3;local e="LibDeflate "..b.." Copyright (C) 2018-2021 Haoqian He.".." Licensed under the zlib License"if LibStub then local f,g=LibStub:GetLibrary(c,true)if f and g and g>=d then return f else a=LibStub:NewLibrary(c,d)end else a={}end;a._VERSION=b;a._MAJOR=c;a._MINOR=d;a._COPYRIGHT=e end;local assert=assert;local error=error;local pairs=pairs;local h=string.byte;local i=string.char;local j=string.find;local k=string.gsub;local l=string.sub;local m=table.concat;local n=table.sort;local tostring=tostring;local type=type;local o={}local p={}local q={}local r={}local s={}local t={}local u={}local v={}local w={}local x={3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258}local y={0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}local z={[0]=1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577}local A={[0]=0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}local B={16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}local C;local D;local E;local F;local G;local H;local I;local J;for K=0,255 do p[K]=i(K)end;do local L=1;for K=0,32 do o[K]=L;L=L*2 end end;for K=1,9 do q[K]={}for M=0,o[K+1]-1 do local N=0;local O=M;for P=1,K do N=N-N%2+((N%2==1 or O%2==1)and 1 or 0)O=(O-O%2)/2;N=N*2 end;q[K][M]=(N-N%2)/2 end end;do local Q=18;local R=16;local S=265;local T=1;for U=3,258 do if U<=10 then r[U]=U+254;t[U]=0 elseif U==258 then r[U]=285;t[U]=0 else if U>Q then Q=Q+R;R=R*2;S=S+4;T=T+1 end;local V=U-Q-1+R/2;r[U]=(V-V%(R/8))/(R/8)+S;t[U]=T;s[U]=V%(R/8)end end end;do u[1]=0;u[2]=1;w[1]=0;w[2]=0;local Q=3;local R=4;local W=2;local T=0;for X=3,256 do if X>R then Q=Q*2;R=R*2;W=W+2;T=T+1 end;u[X]=X<=Q and W or W+1;w[X]=T<0 and 0 or T;if R>=8 then v[X]=(X-R/2-1)%(R/4)end end end;function a:Adler32(Y)if type(Y)~="string"then error(("Usage: LibDeflate:Adler32(str):".." \'str\' - string expected got \'%s\'."):format(type(Y)),2)end;local Z=#Y;local K=1;local Q=1;local R=0;while K<=Z-15 do local _,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,aa,ab,ac,ad,ae=h(Y,K,K+15)R=(R+16*Q+16*_+15*a0+14*a1+13*a2+12*a3+11*a4+10*a5+9*a6+8*a7+7*a8+6*a9+5*aa+4*ab+3*ac+2*ad+ae)%65521;Q=(Q+_+a0+a1+a2+a3+a4+a5+a6+a7+a8+a9+aa+ab+ac+ad+ae)%65521;K=K+16 end;while K<=Z do local af=h(Y,K,K)Q=(Q+af)%65521;R=(R+Q)%65521;K=K+1 end;return(R*65536+Q)%4294967296 end;local function ag(ah,ai)return ah%4294967296==ai%4294967296 end;function a:CreateDictionary(Y,Z,aj)if type(Y)~="string"then error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):".." \'str\' - string expected got \'%s\'."):format(type(Y)),2)end;if type(Z)~="number"then error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):".." \'strlen\' - number expected got \'%s\'."):format(type(Z)),2)end;if type(aj)~="number"then error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):".." \'adler32\' - number expected got \'%s\'."):format(type(aj)),2)end;if Z~=#Y then error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):".." \'strlen\' does not match the actual length of \'str\'.".." \'strlen\': %u, \'#str\': %u .".." Please check if \'str\' is modified unintentionally."):format(Z,#Y))end;if Z==0 then error("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):".." \'str\' - Empty string is not allowed.",2)end;if Z>32768 then error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):".." \'str\' - string longer than 32768 bytes is not allowed.".." Got %d bytes."):format(Z),2)end;local ak=self:Adler32(Y)if not ag(aj,ak)then error(("Usage: LibDeflate:CreateDictionary(str, strlen, adler32):".." \'adler32\' does not match the actual adler32 of \'str\'.".." \'adler32\': %u, \'Adler32(str)\': %u .".." Please check if \'str\' is modified unintentionally."):format(aj,ak))end;local al={}al.adler32=aj;al.hash_tables={}al.string_table={}al.strlen=Z;local am=al.string_table;local an=al.hash_tables;am[1]=h(Y,1,1)am[2]=h(Y,2,2)if Z>=3 then local K=1;local ao=am[1]*256+am[2]while K<=Z-2-3 do local _,a0,a1,a2=h(Y,K+2,K+5)am[K+2]=_;am[K+3]=a0;am[K+4]=a1;am[K+5]=a2;ao=(ao*256+_)%16777216;local V=an[ao]if not V then V={}an[ao]=V end;V[#V+1]=K-Z;K=K+1;ao=(ao*256+a0)%16777216;V=an[ao]if not V then V={}an[ao]=V end;V[#V+1]=K-Z;K=K+1;ao=(ao*256+a1)%16777216;V=an[ao]if not V then V={}an[ao]=V end;V[#V+1]=K-Z;K=K+1;ao=(ao*256+a2)%16777216;V=an[ao]if not V then V={}an[ao]=V end;V[#V+1]=K-Z;K=K+1 end;while K<=Z-2 do local af=h(Y,K+2)am[K+2]=af;ao=(ao*256+af)%16777216;local V=an[ao]if not V then V={}an[ao]=V end;V[#V+1]=K-Z;K=K+1 end end;return al end;local function ap(al)if type(al)~="table"then return false,("\'dictionary\' - table expected got \'%s\'."):format(type(al))end;if type(al.adler32)~="number"or type(al.string_table)~="table"or type(al.strlen)~="number"or al.strlen<=0 or al.strlen>32768 or al.strlen~=#al.string_table or type(al.hash_tables)~="table"then return false,("\'dictionary\' - corrupted dictionary."):format(type(al))end;return true,""end;local aq={[0]={false,nil,0,0,0},[1]={false,nil,4,8,4},[2]={false,nil,5,18,8},[3]={false,nil,6,32,32},[4]={true,4,4,16,16},[5]={true,8,16,32,32},[6]={true,8,16,128,128},[7]={true,8,32,128,256},[8]={true,32,128,258,1024},[9]={true,32,258,258,4096}}local function ar(Y,as,al,at,au)if type(Y)~="string"then return false,("\'str\' - string expected got \'%s\'."):format(type(Y))end;if as then local av,aw=ap(al)if not av then return false,aw end end;if at then local ax=type(au)if ax~="nil"and ax~="table"then return false,("\'configs\' - nil or table expected got \'%s\'."):format(type(au))end;if ax=="table"then for ay,az in pairs(au)do if ay~="level"and ay~="strategy"then return false,("\'configs\' - unsupported table key in the configs: \'%s\'."):format(ay)elseif ay=="level"and not aq[az]then return false,("\'configs\' - unsupported \'level\': %s."):format(tostring(az))elseif ay=="strategy"and az~="fixed"and az~="huffman_only"and az~="dynamic"then return false,("\'configs\' - unsupported \'strategy\': \'%s\'."):format(tostring(az))end end end end;return true,""end;local aA=0;local aB=1;local aC=2;local aD=3;local function aE()local aF=0;local aG=0;local aH=0;local aI=0;local aJ={}local aK={}local function aL(O,T)aG=aG+O*o[aH]aH=aH+T;aI=aI+T;if aH>=32 then aF=aF+1;aJ[aF]=p[aG%256]..p[(aG-aG%256)/256%256]..p[(aG-aG%65536)/65536%256]..p[(aG-aG%16777216)/16777216%256]local aM=o[32-aH+T]aG=(O-O%aM)/aM;aH=aH-32 end end;local function aN(Y)for P=1,aH,8 do aF=aF+1;aJ[aF]=i(aG%256)aG=(aG-aG%256)/256 end;aH=0;aF=aF+1;aJ[aF]=Y;aI=aI+#Y*8 end;local function aO(aP)if aP==aD then return aI end;if aP==aB or aP==aC then local aQ=(8-aH%8)%8;if aH>0 then aG=aG-o[aH]+o[aH+aQ]for P=1,aH,8 do aF=aF+1;aJ[aF]=p[aG%256]aG=(aG-aG%256)/256 end;aG=0;aH=0 end;if aP==aC then aI=aI+aQ;return aI end end;local aR=m(aJ)aJ={}aF=0;aK[#aK+1]=aR;if aP==aA then return aI else return aI,m(aK)end end;return aL,aN,aO end;local function aS(aT,aU,aV)aV=aV+1;aT[aV]=aU;local O=aU[1]local aW=aV;local aX=(aW-aW%2)/2;while aX>=1 and aT[aX][1]>O do local V=aT[aX]aT[aX]=aU;aT[aW]=V;aW=aX;aX=(aX-aX%2)/2 end end;local function aY(aT,aV)local aZ=aT[1]local aU=aT[aV]local O=aU[1]aT[1]=aU;aT[aV]=aZ;aV=aV-1;local aW=1;local a_=aW*2;local b0=a_+1;while a_<=aV do local b1=aT[a_]if b0<=aV and aT[b0][1]<b1[1]then local b2=aT[b0]if b2[1]<O then aT[b0]=aU;aT[aW]=b2;aW=b0;a_=aW*2;b0=a_+1 else break end else if b1[1]<O then aT[a_]=aU;aT[aW]=b1;aW=a_;a_=aW*2;b0=a_+1 else break end end end;return aZ end;local function b3(b4,b5,b6,b7)local b8=0;local b9={}local ba={}for T=1,b7 do b8=(b8+(b4[T-1]or 0))*2;b9[T]=b8 end;for bb=0,b6 do local T=b5[bb]if T then b8=b9[T]b9[T]=b8+1;if T<=9 then ba[bb]=q[T][b8]else local N=0;for P=1,T do N=N-N%2+((N%2==1 or b8%2==1)and 1 or 0)b8=(b8-b8%2)/2;N=N*2 end;ba[bb]=(N-N%2)/2 end end end;return ba end;local function bc(Q,R)return Q[1]<R[1]or Q[1]==R[1]and Q[2]<R[2]end;local function bd(be,b7,b6)local aV;local bf=-1;local bg={}local aT={}local b5={}local bh={}local b4={}local bi=0;for bb,bj in pairs(be)do bi=bi+1;bg[bi]={bj,bb}end;if bi==0 then return{},{},-1 elseif bi==1 then local bb=bg[1][2]b5[bb]=1;bh[bb]=0;return b5,bh,bb else n(bg,bc)aV=bi;for K=1,aV do aT[K]=bg[K]end;while aV>1 do local bk=aY(aT,aV)aV=aV-1;local bl=aY(aT,aV)aV=aV-1;local bm={bk[1]+bl[1],-1,bk,bl}aS(aT,bm,aV)aV=aV+1 end;local bn=0;local bo={aT[1],0,0,0}local bp=1;local bq=1;aT[1][1]=0;while bq<=bp do local aU=bo[bq]local T=aU[1]local bb=aU[2]local b1=aU[3]local b2=aU[4]if b1 then bp=bp+1;bo[bp]=b1;b1[1]=T+1 end;if b2 then bp=bp+1;bo[bp]=b2;b2[1]=T+1 end;bq=bq+1;if T>b7 then bn=bn+1;T=b7 end;if bb>=0 then b5[bb]=T;bf=bb>bf and bb or bf;b4[T]=(b4[T]or 0)+1 end end;if bn>0 then repeat local T=b7-1;while(b4[T]or 0)==0 do T=T-1 end;b4[T]=b4[T]-1;b4[T+1]=(b4[T+1]or 0)+2;b4[b7]=b4[b7]-1;bn=bn-2 until bn<=0;bq=1;for T=b7,1,-1 do local br=b4[T]or 0;while br>0 do local bb=bg[bq][2]b5[bb]=T;br=br-1;bq=bq+1 end end end;bh=b3(b4,b5,b6,b7)return b5,bh,bf end end;local function bs(bt,bu,bv,bw)local bx=0;local by={}local bz={}local bA=0;local bB={}local bC=nil;local bj=0;bw=bw<0 and 0 or bw;local bD=bu+bw+1;for W=0,bD+1 do local U=W<=bu and(bt[W]or 0)or(W<=bD and(bv[W-bu-1]or 0)or nil)if U==bC then bj=bj+1;if U~=0 and bj==6 then bx=bx+1;by[bx]=16;bA=bA+1;bB[bA]=3;bz[16]=(bz[16]or 0)+1;bj=0 elseif U==0 and bj==138 then bx=bx+1;by[bx]=18;bA=bA+1;bB[bA]=127;bz[18]=(bz[18]or 0)+1;bj=0 end else if bj==1 then bx=bx+1;by[bx]=bC;bz[bC]=(bz[bC]or 0)+1 elseif bj==2 then bx=bx+1;by[bx]=bC;bx=bx+1;by[bx]=bC;bz[bC]=(bz[bC]or 0)+2 elseif bj>=3 then bx=bx+1;local bE=bC~=0 and 16 or(bj<=10 and 17 or 18)by[bx]=bE;bz[bE]=(bz[bE]or 0)+1;bA=bA+1;bB[bA]=bj<=10 and bj-3 or bj-11 end;bC=U;if U and U~=0 then bx=bx+1;by[bx]=U;bz[U]=(bz[U]or 0)+1;bj=0 else bj=1 end end end;return by,bB,bz end;local function bF(Y,V,bG,bH,bI)local K=bG-bI;while K<=bH-15-bI do V[K],V[K+1],V[K+2],V[K+3],V[K+4],V[K+5],V[K+6],V[K+7],V[K+8],V[K+9],V[K+10],V[K+11],V[K+12],V[K+13],V[K+14],V[K+15]=h(Y,K+bI,K+15+bI)K=K+16 end;while K<=bH-bI do V[K]=h(Y,K+bI,K+bI)K=K+1 end;return V end;local function bJ(bK,am,an,bL,bM,bI,al)local bN=aq[bK]local bO,bP,bQ,bR,bS=bN[1],bN[2],bN[3],bN[4],bN[5]local bT=not bO and bQ or 2147483646;local bU=bS-bS%4/4;local ao;local bV;local bW;local bX=0;if al then bV=al.hash_tables;bW=al.string_table;bX=al.strlen;assert(bL==1)if bM>=bL and bX>=2 then ao=bW[bX-1]*65536+bW[bX]*256+am[1]local V=an[ao]if not V then V={}an[ao]=V end;V[#V+1]=-1 end;if bM>=bL+1 and bX>=1 then ao=bW[bX]*65536+am[1]*256+am[2]local V=an[ao]if not V then V={}an[ao]=V end;V[#V+1]=0 end end;local bY=bX+3;ao=(am[bL-bI]or 0)*256+(am[bL+1-bI]or 0)local bZ={}local b_=0;local c0={}local c1={}local c2=0;local c3={}local c4={}local c5=0;local c6={}local c7=0;local c8=false;local c9;local ca;local cb=0;local cc=0;local bq=bL;local cd=bM+(bO and 1 or 0)while bq<=cd do local ce=bq-bI;local cf=bI-3;c9=cb;ca=cc;cb=0;ao=(ao*256+(am[ce+2]or 0))%16777216;local cg;local ch;local ci=an[ao]local cj;if not ci then cj=0;ci={}an[ao]=ci;if bV then ch=bV[ao]cg=ch and#ch or 0 else cg=0 end else cj=#ci;ch=ci;cg=cj end;if bq<=bM then ci[cj+1]=bq end;if cg>0 and bq+2<=bM and(not bO or c9<bQ)then local ck=bO and c9>=bP and bU or bS;local cl=bM-bq;cl=cl>=257 and 257 or cl;cl=cl+ce;local cm=ce+3;while cg>=1 and ck>0 do local bC=ch[cg]if bq-bC>32768 then break end;if bC<bq then local cn=cm;if bC>=-257 then local co=bC-cf;while cn<=cl and am[co]==am[cn]do cn=cn+1;co=co+1 end else local co=bY+bC;while cn<=cl and bW[co]==am[cn]do cn=cn+1;co=co+1 end end;local M=cn-ce;if M>cb then cb=M;cc=bq-bC end;if cb>=bR then break end end;cg=cg-1;ck=ck-1;if cg==0 and bC>0 and bV then ch=bV[ao]cg=ch and#ch or 0 end end end;if not bO then c9,ca=cb,cc end;if(not bO or c8)and(c9>3 or c9==3 and ca<4096)and cb<=c9 then local W=r[c9]local cp=t[c9]local cq,cr,cs;if ca<=256 then cq=u[ca]cs=v[ca]cr=w[ca]else cq=16;cr=7;local Q=384;local R=512;while true do if ca<=Q then cs=(ca-R/2-1)%(R/4)break elseif ca<=R then cs=(ca-R/2-1)%(R/4)cq=cq+1;break else cq=cq+2;cr=cr+1;Q=Q*2;R=R*2 end end end;b_=b_+1;bZ[b_]=W;c0[W]=(c0[W]or 0)+1;c2=c2+1;c1[c2]=cq;c3[cq]=(c3[cq]or 0)+1;if cp>0 then local ct=s[c9]c5=c5+1;c4[c5]=ct end;if cr>0 then c7=c7+1;c6[c7]=cs end;for K=bq+1,bq+c9-(bO and 2 or 1)do ao=(ao*256+(am[K-bI+2]or 0))%16777216;if c9<=bT then ci=an[ao]if not ci then ci={}an[ao]=ci end;ci[#ci+1]=K end end;bq=bq+c9-(bO and 1 or 0)c8=false elseif not bO or c8 then local W=am[bO and ce-1 or ce]b_=b_+1;bZ[b_]=W;c0[W]=(c0[W]or 0)+1;bq=bq+1 else c8=true;bq=bq+1 end end;b_=b_+1;bZ[b_]=256;c0[256]=(c0[256]or 0)+1;return bZ,c4,c0,c1,c6,c3 end;local function cu(c0,c3)local cv,cw,bu=bd(c0,15,285)local cx,cy,bw=bd(c3,15,29)local cz,bB,cA=bs(cv,bu,cx,bw)local cB,cC=bd(cA,7,18)local cD=0;for K=1,19 do local bb=B[K]local cE=cB[bb]or 0;if cE~=0 then cD=K end end;cD=cD-4;local cF=bu+1-257;local cG=bw+1-1;if cG<0 then cG=0 end;return cF,cG,cD,cB,cC,cz,bB,cv,cw,cx,cy end;local function cH(bZ,c1,cD,cB,cz,cv,cx)local cI=17;cI=cI+(cD+4)*3;for K=1,#cz do local W=cz[K]cI=cI+cB[W]if W>=16 then cI=cI+(W==16 and 2 or(W==17 and 3 or 7))end end;local cJ=0;for K=1,#bZ do local W=bZ[K]local cK=cv[W]cI=cI+cK;if W>256 then cJ=cJ+1;if W>264 and W<285 then local cL=y[W-256]cI=cI+cL end;local cq=c1[cJ]local cM=cx[cq]cI=cI+cM;if cq>3 then local cr=(cq-cq%2)/2-1;cI=cI+cr end end end;return cI end;local function cN(aL,cO,bZ,c4,c1,c6,cF,cG,cD,cB,cC,cz,bB,cv,cw,cx,cy)aL(cO and 1 or 0,1)aL(2,2)aL(cF,5)aL(cG,5)aL(cD,4)for K=1,cD+4 do local bb=B[K]local cE=cB[bb]or 0;aL(cE,3)end;local cP=1;for K=1,#cz do local W=cz[K]aL(cC[W],cB[W])if W>=16 then local cQ=bB[cP]aL(cQ,W==16 and 2 or(W==17 and 3 or 7))cP=cP+1 end end;local cJ=0;local cR=0;local cS=0;for K=1,#bZ do local cT=bZ[K]local b8=cw[cT]local cK=cv[cT]aL(b8,cK)if cT>256 then cJ=cJ+1;if cT>264 and cT<285 then cR=cR+1;local cU=c4[cR]local cL=y[cT-256]aL(cU,cL)end;local cV=c1[cJ]local cW=cy[cV]local cM=cx[cV]aL(cW,cM)if cV>3 then cS=cS+1;local cs=c6[cS]local cr=(cV-cV%2)/2-1;aL(cs,cr)end end end end;local function cX(bZ,c1)local cI=3;local cJ=0;for K=1,#bZ do local W=bZ[K]local cK=E[W]cI=cI+cK;if W>256 then cJ=cJ+1;if W>264 and W<285 then local cL=y[W-256]cI=cI+cL end;local cq=c1[cJ]cI=cI+5;if cq>3 then local cr=(cq-cq%2)/2-1;cI=cI+cr end end end;return cI end;local function cY(aL,cO,bZ,c4,c1,c6)aL(cO and 1 or 0,1)aL(1,2)local cJ=0;local cR=0;local cS=0;for K=1,#bZ do local cZ=bZ[K]local b8=C[cZ]local cK=E[cZ]aL(b8,cK)if cZ>256 then cJ=cJ+1;if cZ>264 and cZ<285 then cR=cR+1;local cU=c4[cR]local cL=y[cZ-256]aL(cU,cL)end;local cq=c1[cJ]local cW=G[cq]aL(cW,5)if cq>3 then cS=cS+1;local cs=c6[cS]local cr=(cq-cq%2)/2-1;aL(cs,cr)end end end end;local function c_(bL,bM,aI)assert(bM-bL+1<=65535)local cI=3;aI=aI+3;local aQ=(8-aI%8)%8;cI=cI+aQ;cI=cI+32;cI=cI+(bM-bL+1)*8;return cI end;local function d0(aL,aN,cO,Y,bL,bM,aI)assert(bM-bL+1<=65535)aL(cO and 1 or 0,1)aL(0,2)aI=aI+3;local aQ=(8-aI%8)%8;if aQ>0 then aL(o[aQ]-1,aQ)end;local d1=bM-bL+1;aL(d1,16)local d2=255-d1%256+(255-(d1-d1%256)/256)*256;aL(d2,16)aN(Y:sub(bL,bM))end;local function d3(au,aL,aN,aO,Y,al)local am={}local an={}local cO=nil;local bL;local bM;local d4;local aI=aO(aD)local Z=#Y;local bI;local bK;local d5;if au then if au.level then bK=au.level end;if au.strategy then d5=au.strategy end end;if not bK then if Z<2048 then bK=7 elseif Z>65536 then bK=3 else bK=5 end end;while not cO do if not bL then bL=1;bM=64*1024-1;bI=0 else bL=bM+1;bM=bM+32*1024;bI=bL-32*1024-1 end;if bM>=Z then bM=Z;cO=true else cO=false end;local bZ,c4,c0,c1,c6,c3;local cF,cG,cD,cB,cC,cz,bB,cv,cw,cx,cy;local d6;local d7;local d8;if bK~=0 then bF(Y,am,bL,bM+3,bI)if bL==1 and al then local bW=al.string_table;local d9=al.strlen;for K=0,-d9+1<-257 and-257 or-d9+1,-1 do am[K]=bW[d9+K]end end;if d5=="huffman_only"then bZ={}bF(Y,bZ,bL,bM,bL-1)c4={}c0={}bZ[bM-bL+2]=256;for K=1,bM-bL+2 do local W=bZ[K]c0[W]=(c0[W]or 0)+1 end;c1={}c6={}c3={}else bZ,c4,c0,c1,c6,c3=bJ(bK,am,an,bL,bM,bI,al)end;cF,cG,cD,cB,cC,cz,bB,cv,cw,cx,cy=cu(c0,c3)d6=cH(bZ,c1,cD,cB,cz,cv,cx)d7=cX(bZ,c1)end;d8=c_(bL,bM,aI)local da=d8;da=d7 and d7<da and d7 or da;da=d6 and d6<da and d6 or da;if bK==0 or d5~="fixed"and d5~="dynamic"and d8==da then d0(aL,aN,cO,Y,bL,bM,aI)aI=aI+d8 elseif d5~="dynamic"and(d5=="fixed"or d7==da)then cY(aL,cO,bZ,c4,c1,c6)aI=aI+d7 elseif d5=="dynamic"or d6==da then cN(aL,cO,bZ,c4,c1,c6,cF,cG,cD,cB,cC,cz,bB,cv,cw,cx,cy)aI=aI+d6 end;if cO then d4=aO(aD)else d4=aO(aA)end;assert(d4==aI)if not cO then local M;if al and bL==1 then M=0;while am[M]do am[M]=nil;M=M-1 end end;al=nil;M=1;for K=bM-32767,bM do am[M]=am[K-bI]M=M+1 end;for ay,V in pairs(an)do local db=#V;if db>0 and bM+1-V[1]>32768 then if db==1 then an[ay]=nil else local dc={}local dd=0;for K=2,db do M=V[K]if bM+1-M<=32768 then dd=dd+1;dc[dd]=M end end;an[ay]=dc end end end end end end;local function de(Y,al,au)local aL,aN,aO=aE()d3(au,aL,aN,aO,Y,al)local aI,df=aO(aB)local aQ=(8-aI%8)%8;return df,aQ end;local function dg(Y,al,au)local aL,aN,aO=aE()local dh=8;local di=7;local dj=di*16+dh;aL(dj,8)local dk=al and 1 or 0;local dl=2;local dm=dl*64+dk*32;local dn=31-(dj*256+dm)%31;dm=dm+dn;aL(dm,8)if dk==1 then local aj=al.adler32;local dp=aj%256;aj=(aj-dp)/256;local dq=aj%256;aj=(aj-dq)/256;local dr=aj%256;aj=(aj-dr)/256;local ds=aj%256;aL(ds,8)aL(dr,8)aL(dq,8)aL(dp,8)end;d3(au,aL,aN,aO,Y,al)aO(aC)local aj=a:Adler32(Y)local ds=aj%256;aj=(aj-ds)/256;local dr=aj%256;aj=(aj-dr)/256;local dq=aj%256;aj=(aj-dq)/256;local dp=aj%256;aL(dp,8)aL(dq,8)aL(dr,8)aL(ds,8)local aI,df=aO(aB)local aQ=(8-aI%8)%8;return df,aQ end;function a:CompressDeflate(Y,au)local dt,du=ar(Y,false,nil,true,au)if not dt then error("Usage: LibDeflate:CompressDeflate(str, configs): "..du,2)end;return de(Y,nil,au)end;function a:CompressDeflateWithDict(Y,al,au)local dt,du=ar(Y,true,al,true,au)if not dt then error("Usage: LibDeflate:CompressDeflateWithDict".."(str, dictionary, configs): "..du,2)end;return de(Y,al,au)end;function a:CompressZlib(Y,au)local dt,du=ar(Y,false,nil,true,au)if not dt then error("Usage: LibDeflate:CompressZlib(str, configs): "..du,2)end;return dg(Y,nil,au)end;function a:CompressZlibWithDict(Y,al,au)local dt,du=ar(Y,true,al,true,au)if not dt then error("Usage: LibDeflate:CompressZlibWithDict".."(str, dictionary, configs): "..du,2)end;return dg(Y,al,au)end;local function dv(dw)local dx=dw;local dy=#dw;local dz=1;local aH=0;local aG=0;local function dA(T)local aM=o[T]local W;if T<=aH then W=aG%aM;aG=(aG-W)/aM;aH=aH-T else local dB=o[aH]local dq,dr,ds,dC=h(dx,dz,dz+3)aG=aG+((dq or 0)+(dr or 0)*256+(ds or 0)*65536+(dC or 0)*16777216)*dB;dz=dz+4;aH=aH+32-T;W=aG%aM;aG=(aG-W)/aM end;return W end;local function dD(dE,aJ,aF)assert(aH%8==0)local dF=aH/8<dE and aH/8 or dE;for P=1,dF do local dG=aG%256;aF=aF+1;aJ[aF]=i(dG)aG=(aG-dG)/256 end;aH=aH-dF*8;dE=dE-dF;if(dy-dz-dE+1)*8+aH<0 then return-1 end;for K=dz,dz+dE-1 do aF=aF+1;aJ[aF]=l(dx,K,K)end;dz=dz+dE;return aF end;local function dH(dI,dJ,da)local W=0;local dK=0;local bq=0;local bj;if da>0 then if aH<15 and dx then local dB=o[aH]local dq,dr,ds,dC=h(dx,dz,dz+3)aG=aG+((dq or 0)+(dr or 0)*256+(ds or 0)*65536+(dC or 0)*16777216)*dB;dz=dz+4;aH=aH+32 end;local aM=o[da]aH=aH-da;W=aG%aM;aG=(aG-W)/aM;W=q[da][W]bj=dI[da]if W<bj then return dJ[W]end;bq=bj;dK=bj*2;W=W*2 end;for T=da+1,15 do local dL;dL=aG%2;aG=(aG-dL)/2;aH=aH-1;W=dL==1 and W+1-W%2 or W;bj=dI[T]or 0;local dM=W-dK;if dM<bj then return dJ[bq+dM]end;bq=bq+bj;dK=dK+bj;dK=dK*2;W=W*2 end;return-10 end;local function dN()return(dy-dz+1)*8+aH end;local function dO()local dP=aH%8;local aM=o[dP]aH=aH-dP;aG=(aG-aG%aM)/aM end;return dA,dD,dH,dN,dO end;local function dQ(Y,al)local dA,dD,dH,dN,dO=dv(Y)local dR={ReadBits=dA,ReadBytes=dD,Decode=dH,ReaderBitlenLeft=dN,SkipToByteBoundary=dO,buffer_size=0,buffer={},result_buffer={},dictionary=al}return dR end;local function dS(dT,b6,b7)local dI={}local da=b7;for bb=0,b6 do local T=dT[bb]or 0;da=T>0 and T<da and T or da;dI[T]=(dI[T]or 0)+1 end;if dI[0]==b6+1 then return 0,dI,{},0 end;local dU=1;for U=1,b7 do dU=dU*2;dU=dU-(dI[U]or 0)if dU<0 then return dU end end;local dV={}dV[1]=0;for U=1,b7-1 do dV[U+1]=dV[U]+(dI[U]or 0)end;local dJ={}for bb=0,b6 do local T=dT[bb]or 0;if T~=0 then local bI=dV[T]dJ[bI]=bb;dV[T]=dV[T]+1 end end;return dU,dI,dJ,da end;local function dW(dR,cv,dX,dY,cx,dZ,d_)local aJ,aF,dA,dH,dN,aK=dR.buffer,dR.buffer_size,dR.ReadBits,dR.Decode,dR.ReaderBitlenLeft,dR.result_buffer;local al=dR.dictionary;local bW;local d9;local e0=1;if al and not aJ[0]then bW=al.string_table;d9=al.strlen;e0=-d9+1;for K=0,-d9+1<-257 and-257 or-d9+1,-1 do aJ[K]=p[bW[d9+K]]end end;repeat local bb=dH(cv,dX,dY)if bb<0 or bb>285 then return-10 elseif bb<256 then aF=aF+1;aJ[aF]=p[bb]elseif bb>256 then bb=bb-256;local T=x[bb]T=bb>=8 and T+dA(y[bb])or T;bb=dH(cx,dZ,d_)if bb<0 or bb>29 then return-10 end;local X=z[bb]X=X>4 and X+dA(A[bb])or X;local e1=aF-X+1;if e1<e0 then return-11 end;if e1>=-257 then for P=1,T do aF=aF+1;aJ[aF]=aJ[e1]e1=e1+1 end else e1=d9+e1;for P=1,T do aF=aF+1;aJ[aF]=p[bW[e1]]e1=e1+1 end end end;if dN()<0 then return 2 end;if aF>=65536 then aK[#aK+1]=m(aJ,"",1,32768)for K=32769,aF do aJ[K-32768]=aJ[K]end;aF=aF-32768;aJ[aF+1]=nil end until bb==256;dR.buffer_size=aF;return 0 end;local function e2(dR)local aJ,aF,dA,dD,dN,dO,aK=dR.buffer,dR.buffer_size,dR.ReadBits,dR.ReadBytes,dR.ReaderBitlenLeft,dR.SkipToByteBoundary,dR.result_buffer;dO()local dE=dA(16)if dN()<0 then return 2 end;local e3=dA(16)if dN()<0 then return 2 end;if dE%256+e3%256~=255 then return-2 end;if(dE-dE%256)/256+(e3-e3%256)/256~=255 then return-2 end;aF=dD(dE,aJ,aF)if aF<0 then return 2 end;if aF>=65536 then aK[#aK+1]=m(aJ,"",1,32768)for K=32769,aF do aJ[K-32768]=aJ[K]end;aF=aF-32768;aJ[aF+1]=nil end;dR.buffer_size=aF;return 0 end;local function e4(dR)return dW(dR,F,D,7,J,H,5)end;local function e5(dR)local dA,dH=dR.ReadBits,dR.Decode;local e6=dA(5)+257;local e7=dA(5)+1;local e8=dA(4)+4;if e6>286 or e7>30 then return-3 end;local cB={}for K=1,e8 do cB[B[K]]=dA(3)end;local e9,ea,eb,ec=dS(cB,18,7)if e9~=0 then return-4 end;local cv={}local cx={}local bq=0;while bq<e6+e7 do local bb;local T;bb=dH(ea,eb,ec)if bb<0 then return bb elseif bb<16 then if bq<e6 then cv[bq]=bb else cx[bq-e6]=bb end;bq=bq+1 else T=0;if bb==16 then if bq==0 then return-5 end;if bq-1<e6 then T=cv[bq-1]else T=cx[bq-e6-1]end;bb=3+dA(2)elseif bb==17 then bb=3+dA(3)else bb=11+dA(7)end;if bq+bb>e6+e7 then return-6 end;while bb>0 do bb=bb-1;if bq<e6 then cv[bq]=T else cx[bq-e6]=T end;bq=bq+1 end end end;if(cv[256]or 0)==0 then return-9 end;local ed,ee,dX,dY=dS(cv,e6-1,15)if ed~=0 and(ed<0 or e6~=(ee[0]or 0)+(ee[1]or 0))then return-7 end;local ef,eg,dZ,d_=dS(cx,e7-1,15)if ef~=0 and(ef<0 or e7~=(eg[0]or 0)+(eg[1]or 0))then return-8 end;return dW(dR,ee,dX,dY,eg,dZ,d_)end;local function eh(dR)local dA=dR.ReadBits;local cO;while not cO do cO=dA(1)==1;local ei=dA(2)local ej;if ei==0 then ej=e2(dR)elseif ei==1 then ej=e4(dR)elseif ei==2 then ej=e5(dR)else return nil,-1 end;if ej~=0 then return nil,ej end end;dR.result_buffer[#dR.result_buffer+1]=m(dR.buffer,"",1,dR.buffer_size)local df=m(dR.result_buffer)return df end;local function ek(Y,al)local dR=dQ(Y,al)local df,ej=eh(dR)if not df then return nil,ej end;local el=dR.ReaderBitlenLeft()local em=(el-el%8)/8;return df,em end;local function en(Y,al)local dR=dQ(Y,al)local dA=dR.ReadBits;local dj=dA(8)if dR.ReaderBitlenLeft()<0 then return nil,2 end;local dh=dj%16;local di=(dj-dh)/16;if dh~=8 then return nil,-12 end;if di>7 then return nil,-13 end;local dm=dA(8)if dR.ReaderBitlenLeft()<0 then return nil,2 end;if(dj*256+dm)%31~=0 then return nil,-14 end;local dk=(dm-dm%32)/32%2;local dl=(dm-dm%64)/64%4;if dk==1 then if not al then return nil,-16 end;local ds=dA(8)local dr=dA(8)local dq=dA(8)local dp=dA(8)local ak=ds*16777216+dr*65536+dq*256+dp;if dR.ReaderBitlenLeft()<0 then return nil,2 end;if not ag(ak,al.adler32)then return nil,-17 end end;local df,ej=eh(dR)if not df then return nil,ej end;dR.SkipToByteBoundary()local eo=dA(8)local ep=dA(8)local eq=dA(8)local er=dA(8)if dR.ReaderBitlenLeft()<0 then return nil,2 end;local es=eo*16777216+ep*65536+eq*256+er;local et=a:Adler32(df)if not ag(es,et)then return nil,-15 end;local el=dR.ReaderBitlenLeft()local em=(el-el%8)/8;return df,em end;function a:DecompressDeflate(Y)local dt,du=ar(Y)if not dt then error("Usage: LibDeflate:DecompressDeflate(str): "..du,2)end;return ek(Y)end;function a:DecompressDeflateWithDict(Y,al)local dt,du=ar(Y,true,al)if not dt then error("Usage: LibDeflate:DecompressDeflateWithDict(str, dictionary): "..du,2)end;return ek(Y,al)end;function a:DecompressZlib(Y)local dt,du=ar(Y)if not dt then error("Usage: LibDeflate:DecompressZlib(str): "..du,2)end;return en(Y)end;function a:DecompressZlibWithDict(Y,al)local dt,du=ar(Y,true,al)if not dt then error("Usage: LibDeflate:DecompressZlibWithDict(str, dictionary): "..du,2)end;return en(Y,al)end;do E={}for eu=0,143 do E[eu]=8 end;for eu=144,255 do E[eu]=9 end;for eu=256,279 do E[eu]=7 end;for eu=280,287 do E[eu]=8 end;I={}for X=0,31 do I[X]=5 end;local ej;ej,F,D=dS(E,287,9)assert(ej==0)ej,J,H=dS(I,31,5)assert(ej==0)C=b3(F,E,287,9)G=b3(J,I,31,5)end;local ev={["\000"]="%z",["("]="%(",[")"]="%)",["."]="%.",["%"]="%%",["+"]="%+",["-"]="%-",["*"]="%*",["?"]="%?",["["]="%[",["]"]="%]",["^"]="%^",["$"]="%$"}local function ew(Y)return Y:gsub("([%z%(%)%.%%%+%-%*%?%[%]%^%$])",ev)end;function a:CreateCodec(ex,ey,ez)if type(ex)~="string"or type(ey)~="string"or type(ez)~="string"then error("Usage: LibDeflate:CreateCodec(reserved_chars,".." escape_chars, map_chars):".." All arguments must be string.",2)end;if ey==""then return nil,"No escape characters supplied."end;if#ex<#ez then return nil,"The number of reserved characters must be".." at least as many as the number of mapped chars."end;if ex==""then return nil,"No characters to encode."end;local eA=ex..ey..ez;local eB={}for K=1,#eA do local dG=h(eA,K,K)if eB[dG]then return nil,"There must be no duplicate characters in the".." concatenation of reserved_chars, escape_chars and".." map_chars."end;eB[dG]=true end;local eC={}local eD={}local eE={}local eF={}if#ez>0 then local eG={}local eH={}for K=1,#ez do local eI=l(ex,K,K)local eJ=l(ez,K,K)eF[eI]=eJ;eE[#eE+1]=eI;eH[eJ]=eI;eG[#eG+1]=eJ end;eC[#eC+1]="(["..ew(m(eG)).."])"eD[#eD+1]=eH end;local eK=1;local eL=l(ey,eK,eK)local eM=0;local eG={}local eH={}for K=1,#eA do local S=l(eA,K,K)if not eF[S]then while eM>=256 or eB[eM]do eM=eM+1;if eM>255 then eC[#eC+1]=ew(eL).."(["..ew(m(eG)).."])"eD[#eD+1]=eH;eK=eK+1;eL=l(ey,eK,eK)eM=0;eG={}eH={}if not eL or eL==""then return nil,"Out of escape characters."end end end;local eN=p[eM]eF[S]=eL..eN;eE[#eE+1]=S;eH[eN]=S;eG[#eG+1]=eN;eM=eM+1 end;if K==#eA then eC[#eC+1]=ew(eL).."(["..ew(m(eG)).."])"eD[#eD+1]=eH end end;local eO={}local eP="(["..ew(m(eE)).."])"local eQ=eF;function eO:Encode(Y)if type(Y)~="string"then error(("Usage: codec:Encode(str):".." \'str\' - string expected got \'%s\'."):format(type(Y)),2)end;return k(Y,eP,eQ)end;local eR=#eC;local eS="(["..ew(ex).."])"function eO:Decode(Y)if type(Y)~="string"then error(("Usage: codec:Decode(str):".." \'str\' - string expected got \'%s\'."):format(type(Y)),2)end;if j(Y,eS)then return nil end;for K=1,eR do Y=k(Y,eC[K],eD[K])end;return Y end;return eO end;local eT={[0]="a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","0","1","2","3","4","5","6","7","8","9","(",")"}local eU={[97]=0,[98]=1,[99]=2,[100]=3,[101]=4,[102]=5,[103]=6,[104]=7,[105]=8,[106]=9,[107]=10,[108]=11,[109]=12,[110]=13,[111]=14,[112]=15,[113]=16,[114]=17,[115]=18,[116]=19,[117]=20,[118]=21,[119]=22,[120]=23,[121]=24,[122]=25,[65]=26,[66]=27,[67]=28,[68]=29,[69]=30,[70]=31,[71]=32,[72]=33,[73]=34,[74]=35,[75]=36,[76]=37,[77]=38,[78]=39,[79]=40,[80]=41,[81]=42,[82]=43,[83]=44,[84]=45,[85]=46,[86]=47,[87]=48,[88]=49,[89]=50,[90]=51,[48]=52,[49]=53,[50]=54,[51]=55,[52]=56,[53]=57,[54]=58,[55]=59,[56]=60,[57]=61,[40]=62,[41]=63}function a:EncodeForPrint(Y)if type(Y)~="string"then error(("Usage: LibDeflate:EncodeForPrint(str):".." \'str\' - string expected got \'%s\'."):format(type(Y)),2)end;local Z=#Y;local eV=Z-2;local K=1;local aJ={}local aF=0;while K<=eV do local _,a0,a1=h(Y,K,K+2)K=K+3;local aG=_+a0*256+a1*65536;local eW=aG%64;aG=(aG-eW)/64;local eX=aG%64;aG=(aG-eX)/64;local eY=aG%64;local eZ=(aG-eY)/64;aF=aF+1;aJ[aF]=eT[eW]..eT[eX]..eT[eY]..eT[eZ]end;local aG=0;local aH=0;while K<=Z do local af=h(Y,K,K)aG=aG+af*o[aH]aH=aH+8;K=K+1 end;while aH>0 do local e_=aG%64;aF=aF+1;aJ[aF]=eT[e_]aG=(aG-e_)/64;aH=aH-6 end;return m(aJ)end;function a:DecodeForPrint(Y)if type(Y)~="string"then error(("Usage: LibDeflate:DecodeForPrint(str):".." \'str\' - string expected got \'%s\'."):format(type(Y)),2)end;Y=Y:gsub("^[%c ]+","")Y=Y:gsub("[%c ]+$","")local Z=#Y;if Z==1 then return nil end;local f0=Z-3;local K=1;local aJ={}local aF=0;while K<=f0 do local _,a0,a1,a2=h(Y,K,K+3)_=eU[_]a0=eU[a0]a1=eU[a1]a2=eU[a2]if not(_ and a0 and a1 and a2)then return nil end;K=K+4;local aG=_+a0*64+a1*4096+a2*262144;local eW=aG%256;aG=(aG-eW)/256;local eX=aG%256;local eY=(aG-eX)/256;aF=aF+1;aJ[aF]=p[eW]..p[eX]..p[eY]end;local aG=0;local aH=0;while K<=Z do local af=h(Y,K,K)af=eU[af]if not af then return nil end;aG=aG+af*o[aH]aH=aH+6;K=K+1 end;while aH>=8 do local dG=aG%256;aF=aF+1;aJ[aF]=p[dG]aG=(aG-dG)/256;aH=aH-8 end;return m(aJ)end;local function f1()_chat_channel_codec=nil;_addon_channel_codec=nil end;a.internals={LoadStringToTable=bF,IsValidDictionary=ap,IsEqualAdler32=ag,_byte_to_6bit_char=eT,_6bit_to_byte=eU,InternalClearCache=f1}if io and os and debug and _G.arg then local io=io;local os=os;local debug=debug;local f2=_G.arg;local f3=debug.getinfo(1)if f3.source==f2[0]or f3.short_src==f2[0]then local dx;local f4;local K=1;local ej;local f5=false;local f6=false;local bK;local d5;local al;while f2[K]do local Q=f2[K]if Q=="-h"then print(a._COPYRIGHT.."\nUsage: lua LibDeflate.lua [OPTION] [INPUT] [OUTPUT]\n".." -0 store only. no compression.\n".." -1 fastest compression.\n".." -9 slowest and best compression.\n".." -d do decompression instead of compression.\n".." --dict <filename> specify the file that contains".." the entire preset dictionary.\n".." -h give this help.\n".." --strategy <fixed/huffman_only/dynamic>".." specify a special compression strategy.\n".." -v print the version and copyright info.\n".." --zlib use zlib format instead of raw deflate.\n")os.exit(0)elseif Q=="-v"then print(a._COPYRIGHT)os.exit(0)elseif Q:find("^%-[0-9]$")then bK=tonumber(Q:sub(2,2))elseif Q=="-d"then f6=true elseif Q=="--dict"then K=K+1;local f7=f2[K]if not f7 then io.stderr:write("You must speicify the dict filename")os.exit(1)end;local f8,f9=io.open(f7,"rb")if not f8 then io.stderr:write(("LibDeflate: Cannot read the dictionary file \'%s\': %s"):format(f7,f9))os.exit(1)end;local fa=f8:read("*all")f8:close()al=a:CreateDictionary(fa,#fa,a:Adler32(fa))elseif Q=="--strategy"then K=K+1;d5=f2[K]elseif Q=="--zlib"then f5=true elseif Q:find("^%-")then io.stderr:write(("LibDeflate: Invalid argument: %s"):format(Q))os.exit(1)else if not dx then dx,ej=io.open(Q,"rb")if not dx then io.stderr:write(("LibDeflate: Cannot read the file \'%s\': %s"):format(Q,tostring(ej)))os.exit(1)end elseif not f4 then f4,ej=io.open(Q,"wb")if not f4 then io.stderr:write(("LibDeflate: Cannot write the file \'%s\': %s"):format(Q,tostring(ej)))os.exit(1)end end end;K=K+1 end;if not dx or not f4 then io.stderr:write("LibDeflate:".." You must specify both input and output files.")os.exit(1)end;local fb=dx:read("*all")local au={level=bK,strategy=d5}local fc;if not f6 then if not f5 then if not al then fc=a:CompressDeflate(fb,au)else fc=a:CompressDeflateWithDict(fb,al,au)end else if not al then fc=a:CompressZlib(fb,au)else fc=a:CompressZlibWithDict(fb,al,au)end end else if not f5 then if not al then fc=a:DecompressDeflate(fb)else fc=a:DecompressDeflateWithDict(fb,al)end else if not al then fc=a:DecompressZlib(fb)else fc=a:DecompressZlibWithDict(fb,al)end end end;if not fc then io.stderr:write("LibDeflate: Decompress fails.")os.exit(1)end;f4:write(fc)if dx and dx~=io.stdin then dx:close()end;if f4 and f4~=io.stdout then f4:close()end;io.stderr:write(("Successfully writes %d bytes"):format(fc:len()))os.exit(0)end end;return a end)()
|
|
local expect = dofile("rom/modules/main/cc/expect.lua")
|
|
local expect, field = expect.expect, expect.field
|
|
local filesystem = nil
|
|
settings.define("containers.compression_level", {
|
|
description = "The level of compression for the file systems on a scale of 1-8",
|
|
default = 4,
|
|
type = "number",
|
|
})
|
|
settings.save()
|
|
local compression_level = settings.get("containers.compression_level")
|
|
|
|
local lib = {}
|
|
|
|
local function deepcopy(o, seen)
|
|
seen = seen or {}
|
|
if o == nil then return nil end
|
|
if seen[o] then return seen[o] end
|
|
|
|
local no
|
|
if type(o) == 'table' then
|
|
no = {}
|
|
seen[o] = no
|
|
|
|
for k, v in next, o, nil do
|
|
no[deepcopy(k, seen)] = deepcopy(v, seen)
|
|
end
|
|
setmetatable(no, deepcopy(getmetatable(o), seen))
|
|
else -- number, string, boolean, etc
|
|
no = o
|
|
end
|
|
return no
|
|
end
|
|
|
|
|
|
local function buildRom(path)
|
|
sleep()
|
|
local out = {}
|
|
for _, file in ipairs(fs.list(path)) do
|
|
local fullPath = fs.combine(path, file)
|
|
if fs.isDir(fullPath) then
|
|
out[file] = buildRom(fullPath)
|
|
else
|
|
local handle = fs.open(fullPath, "r")
|
|
out[file] = handle.readAll()
|
|
handle.close()
|
|
end
|
|
end
|
|
return out
|
|
end
|
|
|
|
local function getRom()
|
|
if not _G.rom_cache then
|
|
_G.rom_cache = buildRom("/rom")
|
|
end
|
|
return _G.rom_cache
|
|
end
|
|
|
|
function lib.getENV(fspath, has_http)
|
|
local global = deepcopy(_G)
|
|
setmetatable(global, { __index = _G })
|
|
local function getFileSystem()
|
|
if filesystem then filesystem.rom = getRom() return filesystem end
|
|
if not fs.exists(fspath) then
|
|
return {rom=getRom()}
|
|
end
|
|
local file = fs.open(fspath, "r")
|
|
local compressedData = file.readAll()
|
|
file.close()
|
|
local decompressedData = deflate:DecompressDeflate(compressedData,{level=compression_level})
|
|
local data = textutils.unserialize(decompressedData) or {}
|
|
filesystem = data
|
|
data.rom = getRom()
|
|
return data
|
|
end
|
|
|
|
local function setFileSystem(data)
|
|
filesystem = data
|
|
data.rom = nil
|
|
data = textutils.serialize(data)
|
|
local file = fs.open(fspath, "w")
|
|
file.write(deflate:CompressDeflate(data,{level=compression_level}))
|
|
file.close()
|
|
end
|
|
|
|
local function normalizeParts(path)
|
|
local parts = {}
|
|
for part in string.gmatch(path, "[^/]+") do
|
|
if part == "" or part == "." then
|
|
-- skip
|
|
elseif part == ".." then
|
|
if #parts > 0 then table.remove(parts) end
|
|
else
|
|
parts[#parts + 1] = part
|
|
end
|
|
end
|
|
return parts
|
|
end
|
|
|
|
local function getDataAt(path)
|
|
expect(1, path, "string")
|
|
local parts = normalizeParts(path)
|
|
local current = getFileSystem()
|
|
if #parts == 0 then return current end
|
|
for i = 1, #parts do
|
|
local part = parts[i]
|
|
if type(current) ~= "table" then
|
|
-- trying to index into a file
|
|
return nil
|
|
end
|
|
current = current[part]
|
|
if current == nil then return nil end
|
|
end
|
|
return current
|
|
end
|
|
|
|
local function setDataAt(path, data)
|
|
expect(1, path, "string")
|
|
local parts = normalizeParts(path)
|
|
local filesys = getFileSystem()
|
|
local current = filesys
|
|
for i, part in ipairs(parts) do
|
|
if i == #parts then
|
|
current[part] = data
|
|
elseif type(current[part]) == "table" then
|
|
current = current[part]
|
|
elseif type(current[part]) == "string" then
|
|
return
|
|
end
|
|
end
|
|
setFileSystem(filesys)
|
|
end
|
|
|
|
|
|
function global.fs.list(path)
|
|
expect(1, path, "string")
|
|
local out = {}
|
|
local data = getDataAt(path)
|
|
if data then
|
|
for k, v in pairs(data) do
|
|
table.insert(out, k)
|
|
end
|
|
end
|
|
return out
|
|
end
|
|
|
|
function global.fs.isDir(path)
|
|
expect(1, path, "string")
|
|
local data = getDataAt(path)
|
|
return type(data) == "table"
|
|
end
|
|
|
|
function global.fs.isReadOnly(path)
|
|
if path == "" then return false end
|
|
expect(1, path, "string")
|
|
local offset = string.find(path, "rom")
|
|
return offset ~= nil and offset < 3
|
|
end
|
|
|
|
function global.fs.exists(path)
|
|
expect(1, path, "string")
|
|
local data = getDataAt(path)
|
|
return data ~= nil
|
|
end
|
|
|
|
function global.fs.complete(sPath, sLocation, bIncludeFiles, bIncludeDirs)
|
|
expect(1, sPath, "string")
|
|
expect(2, sLocation, "string")
|
|
local bIncludeHidden = nil
|
|
if type(bIncludeFiles) == "table" then
|
|
bIncludeDirs = field(bIncludeFiles, "include_dirs", "boolean", "nil")
|
|
bIncludeHidden = field(bIncludeFiles, "include_hidden", "boolean", "nil")
|
|
bIncludeFiles = field(bIncludeFiles, "include_files", "boolean", "nil")
|
|
else
|
|
expect(3, bIncludeFiles, "boolean", "nil")
|
|
expect(4, bIncludeDirs, "boolean", "nil")
|
|
end
|
|
|
|
bIncludeHidden = bIncludeHidden ~= false
|
|
bIncludeFiles = bIncludeFiles ~= false
|
|
bIncludeDirs = bIncludeDirs ~= false
|
|
local sDir = sLocation
|
|
local nStart = 1
|
|
local nSlash = string.find(sPath, "[/\\]", nStart)
|
|
if nSlash == 1 then
|
|
sDir = ""
|
|
nStart = 2
|
|
end
|
|
local sName
|
|
while not sName do
|
|
local nSlash = string.find(sPath, "[/\\]", nStart)
|
|
if nSlash then
|
|
local sPart = string.sub(sPath, nStart, nSlash - 1)
|
|
sDir = global.fs.combine(sDir, sPart)
|
|
nStart = nSlash + 1
|
|
else
|
|
sName = string.sub(sPath, nStart)
|
|
end
|
|
end
|
|
|
|
if global.fs.isDir(sDir) then
|
|
local tResults = {}
|
|
if bIncludeDirs and sPath == "" then
|
|
table.insert(tResults, ".")
|
|
end
|
|
if sDir ~= "" then
|
|
if sPath == "" then
|
|
table.insert(tResults, bIncludeDirs and ".." or "../")
|
|
elseif sPath == "." then
|
|
table.insert(tResults, bIncludeDirs and "." or "./")
|
|
end
|
|
end
|
|
local tFiles = global.fs.list(sDir)
|
|
for n = 1, #tFiles do
|
|
local sFile = tFiles[n]
|
|
if #sFile >= #sName and string.sub(sFile, 1, #sName) == sName and (
|
|
bIncludeHidden or sFile:sub(1, 1) ~= "." or sName:sub(1, 1) == "."
|
|
) then
|
|
local bIsDir = global.fs.isDir(fs.combine(sDir, sFile))
|
|
local sResult = string.sub(sFile, #sName + 1)
|
|
if bIsDir then
|
|
table.insert(tResults, sResult .. "/")
|
|
if bIncludeDirs and #sResult > 0 then
|
|
table.insert(tResults, sResult)
|
|
end
|
|
else
|
|
if bIncludeFiles and #sResult > 0 then
|
|
table.insert(tResults, sResult)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return tResults
|
|
end
|
|
|
|
return {}
|
|
end
|
|
|
|
local function getReadHandle(path, isBinary)
|
|
expect(1, path, "string")
|
|
local data = getDataAt(path) or ""
|
|
local len = #data
|
|
local seek = 1
|
|
|
|
return {
|
|
readAll = function()
|
|
if data == nil then return nil end
|
|
data = data:sub(seek)
|
|
seek = len + 1
|
|
return data
|
|
end,
|
|
|
|
readLine = function(includeTrailing)
|
|
if data == nil then return nil end
|
|
if seek > len then return nil end -- EOF guard
|
|
|
|
local start = seek
|
|
local nl = data:find("\n", start, true)
|
|
local line
|
|
|
|
if nl then
|
|
if includeTrailing then
|
|
line = data:sub(start, nl) -- include "\n"
|
|
else
|
|
line = data:sub(start, nl - 1) -- exclude "\n"
|
|
end
|
|
seek = nl + 1
|
|
else
|
|
-- last line (no trailing newline)
|
|
line = data:sub(start)
|
|
seek = len + 1
|
|
end
|
|
|
|
-- NOTE: do NOT treat "" as EOF; blank lines are valid
|
|
return line
|
|
end,
|
|
|
|
read = function(count)
|
|
if type(count) == "table" then
|
|
count = 1
|
|
end
|
|
expect(1, count, "number", "nil")
|
|
if data == nil then return nil end
|
|
if not count then count = 1 end
|
|
if seek > len then return nil end
|
|
|
|
local toReturn = string.sub(data, seek, seek + count - 1)
|
|
seek = seek + #toReturn
|
|
if toReturn == "" then
|
|
return nil
|
|
elseif isBinary and count == 1 then
|
|
return string.byte(toReturn)
|
|
end
|
|
return toReturn
|
|
end,
|
|
|
|
seek = function (offset, whence)
|
|
if data == nil then return nil, "File is closed" end
|
|
offset = offset or 0
|
|
whence = whence or "cur"
|
|
|
|
if whence == "set" then
|
|
seek = math.min(math.max(0, offset), len) + 1
|
|
elseif whence == "cur" then
|
|
seek = math.min(math.max(1, seek + offset), len + 1)
|
|
elseif whence == "end" then
|
|
seek = math.min(math.max(0, len + offset), len) + 1
|
|
end
|
|
|
|
return seek - 1
|
|
end,
|
|
|
|
close = function()
|
|
data = nil
|
|
end,
|
|
}
|
|
end
|
|
|
|
|
|
local function getWriteHandle(path, isBinary, append_mode)
|
|
local data = ""
|
|
local seek = 1
|
|
|
|
if append_mode then
|
|
data = getDataAt(path) or ""
|
|
seek = #data + 1
|
|
end
|
|
|
|
local handle -- forward declared so writeLine can see it
|
|
|
|
local function hwrite(str)
|
|
if data == nil then error("File is closed", 2) end
|
|
if type(str) == "number" and isBinary then
|
|
str = string.char(str)
|
|
elseif type(str) ~= "string" then
|
|
error("bad argument #1 (string or number expected, got " .. type(str) .. " )", 2)
|
|
end
|
|
|
|
data = string.sub(data, 1, seek - 1) .. str .. string.sub(data, seek + #str)
|
|
seek = seek + #str
|
|
end
|
|
|
|
handle = {
|
|
write = hwrite,
|
|
writeLine = function (str)
|
|
if data == nil then error("File is closed", 2) end
|
|
expect(1, str, "string")
|
|
hwrite(str .. "\n") -- **no global write**
|
|
end,
|
|
flush = function()
|
|
if data == nil then error("File is closed", 2) end
|
|
setDataAt(path, data)
|
|
end,
|
|
close = function()
|
|
if data == nil then error("File is already closed", 2) end
|
|
setDataAt(path, data)
|
|
data = nil
|
|
end,
|
|
}
|
|
|
|
return handle
|
|
end
|
|
|
|
local function getReadWriteHandle(path, isBinary, erase_data)
|
|
expect(1, path, "string")
|
|
local data = erase_data and "" or (getDataAt(path) or "")
|
|
local len = #data
|
|
local seek = 1
|
|
|
|
local handle = {}
|
|
|
|
handle.readAll = function()
|
|
if data == nil then return nil end
|
|
data = data:sub(seek)
|
|
seek = len + 1
|
|
return data
|
|
end
|
|
|
|
handle.readLine = function(includeTrailing)
|
|
if data == nil then return nil end
|
|
if seek > len then return nil end -- EOF guard
|
|
|
|
local start = seek
|
|
local nl = data:find("\n", start, true)
|
|
local line
|
|
|
|
if nl then
|
|
if includeTrailing then
|
|
line = data:sub(start, nl)
|
|
else
|
|
line = data:sub(start, nl - 1)
|
|
end
|
|
seek = nl + 1
|
|
else
|
|
line = data:sub(start)
|
|
seek = len + 1
|
|
end
|
|
|
|
return line
|
|
end
|
|
|
|
function handle.read(count)
|
|
if type(count) == "table" then
|
|
count = 1
|
|
end
|
|
expect(1, count, "number", "nil")
|
|
if data == nil then error("File is closed", 2) end
|
|
if not count then count = 1 end
|
|
if seek > len then return nil end
|
|
|
|
local toReturn = data:sub(seek, seek + count - 1)
|
|
seek = seek + #toReturn
|
|
|
|
if toReturn == "" then
|
|
return nil
|
|
elseif isBinary and count == 1 then
|
|
return string.byte(toReturn)
|
|
end
|
|
return toReturn
|
|
end
|
|
|
|
function handle.seek(offset, whence)
|
|
if data == nil then return nil, "File is closed" end
|
|
|
|
offset = offset or 0
|
|
whence = whence or "cur"
|
|
|
|
if whence == "set" then
|
|
seek = math.min(math.max(0, offset), len) + 1
|
|
elseif whence == "cur" then
|
|
seek = math.min(math.max(1, seek + offset), len + 1)
|
|
elseif whence == "end" then
|
|
seek = math.min(math.max(0, len + offset), len) + 1
|
|
else
|
|
error("bad argument #2 (invalid whence)", 2)
|
|
end
|
|
|
|
return seek - 1
|
|
end
|
|
|
|
function handle.write(str)
|
|
if type(str) == "number" and isBinary then
|
|
str = string.char(str)
|
|
elseif type(str) ~= "string" then
|
|
error("bad argument #1 (string or number expected, got " .. type(str) .. " )", 2)
|
|
end
|
|
if data == nil then error("File is closed", 2) end
|
|
|
|
data = string.sub(data, 1, seek - 1) .. str .. string.sub(data, seek + #str)
|
|
seek = seek + #str
|
|
len = #data
|
|
end
|
|
|
|
function handle.writeLine(str)
|
|
expect(1, str, "string")
|
|
handle.write(str .. "\n")
|
|
end
|
|
|
|
function handle.flush()
|
|
if data == nil then error("File is closed", 2) end
|
|
setDataAt(path, data)
|
|
end
|
|
|
|
function handle.close()
|
|
if data == nil then error("File is already closed", 2) end
|
|
setDataAt(path, data)
|
|
data = nil
|
|
end
|
|
|
|
return handle
|
|
end
|
|
|
|
local function ioHandleWrapper(handle)
|
|
local closed = false
|
|
return {
|
|
read = function (self, mode)
|
|
if closed then error("File is closed", 2) end
|
|
expect(2, mode, "string", "nil")
|
|
if not mode then mode = "l" end
|
|
if type(self) ~= "table" then
|
|
error("bad argument #1 (FILE expected, got " .. type(self) .. " )", 2)
|
|
end
|
|
|
|
if string.find(mode, "%*") == 1 then
|
|
mode = mode:gsub("%*", "")
|
|
end
|
|
|
|
if mode == "l" then
|
|
return handle.readLine(false)
|
|
elseif mode == "L" then
|
|
return handle.readLine(true)
|
|
elseif mode == "a" then
|
|
return handle.readAll()
|
|
else
|
|
error("Unsupported read mode", 2)
|
|
end
|
|
end,
|
|
|
|
seek = function (self, whence, offset)
|
|
if closed then error("File is closed", 2) end
|
|
expect(2, whence, "string", "nil")
|
|
expect(3, offset, "number", "nil")
|
|
if type(self) ~= "table" then
|
|
error("bad argument #1 (FILE expected, got " .. type(self) .. " )", 2)
|
|
end
|
|
return handle.seek(offset, whence)
|
|
end,
|
|
|
|
write = function (self, ...)
|
|
if closed then error("File is closed", 2) end
|
|
if type(self) ~= "table" then
|
|
error("bad argument #1 (FILE expected, got " .. type(self) .. " )", 2)
|
|
end
|
|
|
|
local args = { ... }
|
|
for i = 1, #args do
|
|
local arg = args[i]
|
|
if type(arg) ~= "string" and type(arg) ~= "number" then
|
|
error("bad argument #" .. (i + 1) ..
|
|
" (string or number expected, got " .. type(arg) .. " )", 2)
|
|
end
|
|
-- preserve your existing semantics: underlying handle decides what to do
|
|
handle.write(arg)
|
|
end
|
|
end,
|
|
|
|
close = function (self)
|
|
if closed then error("File is already closed", 2) end
|
|
if type(self) ~= "table" then
|
|
error("bad argument #1 (FILE expected, got " .. type(self) .. " )", 2)
|
|
end
|
|
handle.close()
|
|
closed = true
|
|
end,
|
|
|
|
flush = function (self)
|
|
if closed then error("File is closed", 2) end
|
|
if type(self) ~= "table" then
|
|
error("bad argument #1 (FILE expected, got " .. type(self) .. " )", 2)
|
|
end
|
|
handle.flush()
|
|
end,
|
|
|
|
lines = function (self)
|
|
if closed then error("File is closed", 2) end
|
|
if type(self) ~= "table" then
|
|
error("bad argument #1 (FILE expected, got " .. type(self) .. " )", 2)
|
|
end
|
|
return function()
|
|
if closed then error("file is already closed", 2) end
|
|
return handle.readLine(false)
|
|
end
|
|
end,
|
|
}
|
|
end
|
|
|
|
|
|
function global.fs.open(path,mode)
|
|
expect(1, path, "string")
|
|
expect(2, mode, "string")
|
|
if mode == "r" or mode == "rb" then
|
|
if global.fs.exists(path) then
|
|
return getReadHandle(path, mode == "rb")
|
|
else
|
|
error("File not found", 2)
|
|
end
|
|
elseif mode == "w" or mode == "wb" then
|
|
return getWriteHandle(path, mode == "wb", false)
|
|
elseif mode == "a" or mode == "ab" then
|
|
return getWriteHandle(path, mode == "ab", true)
|
|
elseif mode == "r+" or mode == "rb+" or mode == "r+b" then
|
|
if global.fs.exists(path) then
|
|
return getReadWriteHandle(path, mode == "rb+" or mode == "r+b", false)
|
|
else
|
|
error("File not found", 2)
|
|
end
|
|
elseif mode == "w+" or mode == "wb+" or mode == "w+b" then
|
|
return getReadWriteHandle(path, mode == "wb+" or mode == "w+b", true)
|
|
elseif mode == "a+" or mode == "ab+" or mode == "a+b" then
|
|
return getReadWriteHandle(path, mode == "ab+" or mode == "a+b", true)
|
|
else
|
|
error("Invalid mode", 2)
|
|
end
|
|
end
|
|
|
|
function global.fs.move(srcPath, dstPath)
|
|
expect(1, srcPath, "string")
|
|
expect(2, dstPath, "string")
|
|
if not global.fs.exists(srcPath) then
|
|
error("Source does not exist", 2)
|
|
end
|
|
if global.fs.exists(dstPath) then
|
|
error("Destination already exists", 2)
|
|
end
|
|
local data = getDataAt(srcPath)
|
|
setDataAt(dstPath, data)
|
|
setDataAt(srcPath, nil)
|
|
end
|
|
|
|
function global.fs.copy(srcPath, dstPath)
|
|
expect(1, srcPath, "string")
|
|
expect(2, dstPath, "string")
|
|
if not global.fs.exists(srcPath) then
|
|
error("Source does not exist", 2)
|
|
end
|
|
if global.fs.exists(dstPath) then
|
|
error("Destination already exists", 2)
|
|
end
|
|
local data = getDataAt(srcPath)
|
|
setDataAt(dstPath, data)
|
|
end
|
|
|
|
local function find_aux(path, parts, i, out)
|
|
local part = parts[i]
|
|
if not part then
|
|
-- If we're at the end of the pattern, ensure our path exists and append it.
|
|
if global.fs.exists(path) then out[#out + 1] = path end
|
|
elseif part.exact then
|
|
-- If we're an exact match, just recurse into this directory.
|
|
return find_aux(global.fs.combine(path, part.contents), parts, i + 1, out)
|
|
else
|
|
-- Otherwise we're a pattern. Check we're a directory, then recurse into each
|
|
-- matching file.
|
|
if not global.fs.isDir(path) then return end
|
|
|
|
local files = global.fs.list(path)
|
|
for j = 1, #files do
|
|
local file = files[j]
|
|
if file:find(part.contents) then find_aux(global.fs.combine(path, file), parts, i + 1, out) end
|
|
end
|
|
end
|
|
end
|
|
|
|
local find_escape = {
|
|
-- Escape standard Lua pattern characters
|
|
["^"] = "%^", ["$"] = "%$", ["("] = "%(", [")"] = "%)", ["%"] = "%%",
|
|
["."] = "%.", ["["] = "%[", ["]"] = "%]", ["+"] = "%+", ["-"] = "%-",
|
|
-- Aside from our wildcards.
|
|
["*"] = ".*",
|
|
["?"] = ".",
|
|
}
|
|
|
|
function global.fs.find(pattern)
|
|
expect(1, pattern, "string")
|
|
|
|
pattern = global.fs.combine(pattern) -- Normalise the path, removing ".."s.
|
|
|
|
-- If the pattern is trying to search outside the computer root, just abort.
|
|
-- This will fail later on anyway.
|
|
if pattern == ".." or pattern:sub(1, 3) == "../" then
|
|
error("/" .. pattern .. ": Invalid Path", 2)
|
|
end
|
|
|
|
-- If we've no wildcards, just check the file exists.
|
|
if not pattern:find("[*?]") then
|
|
if global.fs.exists(pattern) then return { pattern } else return {} end
|
|
end
|
|
|
|
local parts = {}
|
|
for part in pattern:gmatch("[^/]+") do
|
|
if part:find("[*?]") then
|
|
parts[#parts + 1] = {
|
|
exact = false,
|
|
contents = "^" .. part:gsub(".", find_escape) .. "$",
|
|
}
|
|
else
|
|
parts[#parts + 1] = { exact = true, contents = part }
|
|
end
|
|
end
|
|
|
|
local out = {}
|
|
find_aux("", parts, 1, out)
|
|
return out
|
|
end
|
|
|
|
function global.io.open(path, mode)
|
|
expect(1, path, "string")
|
|
expect(2, mode, "string", "nil")
|
|
if not mode then mode = "r" end
|
|
local handle
|
|
if mode == "r" or mode == "rb" then
|
|
if global.fs.exists(path) and not global.fs.isDir(path) then
|
|
handle = getReadHandle(path, mode == "rb")
|
|
elseif global.fs.isDir(path) then
|
|
return nil, "Attempt to open a directory"
|
|
else
|
|
return nil, "File not found"
|
|
end
|
|
elseif mode == "w" or mode == "wb" then
|
|
handle = getWriteHandle(path, mode == "wb", false)
|
|
elseif mode == "a" or mode == "ab" then
|
|
handle = getWriteHandle(path, mode == "ab", true)
|
|
elseif mode == "r+" or mode == "rb+" or mode == "r+b" then
|
|
if global.fs.exists(path) and not global.fs.isDir(path) then
|
|
handle = getReadWriteHandle(path, mode == "rb+" or mode == "r+b", false)
|
|
elseif global.fs.isDir(path) then
|
|
return nil, "Attempt to open a directory"
|
|
else
|
|
return nil, "File not found"
|
|
end
|
|
elseif mode == "w+" or mode == "wb+" or mode == "w+b" then
|
|
handle = getReadWriteHandle(path, mode == "wb+" or mode == "w+b", true)
|
|
elseif mode == "a+" or mode == "ab+" or mode == "a+b" then
|
|
handle = getReadWriteHandle(path, mode == "ab+" or mode == "a+b", true)
|
|
else
|
|
return nil, "Invalid mode"
|
|
end
|
|
return ioHandleWrapper(handle)
|
|
end
|
|
function global.io.lines(path,...)
|
|
local args = {...}
|
|
expect(1, path, "string")
|
|
local handle = global.io.open(path, table.unpack(args))
|
|
local count = 0
|
|
return function ()
|
|
count = count + 1
|
|
local line = handle:read(args[count%#args + 1] or "l")
|
|
if line == nil then
|
|
handle:close()
|
|
end
|
|
return line
|
|
end
|
|
end
|
|
function global.fs.delete(path)
|
|
expect(1, path, "string")
|
|
if not global.fs.exists(path) then
|
|
error("File not found", 2)
|
|
end
|
|
setDataAt(path, nil)
|
|
end
|
|
function global.fs.getSize(path)
|
|
expect(1, path, "string")
|
|
if not global.fs.exists(path) then
|
|
error("File not found", 2)
|
|
end
|
|
if global.fs.isDir(path) then
|
|
return 0
|
|
else
|
|
return #getDataAt(path)
|
|
end
|
|
end
|
|
|
|
function global.fs.makeDir(path)
|
|
expect(1, path, "string")
|
|
if global.fs.exists(path) then
|
|
return
|
|
end
|
|
setDataAt(path, {})
|
|
end
|
|
|
|
local tAPIsLoading = {}
|
|
|
|
local bAPIError = false
|
|
|
|
function global.loadfile(filename, mode, env)
|
|
-- Support the previous `loadfile(filename, env)` form instead.
|
|
if type(mode) == "table" and env == nil then
|
|
mode, env = nil, mode
|
|
end
|
|
|
|
expect(1, filename, "string")
|
|
expect(2, mode, "string", "nil")
|
|
expect(3, env, "table", "nil")
|
|
|
|
local file = global.fs.open(filename, "r")
|
|
if not file then return nil, "File not found" end
|
|
|
|
local func, err = load(file.readAll(), "@/" .. fs.combine(filename), mode, env)
|
|
file.close()
|
|
return func, err
|
|
end
|
|
|
|
function global.dofile(_sFile)
|
|
expect(1, _sFile, "string")
|
|
|
|
local fnFile, e = global.loadfile(_sFile, nil, _G)
|
|
if fnFile then
|
|
return fnFile()
|
|
else
|
|
error(e, 2)
|
|
end
|
|
end
|
|
|
|
function global.loadAPI(_sPath)
|
|
expect(1, _sPath, "string")
|
|
local sName = fs.getName(_sPath)
|
|
if sName:sub(-4) == ".lua" then
|
|
sName = sName:sub(1, -5)
|
|
end
|
|
if sName == "term" then
|
|
return true
|
|
end
|
|
if tAPIsLoading[sName] == true then
|
|
printError("API " .. sName .. " is already being loaded")
|
|
return false
|
|
end
|
|
tAPIsLoading[sName] = true
|
|
|
|
local tEnv = {}
|
|
setmetatable(tEnv, { __index = global })
|
|
local fnAPI, err = global.loadfile(_sPath, nil, tEnv)
|
|
if fnAPI then
|
|
local ok, err = pcall(fnAPI)
|
|
if not ok then
|
|
tAPIsLoading[sName] = nil
|
|
return error("Failed to load API " .. sName .. " due to " .. err, 1)
|
|
end
|
|
else
|
|
tAPIsLoading[sName] = nil
|
|
return error("Failed to load API " .. sName .. " due to " .. err, 1)
|
|
end
|
|
|
|
local tAPI = {}
|
|
for k, v in pairs(tEnv) do
|
|
if k ~= "_ENV" then
|
|
tAPI[k] = v
|
|
end
|
|
end
|
|
global[sName] = tAPI
|
|
tAPIsLoading[sName] = nil
|
|
return true
|
|
end
|
|
local function load_apis(dir)
|
|
if not global.fs.isDir(dir) then return end
|
|
|
|
for _, file in ipairs(global.fs.list(dir)) do
|
|
if file:sub(1, 1) ~= "." then
|
|
local path = fs.combine(dir, file)
|
|
if not fs.isDir(path) then
|
|
if not global.loadAPI(path) then
|
|
bAPIError = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function global.os.run(_tEnv, _sPath, ...)
|
|
expect(1, _tEnv, "table")
|
|
expect(2, _sPath, "string")
|
|
|
|
local tEnv = _tEnv
|
|
setmetatable(tEnv, { __index = global })
|
|
|
|
if settings.get("bios.strict_globals", false) then
|
|
-- load will attempt to set _ENV on this environment, which
|
|
-- throws an error with this protection enabled. Thus we set it here first.
|
|
tEnv._ENV = tEnv
|
|
getmetatable(tEnv).__newindex = function(_, name)
|
|
error("Attempt to create global " .. tostring(name), 2)
|
|
end
|
|
end
|
|
|
|
local fnFile, err = global.loadfile(_sPath, nil, tEnv)
|
|
if fnFile then
|
|
local ok, err = pcall(fnFile, ...)
|
|
if not ok then
|
|
if err and err ~= "" then
|
|
printError(err)
|
|
end
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
if err and err ~= "" then
|
|
printError(err)
|
|
end
|
|
return false
|
|
end
|
|
global.settings = settings
|
|
global._ENV = global
|
|
global._G = global
|
|
global.shell = nil
|
|
global.multishell = nil
|
|
load_apis("rom/apis")
|
|
if http and has_http then global.http = http load_apis("rom/apis/http") end
|
|
return global
|
|
end
|
|
function lib.start(env)
|
|
local func = setfenv(function ()
|
|
settings.define("shell.allow_startup", {
|
|
default = true,
|
|
description = "Run startup files when the computer turns on.",
|
|
type = "boolean",
|
|
})
|
|
settings.define("shell.allow_disk_startup", {
|
|
default = commands == nil,
|
|
description = "Run startup files from disk drives when the computer turns on.",
|
|
type = "boolean",
|
|
})
|
|
|
|
settings.define("shell.autocomplete", {
|
|
default = true,
|
|
description = "Autocomplete program and arguments in the shell.",
|
|
type = "boolean",
|
|
})
|
|
settings.define("edit.autocomplete", {
|
|
default = true,
|
|
description = "Autocomplete API and function names in the editor.",
|
|
type = "boolean",
|
|
})
|
|
settings.define("lua.autocomplete", {
|
|
default = true,
|
|
description = "Autocomplete API and function names in the Lua REPL.",
|
|
type = "boolean",
|
|
})
|
|
|
|
settings.define("edit.default_extension", {
|
|
default = "lua",
|
|
description = [[The file extension the editor will use if none is given. Set to "" to disable.]],
|
|
type = "string",
|
|
})
|
|
settings.define("paint.default_extension", {
|
|
default = "nfp",
|
|
description = [[The file extension the paint program will use if none is given. Set to "" to disable.]],
|
|
type = "string",
|
|
})
|
|
|
|
settings.define("list.show_hidden", {
|
|
default = false,
|
|
description = [[Whether the list program show hidden files (those starting with ".").]],
|
|
type = "boolean",
|
|
})
|
|
|
|
settings.define("motd.enable", {
|
|
default = pocket == nil,
|
|
description = "Display a random message when the computer starts up.",
|
|
type = "boolean",
|
|
})
|
|
settings.define("motd.path", {
|
|
default = "/rom/motd.txt:/motd.txt",
|
|
description = [[The path to load random messages from. Should be a colon (":") separated string of file paths.]],
|
|
type = "string",
|
|
})
|
|
|
|
settings.define("lua.warn_against_use_of_local", {
|
|
default = true,
|
|
description = [[Print a message when input in the Lua REPL starts with the word 'local'. Local variables defined in the Lua REPL are be inaccessible on the next input.]],
|
|
type = "boolean",
|
|
})
|
|
settings.define("lua.function_args", {
|
|
default = true,
|
|
description = "Show function arguments when printing functions.",
|
|
type = "boolean",
|
|
})
|
|
settings.define("lua.function_source", {
|
|
default = false,
|
|
description = "Show where a function was defined when printing functions.",
|
|
type = "boolean",
|
|
})
|
|
settings.define("bios.strict_globals", {
|
|
default = false,
|
|
description = "Prevents assigning variables into a program's environment. Make sure you use the local keyword or assign to _G explicitly.",
|
|
type = "boolean",
|
|
})
|
|
settings.define("shell.autocomplete_hidden", {
|
|
default = false,
|
|
description = [[Autocomplete hidden files and folders (those starting with ".").]],
|
|
type = "boolean",
|
|
})
|
|
print("running container!")
|
|
local ok, err = pcall(parallel.waitForAny,
|
|
function()
|
|
local sShell
|
|
if term.isColour() and settings.get("bios.use_multishell") then
|
|
sShell = "rom/programs/advanced/multishell.lua"
|
|
else
|
|
sShell = "rom/programs/shell.lua"
|
|
end
|
|
os.run({}, sShell)
|
|
os.run({}, "rom/programs/shutdown.lua")
|
|
end,
|
|
rednet.run
|
|
)
|
|
end,env)
|
|
func()
|
|
end
|
|
return lib |