From 4e0a5871bd22101e980b50cafb6b7e418f916fb9 Mon Sep 17 00:00:00 2001 From: Rivulet Date: Mon, 22 Dec 2025 12:01:01 -0800 Subject: [PATCH] so so much... --- .settings | 3 + apps/test | Bin 0 -> 28696 bytes entertest.lua | 6 + libs/containers.lua | 1524 +++++++++++++++++++++++++++++++ startup/00_entrypointclient.lua | 119 +++ startup/01_repo.lua | 3 + startup/99_phoneOS.lua | 85 ++ 7 files changed, 1740 insertions(+) create mode 100644 .settings create mode 100644 apps/test create mode 100644 entertest.lua create mode 100644 libs/containers.lua create mode 100644 startup/00_entrypointclient.lua create mode 100644 startup/01_repo.lua create mode 100644 startup/99_phoneOS.lua diff --git a/.settings b/.settings new file mode 100644 index 0000000..5f66f62 --- /dev/null +++ b/.settings @@ -0,0 +1,3 @@ +{ + [ "motd.path" ] = "/rom/motd.txt:/motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt:/rom/cccbridge_motd.txt", +} \ No newline at end of file diff --git a/apps/test b/apps/test new file mode 100644 index 0000000000000000000000000000000000000000..f61996c0d73c3bb748453521171ac9e32aa6d9c5 GIT binary patch literal 28696 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

#C-CsUS`R&hF;ky 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 Ha(Dmzn(OpY literal 0 HcmV?d00001 diff --git a/entertest.lua b/entertest.lua new file mode 100644 index 0000000..6900767 --- /dev/null +++ b/entertest.lua @@ -0,0 +1,6 @@ +local containers = require "libs.containers" +local appid = ... +local w,h = term.getSize() +local win = window.create(term.native(),1,2,w,h-2,true) +local env = containers.getENV(fs.combine("/apps",appid),true, win) +containers.start(env) \ No newline at end of file diff --git a/libs/containers.lua b/libs/containers.lua new file mode 100644 index 0000000..48df822 --- /dev/null +++ b/libs/containers.lua @@ -0,0 +1,1524 @@ +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]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=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=-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 d70 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/80 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 W0 and T285 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=-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 bqe6+e7 then return-6 end;while bb>0 do bb=bb-1;if bq7 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 specify the file that contains".." the entire preset dictionary.\n".." -h give this help.\n".." --strategy ".." 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 = 8, + 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, term_override) + local global = deepcopy(_G) + global.require = nil + + local native = global.term.native and global.term.native() or global.term + local redirectTarget = native + + local function wrap(_sFunction) + return function(...) + return redirectTarget[_sFunction](...) + end + end + + local term = {} + + --- Redirects terminal output to a monitor, a [`window`], or any other custom + -- terminal object. Once the redirect is performed, any calls to a "term" + -- function - or to a function that makes use of a term function, as [`print`] - + -- will instead operate with the new terminal object. + -- + -- A "terminal object" is simply a table that contains functions with the same + -- names - and general features - as those found in the term table. For example, + -- a wrapped monitor is suitable. + -- + -- The redirect can be undone by pointing back to the previous terminal object + -- (which this function returns whenever you switch). + -- + -- @tparam Redirect target The terminal redirect the [`term`] API will draw to. + -- @treturn Redirect The previous redirect object, as returned by + -- [`term.current`]. + -- @since 1.31 + -- @usage + -- Redirect to a monitor on the right of the computer. + -- + -- term.redirect(peripheral.wrap("right")) + term.redirect = function(target) + expect(1, target, "table") + if target == term or target == global.term then + error("term is not a recommended redirect target, try term.current() instead", 2) + end + for k, v in pairs(native) do + if type(k) == "string" and type(v) == "function" then + if type(target[k]) ~= "function" then + target[k] = function() + error("Redirect object is missing method " .. k .. ".", 2) + end + end + end + end + redirectTarget = target + return oldRedirectTarget + end + + --- Returns the current terminal object of the computer. + -- + -- @treturn Redirect The current terminal redirect + -- @since 1.6 + -- @usage + -- Create a new [`window`] which draws to the current redirect target. + -- + -- window.create(term.current(), 1, 1, 10, 10) + term.current = function() + return redirectTarget + end + + --- Get the native terminal object of the current computer. + -- + -- It is recommended you do not use this function unless you absolutely have + -- to. In a multitasked environment, [`term.native`] will _not_ be the current + -- terminal object, and so drawing may interfere with other programs. + -- + -- @treturn Redirect The native terminal redirect. + -- @since 1.6 + term.native = function() + return native + end + + -- Some methods shouldn't go through redirects, so we move them to the main + -- term API. + for _, method in ipairs { "nativePaletteColor", "nativePaletteColour" } do + term[method] = native[method] + native[method] = nil + end + + for k, v in pairs(native) do + if type(k) == "string" and type(v) == "function" and rawget(term, k) == nil then + term[k] = wrap(k) + end + end + + local function write(sText) + expect(1, sText, "string", "number") + + local w, h = term.getSize() + local x, y = term.getCursorPos() + + local nLinesPrinted = 0 + local function newLine() + if y + 1 <= h then + term.setCursorPos(1, y + 1) + else + term.setCursorPos(1, h) + term.scroll(1) + end + x, y = term.getCursorPos() + nLinesPrinted = nLinesPrinted + 1 + end + + -- Print the line with proper word wrapping + sText = tostring(sText) + while #sText > 0 do + local whitespace = string.match(sText, "^[ \t]+") + if whitespace then + -- Print whitespace + term.write(whitespace) + x, y = term.getCursorPos() + sText = string.sub(sText, #whitespace + 1) + end + + local newline = string.match(sText, "^\n") + if newline then + -- Print newlines + newLine() + sText = string.sub(sText, 2) + end + + local text = string.match(sText, "^[^ \t\n]+") + if text then + sText = string.sub(sText, #text + 1) + if #text > w then + -- Print a multiline word + while #text > 0 do + if x > w then + newLine() + end + term.write(text) + text = string.sub(text, w - x + 2) + x, y = term.getCursorPos() + end + else + -- Print a word normally + if x + #text - 1 > w then + newLine() + end + term.write(text) + x, y = term.getCursorPos() + end + end + end + + return nLinesPrinted + end + + local function print(...) + local nLinesPrinted = 0 + local nLimit = select("#", ...) + for n = 1, nLimit do + local s = tostring(select(n, ...)) + if n < nLimit then + s = s .. "\t" + end + nLinesPrinted = nLinesPrinted + write(s) + end + nLinesPrinted = nLinesPrinted + write("\n") + return nLinesPrinted + end + + local function printError(...) + local oldColour + if term.isColour() then + oldColour = term.getTextColour() + term.setTextColour(colors.red) + end + print(...) + if term.isColour() then + term.setTextColour(oldColour) + end + end + + local function read(_sReplaceChar, _tHistory, _fnComplete, _sDefault) + expect(1, _sReplaceChar, "string", "nil") + expect(2, _tHistory, "table", "nil") + expect(3, _fnComplete, "function", "nil") + expect(4, _sDefault, "string", "nil") + + term.setCursorBlink(true) + + local sLine + if type(_sDefault) == "string" then + sLine = _sDefault + else + sLine = "" + end + local nHistoryPos + local nPos, nScroll = #sLine, 0 + if _sReplaceChar then + _sReplaceChar = string.sub(_sReplaceChar, 1, 1) + end + + local tCompletions + local nCompletion + local function recomplete() + if _fnComplete and nPos == #sLine then + tCompletions = _fnComplete(sLine) + if tCompletions and #tCompletions > 0 then + nCompletion = 1 + else + nCompletion = nil + end + else + tCompletions = nil + nCompletion = nil + end + end + + local function uncomplete() + tCompletions = nil + nCompletion = nil + end + + local w = term.getSize() + local sx = term.getCursorPos() + + local function redraw(_bClear) + local cursor_pos = nPos - nScroll + if sx + cursor_pos >= w then + -- We've moved beyond the RHS, ensure we're on the edge. + nScroll = sx + nPos - w + elseif cursor_pos < 0 then + -- We've moved beyond the LHS, ensure we're on the edge. + nScroll = nPos + end + + local _, cy = term.getCursorPos() + term.setCursorPos(sx, cy) + local sReplace = _bClear and " " or _sReplaceChar + if sReplace then + term.write(string.rep(sReplace, math.max(#sLine - nScroll, 0))) + else + term.write(string.sub(sLine, nScroll + 1)) + end + + if nCompletion then + local sCompletion = tCompletions[nCompletion] + local oldText, oldBg + if not _bClear then + oldText = term.getTextColor() + oldBg = term.getBackgroundColor() + term.setTextColor(colors.white) + term.setBackgroundColor(colors.gray) + end + if sReplace then + term.write(string.rep(sReplace, #sCompletion)) + else + term.write(sCompletion) + end + if not _bClear then + term.setTextColor(oldText) + term.setBackgroundColor(oldBg) + end + end + + term.setCursorPos(sx + nPos - nScroll, cy) + end + + local function clear() + redraw(true) + end + + recomplete() + redraw() + + local function acceptCompletion() + if nCompletion then + -- Clear + clear() + + -- Find the common prefix of all the other suggestions which start with the same letter as the current one + local sCompletion = tCompletions[nCompletion] + sLine = sLine .. sCompletion + nPos = #sLine + + -- Redraw + recomplete() + redraw() + end + end + while true do + local sEvent, param, param1, param2 = os.pullEvent() + if sEvent == "char" then + -- Typed key + clear() + sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1) + nPos = nPos + 1 + recomplete() + redraw() + + elseif sEvent == "paste" then + -- Pasted text + clear() + sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1) + nPos = nPos + #param + recomplete() + redraw() + + elseif sEvent == "key" then + if param == keys.enter or param == keys.numPadEnter then + -- Enter/Numpad Enter + if nCompletion then + clear() + uncomplete() + redraw() + end + break + + elseif param == keys.left then + -- Left + if nPos > 0 then + clear() + nPos = nPos - 1 + recomplete() + redraw() + end + + elseif param == keys.right then + -- Right + if nPos < #sLine then + -- Move right + clear() + nPos = nPos + 1 + recomplete() + redraw() + else + -- Accept autocomplete + acceptCompletion() + end + + elseif param == keys.up or param == keys.down then + -- Up or down + if nCompletion then + -- Cycle completions + clear() + if param == keys.up then + nCompletion = nCompletion - 1 + if nCompletion < 1 then + nCompletion = #tCompletions + end + elseif param == keys.down then + nCompletion = nCompletion + 1 + if nCompletion > #tCompletions then + nCompletion = 1 + end + end + redraw() + + elseif _tHistory then + -- Cycle history + clear() + if param == keys.up then + -- Up + if nHistoryPos == nil then + if #_tHistory > 0 then + nHistoryPos = #_tHistory + end + elseif nHistoryPos > 1 then + nHistoryPos = nHistoryPos - 1 + end + else + -- Down + if nHistoryPos == #_tHistory then + nHistoryPos = nil + elseif nHistoryPos ~= nil then + nHistoryPos = nHistoryPos + 1 + end + end + if nHistoryPos then + sLine = _tHistory[nHistoryPos] + nPos, nScroll = #sLine, 0 + else + sLine = "" + nPos, nScroll = 0, 0 + end + uncomplete() + redraw() + + end + + elseif param == keys.backspace then + -- Backspace + if nPos > 0 then + clear() + sLine = string.sub(sLine, 1, nPos - 1) .. string.sub(sLine, nPos + 1) + nPos = nPos - 1 + if nScroll > 0 then nScroll = nScroll - 1 end + recomplete() + redraw() + end + + elseif param == keys.home then + -- Home + if nPos > 0 then + clear() + nPos = 0 + recomplete() + redraw() + end + + elseif param == keys.delete then + -- Delete + if nPos < #sLine then + clear() + sLine = string.sub(sLine, 1, nPos) .. string.sub(sLine, nPos + 2) + recomplete() + redraw() + end + + elseif param == keys["end"] then + -- End + if nPos < #sLine then + clear() + nPos = #sLine + recomplete() + redraw() + end + + elseif param == keys.tab then + -- Tab (accept autocomplete) + acceptCompletion() + + end + + elseif sEvent == "mouse_click" or sEvent == "mouse_drag" and param == 1 then + local _, cy = term.getCursorPos() + if param1 >= sx and param1 <= w and param2 == cy then + -- Ensure we don't scroll beyond the current line + nPos = math.min(math.max(nScroll + param1 - sx, 0), #sLine) + redraw() + end + + elseif sEvent == "term_resize" then + -- Terminal resized + w = term.getSize() + redraw() + + end + end + + local _, cy = term.getCursorPos() + term.setCursorBlink(false) + term.setCursorPos(w + 1, cy) + print() + + return sLine + end + 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 + return nil, "File not found" + 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 + return nil, "File not found" + 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 + return nil, "Invalid read mode" + 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 global.fs.exists(path) then + setDataAt(path, nil) + end + 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, global) + 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 + if network then + global.network = network + end + if app then + global.app = app + end + global.settings = settings + global._ENV = global + global._G = global + global.shell = nil + global.multishell = nil + global.term = term + global.write = write + global.read = read + global.print = print + global.printError = printError + if type(term_override) == "table" then + global.term.redirect(term_override) + end + load_apis("rom/apis") + if http and has_http then global.http = http load_apis("rom/apis/http") end + return global +end + +local exception = dofile("rom/modules/main/cc/internal/tiny_require.lua")("cc.internal.exception") + +local function create(...) + local barrier_ctx = { co = coroutine.running() } + + local functions = table.pack(...) + local threads = {} + for i = 1, functions.n, 1 do + local fn = functions[i] + if type(fn) ~= "function" then + error("bad argument #" .. i .. " (function expected, got " .. type(fn) .. ")", 3) + end + + threads[i] = { co = coroutine.create(function() return exception.try_barrier(barrier_ctx, fn) end), filter = nil } + end + + return threads +end + +local function runUntilLimit(threads, limit) + local count = #threads + if count < 1 then return 0 end + local living = count + + local event = { n = 0 } + while true do + for i = 1, count do + local thread = threads[i] + if thread and (thread.filter == nil or thread.filter == event[1] or event[1] == "terminate") then + local ok, param = coroutine.resume(thread.co, table.unpack(event, 1, event.n)) + if ok then + if param == "thread_shutdown" then + return {command=true,type="shutdown"} + elseif param == "thread_reboot" then + print("reboot") + return {command=true,type="reboot"} + else + thread.filter = param + + end + elseif type(param) == "string" and exception.can_wrap_errors() then + printError(exception.make_exception(param, thread.co)) + else + printError(param) + end + + if coroutine.status(thread.co) == "dead" then + threads[i] = false + living = living - 1 + if living <= limit then + return i + end + end + end + end + + event = table.pack(os.pullEventRaw()) + end +end + +function lib.start(env) + local command = nil + env.os.reboot = function() command = "reboot" sleep() end + env.os.shutdown = function() command = "shutdown" sleep() end + local func = function () + command = nil + 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("bios.use_multishell", { + default = true, + description = "Allow running multiple program at once, through the use of the \"fg\" and \"bg\" programs..", + type = "boolean", + }) + settings.define("shell.autocomplete_hidden", { + default = false, + description = [[Autocomplete hidden files and folders (those starting with ".").]], + type = "boolean", + }) + print("running container!") + + return runUntilLimit(create(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,function () + while true do + sleep() + if command == "shutdown" then + while true do coroutine.yield("thread_shutdown") end + elseif command == "reboot" then + while true do coroutine.yield("thread_reboot") end + end + end + end),0) + end + local command = "starting" + while command == "starting" or (type(command) == "table" and command.command and command.type=="reboot") do + command = setfenv(func,deepcopy(env))() + end +end + +return lib \ No newline at end of file diff --git a/startup/00_entrypointclient.lua b/startup/00_entrypointclient.lua new file mode 100644 index 0000000..4734c84 --- /dev/null +++ b/startup/00_entrypointclient.lua @@ -0,0 +1,119 @@ +local max_distance = 120 +local expect = dofile("rom/modules/main/cc/expect.lua") +local expect, field = expect.expect, expect.field +local pullEvent = os.pullEventRaw +local modem = peripheral.find("modem",function (s) return peripheral.wrap(s).isWireless() end) +term.clear() +term.setCursorPos(1,1) +_G.network = {} +if not modem then + shell.run("shell") + os.shutdown() +end +local message_queue = {} +modem.open(15125) +local canidate = {id = -1, distance = max_distance} +local last_heartbeat = os.epoch("utc") +local function receive() + while true do + local _, _, channel, _, msg, distance = pullEvent("modem_message") + if channel == 15125 then + if msg.protocol == "heartbeat" and msg.target == os.getComputerID() and msg.sender == canidate.id then + canidate.distance = distance + last_heartbeat = os.epoch("utc") + modem.transmit(15125,15125,{protocol="heartbeat_response",sender=os.getComputerID(),target=canidate.id}) + if distance > max_distance then + modem.transmit(15125,15125,{protocol="entrypoint_disconnect",sender=os.getComputerID(),target=canidate.id}) + canidate = {id = -1, distance = max_distance} + parallel.waitForAny(function () repeat sleep(0.1) until canidate.id ~= -1 end, + function () + while true do + local _, _, channel, _, msg, distance = pullEvent("modem_message") + if channel == 15125 then + if msg.protocol == "entrypoint_advertise" then + if distance < canidate.distance then + canidate.id = msg.sender + canidate.distance = distance + end + end + end + end + end) + if canidate.id == -1 then + sleep(5) + else + modem.transmit(15125,15125,{protocol="entrypoint_connect",sender=os.getComputerID(),target=canidate.id}) + last_heartbeat = os.epoch("utc") + end + end + elseif msg.protocol == "packet" then + if msg.hops >= 1 then + os.queueEvent("network_packet",msg.content,msg.sender,msg.hops) + end + end + end + end +end + +function _G.network.send(msg,destination) + if not msg then error("No message provided",2) end + if not destination then error("No destination provided",2) end + message_queue[#message_queue+1] = {protocol="packet",content=msg,destination=destination,sender=os.getComputerID(),hops=0} +end + +function _G.network.getCandidate() + return canidate +end + +local function connect() + while true do + if os.epoch("utc") - last_heartbeat > 200 then + canidate = {id = -1, distance = max_distance} + parallel.waitForAny(function () repeat sleep(0.1) until canidate.id ~= -1 end, + function () + while true do + local _, _, channel, _, msg, distance = pullEvent("modem_message") + if channel == 15125 then + if msg.protocol == "entrypoint_advertise" then + if distance < canidate.distance then + canidate.id = msg.sender + canidate.distance = distance + end + end + end + end + end) + if canidate.id == -1 then + sleep(5) + else + modem.transmit(15125,15125,{protocol="entrypoint_connect",sender=os.getComputerID(),target=canidate.id}) + last_heartbeat = os.epoch("utc") + end + else + local msg = table.remove(message_queue,1) + if msg then + modem.transmit(15125,15125,msg) + end + end + sleep() + end +end +function _G.network.run() + parallel.waitForAny(function () repeat sleep(0.1) until canidate.id ~= -1 end, + function () + while true do + local _, _, channel, _, msg, distance = pullEvent("modem_message") + if channel == 15125 then + if msg.protocol == "entrypoint_advertise" then + if distance < canidate.distance then + canidate.id = msg.sender + canidate.distance = distance + end + end + end + end + end) + last_heartbeat = os.epoch("utc") + modem.transmit(15125,15125,{protocol="entrypoint_connect",sender=os.getComputerID(),target=canidate.id}) + parallel.waitForAny(receive, connect) +end diff --git a/startup/01_repo.lua b/startup/01_repo.lua new file mode 100644 index 0000000..e9afa9f --- /dev/null +++ b/startup/01_repo.lua @@ -0,0 +1,3 @@ +local lib = {} + +_G.repo = lib \ No newline at end of file diff --git a/startup/99_phoneOS.lua b/startup/99_phoneOS.lua new file mode 100644 index 0000000..80fc958 --- /dev/null +++ b/startup/99_phoneOS.lua @@ -0,0 +1,85 @@ +local apps = {} +local containers = loadfile("/libs/containers.lua")() +local focusedapp = nil +_G.app = {} +function _G.app.focusapp(pid) + print("focusing "..pid) + if focusedapp ~= nil and focusedapp.win ~= nil then + focusedapp.win.setVisible(false) + end + focusedapp = apps[pid] + print(focusedapp) + if focusedapp ~= nil and focusedapp.win ~= nil then + focusedapp.win.setVisible(true) + end +end +function _G.app.launch(id) + local w,h = term.getSize() + if not fs.exists(fs.combine("/apps",id)) then return false,"App Not Found" end + local win = window.create(term.native(),1,2,w,h-2,false) + print("loading "..id) + local env = containers.getENV(fs.combine("/apps",id),true, win) + local appobj = {win=win,co=nil,id=id,title=""} + function env.setAppTitle(str) appobj.title=str end + print("loaded "..id) + appobj.co = coroutine.create(function () containers.start(env) end) + local pid = -1 + for k=1,#apps+1 do + if apps[k] == nil then + apps[k] = appobj + pid = k + end + end + app.focusapp(pid) +end + +local function render() + while true do + local w,h = term.getSize() + term.setCursorPos(1,1) + term.setBackgroundColor(colors.black) + term.clearLine() + term.write(os.date("%I:%M %p")) + local candidate = network.getCandidate() + local right = "id: "..tostring(candidate.id).." dist: "..tostring(math.floor(candidate.distance+0.5)).." m" + if candidate.id == -1 then + right = "disconnected" + end + term.setCursorPos(w-right:len()+1,1) + term.write(right) + sleep() + end +end + +local function process() + while true do + local event = {os.pullEvent()} + if focusedapp then + if focusedapp.event then + local success, content = coroutine.resume(focusedapp.co,table.unpack(focusedapp.event)) + if success then + focusedapp.event = nil + focusedapp.filter = content + else + app.focusapp(-1) + end + elseif focusedapp.filter == nil or focusedapp.filter == event[1] then + local success, content = coroutine.resume(focusedapp.co,table.unpack(event)) + if success then + focusedapp.filter = content + else + app.focusapp(-1) + end + end + end + for k,v in pairs(apps) do + if v.filter == nil or v.filter == event[1] and v.event == nil and not v == focusedapp then + v.event = event + end + end + end +end +print("launching test..") +app.launch("test") +print("launched") +parallel.waitForAny(process,render,network.run) \ No newline at end of file