cc-mc-server/mc/string_pack.lua
2024-07-14 14:23:30 +01:00

1 line
No EOL
13 KiB
Lua

local a=dofile"/rom/modules/main/cc/expect.lua".expect;local b={BIG_ENDIAN=1,LITTLE_ENDIAN=2}local c={b=1,B=1,h=1,H=1,l=1,L=1,j=1,J=1,T=1}local d={b=1,B=1,x=1,h=2,H=2,f=4,j=4,J=4,l=8,L=8,T=8,d=8,n=8}local function e(f)if f%1>=0.5 then return math.ceil(f)else return math.floor(f)end end;local function g(h)if h==0 then return 0 elseif h==-0 then return 0x80000000 elseif h==math.huge then return 0x7F800000 elseif h==-math.huge then return 0xFF800000 end;local i,j=math.frexp(h)if j>127 or j<-126 then error("number out of range",3)end;j,i=j+126,e((math.abs(i)-0.5)*0x1000000)if i>0x7FFFFF then j=j+1 end;return bit32.bor(h<0 and 0x80000000 or 0,bit32.lshift(bit32.band(j,0xFF),23),bit32.band(i,0x7FFFFF))end;local function k(h)if h==0 then return 0,0 elseif h==-0 then return 0x80000000,0 elseif h==math.huge then return 0x7FF00000,0 elseif h==-math.huge then return 0xFFF00000,0 end;local i,j=math.frexp(h)if j>1023 or j<-1022 then error("number out of range",3)end;j,i=j+1022,e((math.abs(i)-0.5)*0x20000000000000)if i>0xFFFFFFFFFFFFF then j=j+1 end;return bit32.bor(h<0 and 0x80000000 or 0,bit32.lshift(bit32.band(j,0x7FF),20),bit32.band(i/0x100000000,0xFFFFF)),bit32.band(i,0xFFFFFFFF)end;local function l(m)if m==0 then return 0 elseif m==0x80000000 then return-0 elseif m==0x7F800000 then return math.huge elseif m==0xFF800000 then return-math.huge end;local i,j=bit32.band(m,0x7FFFFF),bit32.band(bit32.rshift(m,23),0xFF)j,i=j-126,i/0x1000000+0.5;local f=math.ldexp(i,j)return bit32.btest(m,0x80000000)and-f or f end;local function n(o,p)if o==0 and p==0 then return 0 elseif o==0x80000000 and p==0 then return-0 elseif o==0x7FF00000 and p==0 then return math.huge elseif o==0xFFF00000 and p==0 then return-math.huge end;local i,j=bit32.band(o,0xFFFFF)*0x100000000+bit32.band(p,0xFFFFFFFF),bit32.band(bit32.rshift(o,20),0x7FF)j,i=j-1022,i/0x20000000000000+0.5;local f=math.ldexp(i,j)return bit32.btest(o,0x80000000)and-f or f end;local function q(r,s,t,u,v,w,x)local y=0;if u%math.min(s,v)~=0 and v>1 then local z=0;while u%math.min(s,v)~=0 and z<v do t[u]=0;u=u+1;y=y+1;z=z+1 end end;if w==b.BIG_ENDIAN then local A=0;if s>8 then for z=0,s-9 do t[u+z]=x and r>=2^(s*8-1)~=0 and 0xFF or 0;A=A+1;y=y+1 end end;for z=A,s-1 do t[u+z]=bit32.band(bit32.rshift(r,(s-z-1)*8),0xFF)y=y+1 end else for z=0,math.min(s,8)-1 do t[u+z]=r/2^(z*8)%256;y=y+1 end;for z=8,s-1 do t[u+z]=x and r>=2^(s*8-1)~=0 and 0xFF or 0;y=y+1 end end;return y end;local function B(C,u,s,w,v,x)local D,E=0,0;if u%math.min(s,v)~=0 and v>1 then for z=0,v-1 do if u%math.min(s,v)==0 then break end;u=u+1;E=E+1 end end;for z=0,s-1 do D=D+C:byte(u+z)*2^((w==b.BIG_ENDIAN and s-z-1 or z)*8)E=E+1 end;if x and D>=2^(s*8-1)then D=D-2^(s*8)end;return D,E end;local function F(G,v)local H=d[G]or 0;if v>1 and H%v~=0 then H=H+v-H%v end;return H end;local function I(...)local J=a(1,...,"string")local w=b.LITTLE_ENDIAN;local v=1;local K=1;local L=2;local t={}local z=1;while z<=#J do local M=J:sub(z,z)z=z+1;if M=='='or M=='<'then w=b.LITTLE_ENDIAN elseif M=='>'then w=b.BIG_ENDIAN elseif M=='!'then local s=-1;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=math.max(s,0)*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 or s==0 then error(string.format("integral size (%d) out of limits [1,16]",s),2)elseif s==-1 then v=4 else v=s end elseif c[M]then local r=a(L,select(L,...),"number")L=L+1;if r>=math.pow(2,F(M,0)*8-(M:match("%l")and 1 or 0))or r<(M:match("%l")and-math.pow(2,F(M,0)*8-1)or 0)then error(string.format("bad argument #%d to 'pack' (integer overflow)",L-1),2)end;K=K+q(r,F(M,0),t,K,v,w,false)elseif M:lower()=='i'then local x=M=='i'local s=-1;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=math.max(s,0)*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 or s==0 then error(string.format("integral size (%d) out of limits [1,16]",s),2)elseif v>1 and(s~=1 and s~=2 and s~=4 and s~=8 and s~=16)then error("bad argument #1 to 'pack' (format asks for alignment not power of 2)",2)elseif s==-1 then s=4 end;local r=a(L,select(L,...),"number")L=L+1;if r>=math.pow(2,s*8-(M:match("%l")and 1 or 0))or r<(M:match("%l")and-math.pow(2,s*8-1)or 0)then error(string.format("bad argument #%d to 'pack' (integer overflow)",L-1),2)end;K=K+q(r,s,t,K,v,w,x)elseif M=='f'then local h=a(L,select(L,...),"number")L=L+1;local m=g(h)if K%math.min(4,v)~=0 and v>1 then for N=0,v-1 do if K%math.min(4,v)==0 then break end;t[K]=0;K=K+1 end end;for N=0,3 do t[K+(w==b.BIG_ENDIAN and 3-N or N)]=bit32.band(bit32.rshift(m,N*8),0xFF)end;K=K+4 elseif M=='d'or M=='n'then local h=a(L,select(L,...),"number")L=L+1;local o,p=k(h)if K%math.min(8,v)~=0 and v>1 then for N=0,v-1 do if K%math.min(8,v)==0 then break end;t[K]=0;K=K+1 end end;for N=0,3 do t[K+(w==b.BIG_ENDIAN and 7-N or N)]=bit32.band(bit32.rshift(p,N*8),0xFF)end;for N=4,7 do t[K+(w==b.BIG_ENDIAN and 7-N or N)]=bit32.band(bit32.rshift(o,(N-4)*8),0xFF)end;K=K+8 elseif M=='c'then local s=0;if z>#J or not J:sub(z,z):match("%d")then error("missing size for format option 'c'",2)end;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if K+s<K or K+s>0xFFFFFFFF then error("bad argument #1 to 'pack' (format result too large)",2)end;local C=a(L,select(L,...),"string")L=L+1;if#C>s then error(string.format("bad argument #%d to 'pack' (string longer than given size)",L-1),2)end;if s>0 then for N=0,s-1 do t[K+N]=C:byte(N+1)or 0 end;K=K+s end elseif M=='z'then local C=a(L,select(L,...),"string")L=L+1;for O in C:gmatch"."do if O=='\0'then error(string.format("bad argument #%d to 'pack' (string contains zeros)",L-1),2)end end;for N=0,#C-1 do t[K+N]=C:byte(N+1)end;t[K+#C]=0;K=K+#C+1 elseif M=='s'then local s=0;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 then error(string.format("integral size (%d) out of limits [1,16]",s),2)elseif s==0 then s=4 end;local C=a(L,select(L,...),"string")L=L+1;if#C>=math.pow(2,s*8)then error(string.format("bad argument #%d to 'pack' (string length does not fit in given size)",L-1),2)end;q(#C,s,t,K,1,w,false)for N=s,#C+s-1 do t[K+N]=C:byte(N-s+1)or 0 end;K=K+#C+s elseif M=='x'then t[K]=0;K=K+1 elseif M=='X'then if z>=#J then error("invalid next option for option 'X'",2)end;local s=0;local M=J:sub(z,z)z=z+1;if M:lower()=='i'then while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 or s==0 then error(string.format("integral size (%d) out of limits [1,16]",s),2)end else s=F(M,0)end;if s<1 then error("invalid next option for option 'X'",2)end;if K%math.min(s,v)~=0 and v>1 then for N=1,v do if K%math.min(s,v)==0 then break end;t[K]=0;K=K+1 end end elseif M~=' 'then error(string.format("invalid format option '%s'",M),2)end end;return string.char(table.unpack(t))end;local function P(J)local K=0;local v=1;local z=1;while z<=#J do local M=J:sub(z,z)z=z+1;if M=='!'then local s=0;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 then error(string.format("integral size (%d) out of limits [1,16]",s),2)elseif s==0 then v=4 else v=s end elseif c[M]then local s=F(M,0)if K%math.min(s,v)~=0 and v>1 then for N=1,v do if K%math.min(s,v)==0 then break end;K=K+1 end end;K=K+s elseif M:lower()=='i'then local s=0;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 then error(string.format("integral size (%d) out of limits [1,16]",s))elseif v>1 and(s~=1 and s~=2 and s~=4 and s~=8 and s~=16)then error("bad argument #1 to 'pack' (format asks for alignment not power of 2)",2)elseif s==0 then s=4 end;if K%math.min(s,v)~=0 and v>1 then for N=1,v do if K%math.min(s,v)==0 then break end;K=K+1 end end;K=K+s elseif M=='f'then if K%math.min(4,v)~=0 and v>1 then for N=1,v do if K%math.min(4,v)==0 then break end;K=K+1 end end;K=K+4 elseif M=='d'or M=='n'then if K%math.min(8,v)~=0 and v>1 then for N=1,v do if K%math.min(8,v)==0 then break end;K=K+1 end end;K=K+8 elseif M=='c'then local s=0;if z>#J or not J:sub(z,z):match("%d")then error("missing size for format option 'c'",2)end;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if K+s<K or K+s>0x7FFFFFFF then error("bad argument #1 to 'packsize' (format result too large)",2)end;K=K+s elseif M=='x'then K=K+1 elseif M=='X'then if z>=#J then error("invalid next option for option 'X'",2)end;local s=0;local M=J:sub(z,z)z=z+1;if M:lower()=='i'then while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 or s==0 then error(string.format("integral size (%d) out of limits [1,16]",s),2)end else s=F(M,0)end;if s<1 then error("invalid next option for option 'X'",2)end;if K%math.min(s,v)~=0 and v>1 then for N=1,v do if K%math.min(s,v)==0 then break end;K=K+1 end end elseif M=='s'or M=='z'then error("bad argument #1 to 'packsize' (variable-length format)",2)elseif M~=' 'and M~='<'and M~='>'and M~='='then error(string.format("invalid format option '%s'",M),2)end end;return K end;local function Q(J,C,K)a(1,J,"string")a(2,C,"string")a(3,K,"number","nil")if K then if K<0 then K=#C+K elseif K==0 then error("bad argument #3 to 'unpack' (initial position out of string)",2)end;if K>#C or K<0 then error("bad argument #3 to 'unpack' (initial position out of string)",2)end else K=1 end;local w=b.LITTLE_ENDIAN;local v=1;local H={}local z=1;while z<=#J do local M=J:sub(z,z)z=z+1;if M=='<'or M=='='then w=b.LITTLE_ENDIAN elseif M=='>'then w=b.BIG_ENDIAN elseif M=='!'then local s=0;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 then error(string.format("integral size (%d) out of limits [1,16]",s))elseif s==0 then v=4 else v=s end elseif c[M]then if K+F(M,0)>#C+1 then error("data string too short",2)end;local R,S=B(C,K,F(M,0),w,v,M:match("%l")~=nil)H[#H+1]=R;K=K+S elseif M:lower()=='i'then local x=M=='i'local s=0;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 then error(string.format("integral size (%d) out of limits [1,16]",s),2)elseif s>8 then error(string.format("%d-byte integer does not fit into Lua Integer",s),2)elseif s==0 then s=4 end;if K+s>#C+1 then error("data string too short",2)end;local R,S=B(C,K,s,w,v,x)H[#H+1]=R;K=K+S elseif M=='f'then if K%math.min(4,v)~=0 and v>1 then for N=1,v do if K%math.min(4,v)==0 then break end;K=K+1 end end;if K+4>#C+1 then error("data string too short",2)end;local R=B(C,K,4,w,v,false)H[#H+1]=l(R)K=K+4 elseif M=='d'or M=='n'then if K%math.min(8,v)~=0 and v>1 then for N=1,v do if K%math.min(8,v)==0 then break end;K=K+1 end end;if K+8>#C+1 then error("data string too short",2)end;local o,p=0,0;for N=0,3 do o=bit32.bor(o,bit32.lshift(C:byte(K+N),(w==b.BIG_ENDIAN and 3-N or N)*8))end;for N=0,3 do p=bit32.bor(p,bit32.lshift(C:byte(K+N+4),(w==b.BIG_ENDIAN and 3-N or N)*8))end;if w==b.LITTLE_ENDIAN then o,p=p,o end;H[#H+1]=n(o,p)K=K+8 elseif M=='c'then local s=0;if z>#J or not J:sub(z,z):match("%d")then error("missing size for format option 'c'",2)end;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)")end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if K+s>#C+1 then error("data string too short",2)end;H[#H+1]=C:sub(K,K+s-1)K=K+s elseif M=='z'then local s=0;while C:byte(K+s)~=0 do s=s+1;if K+s>#C then error("unfinished string for format 'z'",2)end end;H[#H+1]=C:sub(K,K+s-1)K=K+s+1 elseif M=='s'then local s=0;while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 then error(string.format("integral size (%d) out of limits [1,16]",s),2)elseif s==0 then s=4 end;if K+s>#C+1 then error("data string too short",2)end;local r,T=B(C,K,s,w,v,false)K=K+T;if K+r>#C+1 then error("data string too short",2)end;H[#H+1]=C:sub(K,K+r-1)K=K+r elseif M=='x'then K=K+1 elseif M=='X'then if z>=#J then error("invalid next option for option 'X'",2)end;local s=0;local M=J:sub(z,z)z=z+1;if M:lower()=='i'then while z<=#J and J:sub(z,z):match("%d")do if s>=0xFFFFFFFF/10 then error("bad argument #1 to 'pack' (invalid format)",2)end;s=s*10+tonumber(J:sub(z,z))z=z+1 end;if s>16 or s==0 then error(string.format("integral size (%d) out of limits [1,16]",s),2)elseif s==-1 then s=4 end else s=F(M,0)end;if s<1 then error("invalid next option for option 'X'",2)end;if K%math.min(s,v)~=0 and v>1 then for N=1,v do if K%math.min(s,v)==0 then break end;K=K+1 end end elseif M~=' 'then error(string.format("invalid format option '%s'",M),2)end end;H[#H+1]=K;return table.unpack(H)end;return{pack=I,packsize=P,unpack=Q}