diff --git a/README.md b/README.md index 914f63d..f3094c1 100644 --- a/README.md +++ b/README.md @@ -1 +1,3 @@ -# Custom-Music-Discs-Minecraft-Datapack \ No newline at end of file +# Custom-Music-Discs-Minecraft-Datapack + +To use create a folder called: `music` diff --git a/bin/bin/compressors/lzw.lua b/bin/bin/compressors/lzw.lua new file mode 100644 index 0000000..57653b3 --- /dev/null +++ b/bin/bin/compressors/lzw.lua @@ -0,0 +1,73 @@ +--[[ +LZW String Compression demo for Gideros +This code is MIT licensed, see http://www.opensource.org/licenses/mit-license.php +(C) 2013 - Guava7 +]] +CLZWCompression = {} +function CLZWCompression:InitDictionary(isEncode) + self.mDictionary = {} + -- local s = " !#$%&'\"()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" + local s={} + for i=1,255 do + s[#s+1]=string.char(i) + end + s=table.concat(s) + local len = #s + for i = 1, len do + if isEncode then + self.mDictionary[s:sub(i, i)] = i + else + self.mDictionary[i] = s:sub(i, i) + end + end + self.mDictionaryLen = len +end +function CLZWCompression:Encode(sInput) + self:InitDictionary(true) + local s = "" + local ch + local len = #sInput + local result = {} + local dic = self.mDictionary + local temp + for i = 1, len do + ch = sInput:sub(i, i) + temp = s..ch + if dic[temp] then + s = temp + else + result[#result + 1] = dic[s] + self.mDictionaryLen = self.mDictionaryLen + 1 + dic[temp] = self.mDictionaryLen + s = ch + end + end + result[#result + 1] = dic[s] + return result +end +function CLZWCompression:Decode(data) + self:InitDictionary(false) + local dic = self.mDictionary + local entry + local ch + local prevCode, currCode + local result = {} + prevCode = data[1] + result[#result + 1] = dic[prevCode] + for i = 2, #data do + currCode = data[i] + entry = dic[currCode] + if entry then + ch = entry:sub(1, 1) + result[#result + 1] = entry + else + ch = dic[prevCode]:sub(1, 1) + result[#result + 1] = dic[prevCode]..ch + end + dic[#dic + 1] = dic[prevCode]..ch + prevCode = currCode + end + return table.concat(result) +end + +return CLZWCompression diff --git a/bin/bin/converters/base64.lua b/bin/bin/converters/base64.lua new file mode 100644 index 0000000..d66cc90 --- /dev/null +++ b/bin/bin/converters/base64.lua @@ -0,0 +1,40 @@ +local bin = require("bin") +local base64={} +local bs = { [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 bsd=table.flip(bs) +local char=string.char +function base64.encode(s) + local byte, rep, pad = string.byte, string.rep, 2 - ((#s-1) % 3) + s = (s..rep('\0', pad)):gsub("...", function(cs) + local a, b, c = byte(cs, 1, 3) + return bs[bit.rshift(a,2)] .. bs[bit.bor(bit.lshift(bit.band(a,3),4),bit.rshift(b,4))] .. bs[bit.bor(bit.lshift(bit.band(b,15),2),bit.rshift(c,6))] .. bs[bit.band(c,63)] + end) + return s:sub(1, #s-pad) .. rep('=', pad) +end +function base64.decode(s) + local s=s:match("["..s.."=]+") + local p,cc=s:gsub("=","A") + local r="" + local n=0 + s=s:sub(1,#s-#p)..p + for c = 1,#s,4 do + n = bit.lshift(bsd[s:sub(c, c)], 18) + bit.lshift(bsd[s:sub(c+1, c+1)], 12) + bit.lshift(bsd[s:sub(c + 2, c + 2)], 6) + bsd[s:sub(c + 3, c + 3)] + r = r .. char(bit.band(bit.arshift(n, 16), 0xFF)) .. char(bit.band(bit.arshift(n, 8), 0xFF)) .. char(bit.band(n, 0xFF)) + end + return r:sub(1,-(cc+1)) +end +function bin.newFromBase91(data) + return bin.new(bin.fromBase91(data)) +end +function bin.toBase91(s) + return base91.encode(s) +end +function bin.fromBase91(s) + return base91.decode(s) +end +return base64 diff --git a/bin/bin/converters/base91.lua b/bin/bin/converters/base91.lua new file mode 100644 index 0000000..cb2f447 --- /dev/null +++ b/bin/bin/converters/base91.lua @@ -0,0 +1,82 @@ +local bin = require("bin") +local base91={} +local b91enc={[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 b91dec=table.flip(b91enc) +function base91.decode(d) + local l,v,o,b,n = #d,-1,"",0,0 + for i in d:gmatch(".") do + local c=b91dec[i] + if not(c) then + -- Continue + else + if v < 0 then + v = c + else + v = v+c*91 + b = bit.bor(b, bit.lshift(v,n)) + if bit.band(v,8191) then + n = n + 13 + else + n = n + 14 + end + while true do + o=o..string.char(bit.band(b,255)) + b=bit.rshift(b,8) + n=n-8 + if not (n>7) then + break + end + end + v=-1 + end + end + end + if v + 1>0 then + o=o..string.char(bit.band(bit.bor(b,bit.lshift(v,n)),255)) + end + return o +end +function base91.encode(d) + local b,n,o,l=0,0,"",#d + for i in d:gmatch(".") do + b=bit.bor(b,bit.lshift(string.byte(i),n)) + n=n+8 + if n>13 then + v=bit.band(b,8191) + if v>88 then + b=bit.rshift(b,13) + n=n-13 + else + v=bit.band(b,16383) + b=bit.rshift(b,14) + n=n-14 + end + o=o..b91enc[v % 91] .. b91enc[math.floor(v / 91)] + end + end + if n>0 then + o=o..b91enc[b % 91] + if n>7 or b>90 then + o=o .. b91enc[math.floor(b / 91)] + end + end + return o +end +function bin.newFromBase64(data) + return bin.new(bin.fromBase64(data)) +end +function bin.toBase64(s) + return base64.encode(s) +end +function bin.fromBase64(s) + return base64.decode(s) +end +return base91 diff --git a/bin/bin/hashes/md5.lua b/bin/bin/hashes/md5.lua new file mode 100644 index 0000000..c2cc716 --- /dev/null +++ b/bin/bin/hashes/md5.lua @@ -0,0 +1,394 @@ +local bin = require("bin") +local md5 = { + _VERSION = "md5.lua 1.1.0", + _DESCRIPTION = "MD5 computation in Lua (5.1-3, LuaJIT)", + _URL = "https://github.com/kikito/md5.lua", + _LICENSE = [[ + MIT LICENSE + + Copyright (c) 2013 Enrique García Cota + Adam Baldwin + hanzao + Equi 4 Software + ]] +} + +-- bit lib implementions + +local char, byte, format, rep, sub = + string.char, string.byte, string.format, string.rep, string.sub +local bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift + +local ok, bit = pcall(require, 'bit') +if ok then + bit_or, bit_and, bit_not, bit_xor, bit_rshift, bit_lshift = bit.bor, bit.band, bit.bnot, bit.bxor, bit.rshift, bit.lshift +else + ok, bit = pcall(require, 'bit32') + + if ok then + + bit_not = bit.bnot + + local tobit = function(n) + return n <= 0x7fffffff and n or -(bit_not(n) + 1) + end + + local normalize = function(f) + return function(a,b) return tobit(f(tobit(a), tobit(b))) end + end + + bit_or, bit_and, bit_xor = normalize(bit.bor), normalize(bit.band), normalize(bit.bxor) + bit_rshift, bit_lshift = normalize(bit.rshift), normalize(bit.lshift) + + else + + local function tbl2number(tbl) + local result = 0 + local power = 1 + for i = 1, #tbl do + result = result + tbl[i] * power + power = power * 2 + end + return result + end + + local function expand(t1, t2) + local big, small = t1, t2 + if(#big < #small) then + big, small = small, big + end + -- expand small + for i = #small + 1, #big do + small[i] = 0 + end + end + + local to_bits -- needs to be declared before bit_not + + bit_not = function(n) + local tbl = to_bits(n) + local size = math.max(#tbl, 32) + for i = 1, size do + if(tbl[i] == 1) then + tbl[i] = 0 + else + tbl[i] = 1 + end + end + return tbl2number(tbl) + end + + -- defined as local above + to_bits = function (n) + if(n < 0) then + -- negative + return to_bits(bit_not(math.abs(n)) + 1) + end + -- to bits table + local tbl = {} + local cnt = 1 + local last + while n > 0 do + last = n % 2 + tbl[cnt] = last + n = (n-last)/2 + cnt = cnt + 1 + end + + return tbl + end + + bit_or = function(m, n) + local tbl_m = to_bits(m) + local tbl_n = to_bits(n) + expand(tbl_m, tbl_n) + + local tbl = {} + for i = 1, #tbl_m do + if(tbl_m[i]== 0 and tbl_n[i] == 0) then + tbl[i] = 0 + else + tbl[i] = 1 + end + end + + return tbl2number(tbl) + end + + bit_and = function(m, n) + local tbl_m = to_bits(m) + local tbl_n = to_bits(n) + expand(tbl_m, tbl_n) + + local tbl = {} + for i = 1, #tbl_m do + if(tbl_m[i]== 0 or tbl_n[i] == 0) then + tbl[i] = 0 + else + tbl[i] = 1 + end + end + + return tbl2number(tbl) + end + + bit_xor = function(m, n) + local tbl_m = to_bits(m) + local tbl_n = to_bits(n) + expand(tbl_m, tbl_n) + + local tbl = {} + for i = 1, #tbl_m do + if(tbl_m[i] ~= tbl_n[i]) then + tbl[i] = 1 + else + tbl[i] = 0 + end + end + + return tbl2number(tbl) + end + + bit_rshift = function(n, bits) + local high_bit = 0 + if(n < 0) then + -- negative + n = bit_not(math.abs(n)) + 1 + high_bit = 0x80000000 + end + + local floor = math.floor + + for i=1, bits do + n = n/2 + n = bit_or(floor(n), high_bit) + end + return floor(n) + end + + bit_lshift = function(n, bits) + if(n < 0) then + -- negative + n = bit_not(math.abs(n)) + 1 + end + + for i=1, bits do + n = n*2 + end + return bit_and(n, 0xFFFFFFFF) + end + end +end + +-- convert little-endian 32-bit int to a 4-char string +local function lei2str(i) + local f=function (s) return char( bit_and( bit_rshift(i, s), 255)) end + return f(0)..f(8)..f(16)..f(24) +end + +-- convert raw string to big-endian int +local function str2bei(s) + local v=0 + for i=1, #s do + v = v * 256 + byte(s, i) + end + return v +end + +-- convert raw string to little-endian int +local function str2lei(s) + local v=0 + for i = #s,1,-1 do + v = v*256 + byte(s, i) + end + return v +end + +-- cut up a string in little-endian ints of given size +local function cut_le_str(s,...) + local o, r = 1, {} + local args = {...} + for i=1, #args do + table.insert(r, str2lei(sub(s, o, o + args[i] - 1))) + o = o + args[i] + end + return r +end + +local swap = function (w) return str2bei(lei2str(w)) end + +-- An MD5 mplementation in Lua, requires bitlib (hacked to use LuaBit from above, ugh) +-- 10/02/2001 jcw@equi4.com + +local CONSTS = { + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, + 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476 +} + +local f=function (x,y,z) return bit_or(bit_and(x,y),bit_and(-x-1,z)) end +local g=function (x,y,z) return bit_or(bit_and(x,z),bit_and(y,-z-1)) end +local h=function (x,y,z) return bit_xor(x,bit_xor(y,z)) end +local i=function (x,y,z) return bit_xor(y,bit_or(x,-z-1)) end +local z=function (ff,a,b,c,d,x,s,ac) + a=bit_and(a+ff(b,c,d)+x+ac,0xFFFFFFFF) + -- be *very* careful that left shift does not cause rounding! + return bit_or(bit_lshift(bit_and(a,bit_rshift(0xFFFFFFFF,s)),s),bit_rshift(a,32-s))+b +end + +local function transform(A,B,C,D,X) + local a,b,c,d=A,B,C,D + local t=CONSTS + + a=z(f,a,b,c,d,X[ 0], 7,t[ 1]) + d=z(f,d,a,b,c,X[ 1],12,t[ 2]) + c=z(f,c,d,a,b,X[ 2],17,t[ 3]) + b=z(f,b,c,d,a,X[ 3],22,t[ 4]) + a=z(f,a,b,c,d,X[ 4], 7,t[ 5]) + d=z(f,d,a,b,c,X[ 5],12,t[ 6]) + c=z(f,c,d,a,b,X[ 6],17,t[ 7]) + b=z(f,b,c,d,a,X[ 7],22,t[ 8]) + a=z(f,a,b,c,d,X[ 8], 7,t[ 9]) + d=z(f,d,a,b,c,X[ 9],12,t[10]) + c=z(f,c,d,a,b,X[10],17,t[11]) + b=z(f,b,c,d,a,X[11],22,t[12]) + a=z(f,a,b,c,d,X[12], 7,t[13]) + d=z(f,d,a,b,c,X[13],12,t[14]) + c=z(f,c,d,a,b,X[14],17,t[15]) + b=z(f,b,c,d,a,X[15],22,t[16]) + + a=z(g,a,b,c,d,X[ 1], 5,t[17]) + d=z(g,d,a,b,c,X[ 6], 9,t[18]) + c=z(g,c,d,a,b,X[11],14,t[19]) + b=z(g,b,c,d,a,X[ 0],20,t[20]) + a=z(g,a,b,c,d,X[ 5], 5,t[21]) + d=z(g,d,a,b,c,X[10], 9,t[22]) + c=z(g,c,d,a,b,X[15],14,t[23]) + b=z(g,b,c,d,a,X[ 4],20,t[24]) + a=z(g,a,b,c,d,X[ 9], 5,t[25]) + d=z(g,d,a,b,c,X[14], 9,t[26]) + c=z(g,c,d,a,b,X[ 3],14,t[27]) + b=z(g,b,c,d,a,X[ 8],20,t[28]) + a=z(g,a,b,c,d,X[13], 5,t[29]) + d=z(g,d,a,b,c,X[ 2], 9,t[30]) + c=z(g,c,d,a,b,X[ 7],14,t[31]) + b=z(g,b,c,d,a,X[12],20,t[32]) + + a=z(h,a,b,c,d,X[ 5], 4,t[33]) + d=z(h,d,a,b,c,X[ 8],11,t[34]) + c=z(h,c,d,a,b,X[11],16,t[35]) + b=z(h,b,c,d,a,X[14],23,t[36]) + a=z(h,a,b,c,d,X[ 1], 4,t[37]) + d=z(h,d,a,b,c,X[ 4],11,t[38]) + c=z(h,c,d,a,b,X[ 7],16,t[39]) + b=z(h,b,c,d,a,X[10],23,t[40]) + a=z(h,a,b,c,d,X[13], 4,t[41]) + d=z(h,d,a,b,c,X[ 0],11,t[42]) + c=z(h,c,d,a,b,X[ 3],16,t[43]) + b=z(h,b,c,d,a,X[ 6],23,t[44]) + a=z(h,a,b,c,d,X[ 9], 4,t[45]) + d=z(h,d,a,b,c,X[12],11,t[46]) + c=z(h,c,d,a,b,X[15],16,t[47]) + b=z(h,b,c,d,a,X[ 2],23,t[48]) + + a=z(i,a,b,c,d,X[ 0], 6,t[49]) + d=z(i,d,a,b,c,X[ 7],10,t[50]) + c=z(i,c,d,a,b,X[14],15,t[51]) + b=z(i,b,c,d,a,X[ 5],21,t[52]) + a=z(i,a,b,c,d,X[12], 6,t[53]) + d=z(i,d,a,b,c,X[ 3],10,t[54]) + c=z(i,c,d,a,b,X[10],15,t[55]) + b=z(i,b,c,d,a,X[ 1],21,t[56]) + a=z(i,a,b,c,d,X[ 8], 6,t[57]) + d=z(i,d,a,b,c,X[15],10,t[58]) + c=z(i,c,d,a,b,X[ 6],15,t[59]) + b=z(i,b,c,d,a,X[13],21,t[60]) + a=z(i,a,b,c,d,X[ 4], 6,t[61]) + d=z(i,d,a,b,c,X[11],10,t[62]) + c=z(i,c,d,a,b,X[ 2],15,t[63]) + b=z(i,b,c,d,a,X[ 9],21,t[64]) + + return bit_and(A+a,0xFFFFFFFF),bit_and(B+b,0xFFFFFFFF), + bit_and(C+c,0xFFFFFFFF),bit_and(D+d,0xFFFFFFFF) +end + +---------------------------------------------------------------- + +local function md5_update(self, s) + self.pos = self.pos + #s + s = self.buf .. s + for ii = 1, #s - 63, 64 do + local X = cut_le_str(sub(s,ii,ii+63),4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4) + assert(#X == 16) + X[0] = table.remove(X,1) -- zero based! + self.a,self.b,self.c,self.d = transform(self.a,self.b,self.c,self.d,X) + end + self.buf = sub(s, math.floor(#s/64)*64 + 1, #s) + return self +end + +local function md5_finish(self) + local msgLen = self.pos + local padLen = 56 - msgLen % 64 + + if msgLen % 64 > 56 then padLen = padLen + 64 end + + if padLen == 0 then padLen = 64 end + + local s = char(128) .. rep(char(0),padLen-1) .. lei2str(bit_and(8*msgLen, 0xFFFFFFFF)) .. lei2str(math.floor(msgLen/0x20000000)) + md5_update(self, s) + + assert(self.pos % 64 == 0) + return lei2str(self.a) .. lei2str(self.b) .. lei2str(self.c) .. lei2str(self.d) +end + +---------------------------------------------------------------- + +function md5.new() + return { a = CONSTS[65], b = CONSTS[66], c = CONSTS[67], d = CONSTS[68], + pos = 0, + buf = '', + update = md5_update, + finish = md5_finish } +end + +function md5.tohex(s) + return format("%08x%08x%08x%08x", str2bei(sub(s, 1, 4)), str2bei(sub(s, 5, 8)), str2bei(sub(s, 9, 12)), str2bei(sub(s, 13, 16))) +end + +function md5.sum(s) + return md5.new():update(s):finish() +end + +function md5.sumhexa(s) + return md5.tohex(md5.sum(s)) +end +bin.md5 = md5 +function bin:getMD5Hash() + self:setSeek(1) + local len=self:getSize() + local md5=bin.md5.new() + local SIZE=2048 + if len>SIZE then + local dat=self:read(SIZE) + while dat~=nil do + md5:update(dat) + dat=self:read(SIZE) + end + return bin.md5.tohex(md5:finish()):upper() + else + return bin.md5.sumhexa(self:getData()):upper() + end +end +return md5 diff --git a/bin/bin/init.lua b/bin/bin/init.lua new file mode 100644 index 0000000..6962134 --- /dev/null +++ b/bin/bin/init.lua @@ -0,0 +1,1434 @@ +bin={} +bin.Version={6,0,0} +bin.stage='stable' +bin.data='' +bin.t='bin' +bin.__index = bin +bin.__tostring=function(self) return self:getData() end +bin.__len=function(self) return self:getlength() end +bin.lastBlockSize=0 +bin.streams={} +-- Helpers +function bin.getVersion() + return bin.Version[1]..'.'..bin.Version[2]..'.'..bin.Version[3] +end +function table.print(tbl, indent) + if not indent then indent = 0 end + for k, v in pairs(tbl) do + formatting = string.rep(" ", indent) .. k .. ": " + if type(v) == "table" then + print(formatting) + table.print(v, indent+1) + elseif type(v) == 'boolean' then + print(formatting .. tostring(v)) + else + print(formatting .. tostring(v)) + end + end +end +function table.flip(t) + local tt={} + for i,v in pairs(t) do + tt[v]=i + end + return tt +end +function toFraction(n) + local w,p=math.modf(n) + if p~=0 then + p=tonumber(tostring(p):sub(3)) + end + return w,p +end +function io.cleanName(name) + name=name:gsub("\\","") + name=name:gsub("/","") + name=name:gsub(":","") + name=name:gsub("*","") + name=name:gsub("%?","") + name=name:gsub("\"","''") + name=name:gsub("<","") + name=name:gsub(">","") + name=name:gsub("|","") + return name +end +function math.numfix(n,x) + local str=tostring(n) + if #str1 or #d<1 then + error("A byte must be one character!") + else + c.data=string.byte(d) + end + elseif type(d)=="number" then + if d>255 or d<0 then + error("A byte must be between 0 and 255!") + else + c.data=d + end + else + error("cannot use type "..type(d).." as an argument! Takes only strings or numbers!") + end + c.__index=function(self,k) + if k>=0 and k<9 then + if self.data==0 then + return 0 + elseif self.data==255 then + return 1 + else + return bits.ref[self.data][k] + end + end + end + c.__tostring=function(self) + return bits.ref[tostring(self.data)] + end + setmetatable(c,c) + return c +end +function bits.newByteArray(s) + local c={} + if type(s)~="string" then + error("Must be a string type or bin/buffer type") + elseif type(s)=="table" then + if s.t=="sink" or s.t=="buffer" or s.t=="bin" then + local data=s:getData() + for i=1,#data do + c[#c+1]=bits.newByte(data:sub(i,i)) + end + else + error("Must be a string type or bin/buffer type") + end + else + for i=1,#s do + c[#c+1]=bits.newByte(s:sub(i,i)) + end + end + return c +end +function bits.new(n,binary) + local temp={} + temp.t="bits" + temp.Type="bits" + if type(n)=="string" then + if binary then + temp.data=n:match("[10]+") + else + local t={} + for i=#n,1,-1 do + table.insert(t,bits:conv(string.byte(n,i))) + end + temp.data=table.concat(t) + end + elseif type(n)=="number" or type(n)=="table" then + temp.data=basen(n,2) + end + if #temp.data%8~=0 then + temp.data=string.rep('0',8-#temp.data%8)..temp.data + end + setmetatable(temp, bits) + return temp +end +for i=0,255 do + local d=bits.new(i).data + bits.ref[i]={d:match("(%d)(%d)(%d)(%d)(%d)(%d)(%d)(%d)")} + bits.ref[tostring(i)]=d + bits.ref[d]=i + bits.ref["\255"..string.char(i)]=d +end +function bits.numToBytes(n,fit,func) + local num=string.reverse(bits.new(n):toSbytes()) + local ref={["num"]=num,["fit"]=fit} + if fit then + if fit<#num then + if func then + print("Warning: attempting to store a number that takes up more space than allotted! Using provided method!") + func(ref) + else + print("Warning: attempting to store a number that takes up more space than allotted!") + end + return ref.num:sub(1,ref.fit) + elseif fit==#num then + return string.reverse(num) + else + return string.reverse(string.rep("\0",fit-#num)..num) + end + else + return string.reverse(num) + end +end +function bits:conv(n) + local tab={} + while n>=1 do + table.insert(tab,n%2) + n=math.floor(n/2) + end + local str=string.reverse(table.concat(tab)) + if #str%8~=0 or #str==0 then + str=string.rep('0',8-#str%8)..str + end + return str +end +function bits:tonumber(s,e) + if s==0 then + return tonumber(self.data,2) + end + s=s or 1 + return tonumber(string.sub(self.data,(8*(s-1))+1,8*s),2) or error('Bounds!') +end +function bits:isover() + return #self.data>8 +end +function bits:flipbits() + tab={} + for i=1,#self.data do + if string.sub(self.data,i,i)=='1' then + table.insert(tab,'0') + else + table.insert(tab,'1') + end + end + self.data=table.concat(tab) +end +function bits:tobytes() + local tab={} + for i=self:getbytes(),1,-1 do + table.insert(tab,string.char(self:tonumber(i))) + end + return bin.new(table.concat(tab)) +end +function bits:toSbytes() + local tab={} + for i=self:getbytes(),1,-1 do + table.insert(tab,string.char(self:tonumber(i))) + end + return table.concat(tab) +end +function bits:getBin() + return self.data +end +function bits:getbytes() + return #self.data/8 +end +local binNum=require("bin.numbers.BigNum") +local infinabits={} +bin.infinabits = infinabits +infinabits.data='' +infinabits.t='infinabits' +infinabits.Type='infinabits' +infinabits.__index = infinabits +infinabits.__tostring=function(self) return self.data end +infinabits.__len=function(self) return (#self.data)/8 end +local floor,insert = math.floor, table.insert +function basen(n,b) + n=BigNum.new(n) + if not b or b == 10 then return tostring(n) end + local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + local t = {} + local sign = "" + if n < BigNum.new(0) then + sign = "-" + n = -n + end + repeat + local d = tonumber(tostring(n % b)) + 1 + n = n / b + insert(t, 1, digits:sub(d,d)) + until n == BigNum.new(0) + return sign .. table.concat(t,"") +end +function base2to10(num) + local n=BigNum.new(0) + for i = #num-1,0,-1 do + nn=BigNum.new(num:sub(i+1,i+1))*(BigNum.new(2)^((#num-i)-1)) + n=n+nn + end + return n +end +function infinabits.newBitBuffer(n) + -- WIP +end +function infinabits.newConverter(bitsIn,bitsOut) + local c={} + -- WIP +end +infinabits.ref={} +function infinabits.newByte(d)-- WIP + local c={} + if type(d)=="string" then + if #d>1 or #d<1 then + error("A byte must be one character!") + else + c.data=string.byte(d) + end + elseif type(d)=="number" then + if d>255 or d<0 then + error("A byte must be between 0 and 255!") + else + c.data=d + end + else + error("cannot use type "..type(d).." as an argument! Takes only strings or numbers!") + end + c.__index=function(self,k) + if k>=0 and k<9 then + if self.data==0 then + return 0 + elseif self.data==255 then + return 1 + else + return infinabits.ref[self.data][k] + end + end + end + c.__tostring=function(self) + return infinabits.ref[tostring(self.data)] + end + setmetatable(c,c) + return c +end +function infinabits.newByteArray(s)-- WIP + local c={} + if type(s)~="string" then + error("Must be a string type or bin/buffer type") + elseif type(s)=="table" then + if s.t=="sink" or s.t=="buffer" or s.t=="bin" then + local data=s:getData() + for i=1,#data do + c[#c+1]=infinabits.newByte(data:sub(i,i)) + end + else + error("Must be a string type or bin/buffer type") + end + else + for i=1,#s do + c[#c+1]=infinabits.newByte(s:sub(i,i)) + end + end + return c +end +function infinabits.new(n,binary) + local temp={} + temp.t="infinabits" + temp.Type="infinabits" + if type(n)=="string" then + if binary then + temp.data=n:match("[10]+") + else + local t={} + for i=#n,1,-1 do + table.insert(t,infinabits:conv(string.byte(n,i))) + end + temp.data=table.concat(t) + end + elseif type(n)=="number" or type(n)=="table" then + temp.data=basen(tostring(n),2) + end + if #temp.data%8~=0 then + temp.data=string.rep('0',8-#temp.data%8)..temp.data + end + setmetatable(temp, infinabits) + return temp +end +for i=0,255 do + local d=infinabits.new(i).data + infinabits.ref[i]={d:match("(%d)(%d)(%d)(%d)(%d)(%d)(%d)(%d)")} + infinabits.ref[tostring(i)]=d + infinabits.ref[d]=i + infinabits.ref["\255"..string.char(i)]=d +end +function infinabits.numToBytes(n,fit,func) + local num=string.reverse(infinabits.new(BigNum.new(n)):toSbytes()) + local ref={["num"]=num,["fit"]=fit} + if fit then + if fit<#num then + if func then + print("Warning: attempting to store a number that takes up more space than allotted! Using provided method!") + func(ref) + else + print("Warning: attempting to store a number that takes up more space than allotted!") + end + return ref.num:sub(1,ref.fit) + elseif fit==#num then + return string.reverse(num) + else + return string.reverse(string.rep("\0",fit-#num)..num) + end + else + return string.reverse(num) + end +end +function infinabits.numToBytes(n,fit,fmt,func) + if fmt=="%e" then + local num=string.reverse(infinabits.new(BigNum.new(n)):toSbytes()) + local ref={["num"]=num,["fit"]=fit} + if fit then + if fit<#num then + if func then + print("Warning: attempting to store a number that takes up more space than allotted! Using provided method!") + func(ref) + else + print("Warning: attempting to store a number that takes up more space than allotted!") + end + return ref.num:sub(1,ref.fit) + elseif fit==#num then + return num + else + return string.rep("\0",fit-#num)..num + end + else + return num + end + + else + local num=string.reverse(infinabits.new(BigNum.new(n)):toSbytes()) + local ref={["num"]=num,["fit"]=fit} + if fit then + if fit<#num then + if func then + print("Warning: attempting to store a number that takes up more space than allotted! Using provided method!") + func(ref) + else + print("Warning: attempting to store a number that takes up more space than allotted!") + end + return ref.num:sub(1,ref.fit) + elseif fit==#num then + return string.reverse(num) + else + return string.reverse(string.rep("\0",fit-#num)..num) + end + else + return string.reverse(num) + end + end +end +function infinabits:conv(n) + local tab={} + local one=BigNum.new(1) + local n=BigNum.new(n) + while n>=one do + table.insert(tab,tonumber(tostring(n%2))) + n=n/2 + end + local str=string.reverse(table.concat(tab)) + if #str%8~=0 or #str==0 then + str=string.rep('0',8-#str%8)..str + end + return str +end +function infinabits:tonumber(s) + if s==0 then + return tonumber(self.data,2) + end + s=s or 1 + return tonumber(tostring(base2to10(string.sub(self.data,(8*(s-1))+1,8*s)))) or error('Bounds!') +end +function infinabits:isover() + return #self.data>8 +end +function infinabits:flipbits() + tab={} + local s=self.data + s=s:gsub("1","_") + s=s:gsub("0","1") + s=s:gsub("_","0") + self.data=s +end +function infinabits:tobytes() + local tab={} + for i=self:getbytes(),1,-1 do + table.insert(tab,string.char(self:tonumber(i))) + end + return bin.new(table.concat(tab)) +end +function infinabits:toSbytes() + local tab={} + for i=self:getbytes(),1,-1 do + table.insert(tab,string.char(self:tonumber(i))) + end + return table.concat(tab) +end +function infinabits:getBin() + return self.data +end +function infinabits:getbytes() + return #self.data/8 +end +local randomGen=require("bin.numbers.random") +function bin.setBitsInterface(int) + bin.defualtBit=int or infinabits +end +bin.setBitsInterface() +function bin.normalizeData(data) -- unified function to allow for all types to string + if type(data)=="string" then return data end + if type(data)=="table" then + if data.Type=="bin" or data.Type=="streamable" or data.Type=="buffer" then + return data:getData() + elseif data.Type=="bits" or data.Type=="infinabits" then + return data:toSbytes() + elseif data.Type=="sink" then + -- LATER + else + return "" + end + elseif type(data)=="userdata" then + if tostring(data):sub(1,4)=="file" then + local cur=data:seek("cur") + data:seek("set",0) + local dat=data:read("*a") + data:seek("set",cur) + return dat + else + error("File handles are the only userdata that can be used!") + end + end +end +function bin.resolveType(tab) -- used in getblock for auto object creation. Internal method + if tab.Type then + if tab.Type=="bin" then + return bin.new(tab.data) + elseif tab.Type=="streamable" then + if bin.fileExist(tab.file) then return nil,"Cannot load the stream file, source file does not exist!" end + return bin.stream(tab.file,tab.lock) + elseif tab.Type=="buffer" then + local buf=bin.newDataBuffer(tab.size) + buf[1]=tab:getData() + return buf + elseif tab.Type=="bits" then + local b=bits.new("") + b.data=tab.data + return b + elseif tab.Type=="infinabits" then + local b=infinabits.new("") + b.data=tab.data + return b + elseif tab.Type=="sink" then + return bin.newSync(tab.data) + else -- maybe a type from another library + return tab + end + else return tab end +end +function bin.fileExist(path) + g=io.open(path or '','r') + if path =='' then + p='empty path' + return nil + end + if g~=nil and true or false then + p=(g~=nil and true or false) + end + if g~=nil then + io.close(g) + else + return false + end + return p +end +function bin.toHex(str) + local str=bin.normalizeData(str) + return (str:gsub('.', function (c) + return string.format('%02X', string.byte(c)) + end)) +end +function bin.fromHex(str) + return (str:gsub('..', function (cc) + return string.char(tonumber(cc, 16)) + end)) +end + +-- Constructors +function bin.new(data) + data=bin.normalizeData(data) + local c = {} + setmetatable(c, bin) + c.data=data + c.Type="bin" + c.t="bin" + c.pos=1 + c.stream=false + return c +end + +function bin.newFromHex(data) + return bin.new(bin.fromHex(data)) +end +function bin.load(path) + if type(path) ~= "string" then error("Path must be a string!") end + local f = io.open(path, 'rb') + local content = f:read('*a') + f:close() + return bin.new(content) +end +function bin.stream(file,l) + if not(l==false) then l=true end + local c=bin.new() + c.Type="streamable" + c.t="streamable" + if bin.streams[file]~=nil then + c.file=file + c.lock = l + c.workingfile=bin.streams[file][1].workingfile + bin.streams[file][2]=bin.streams[file][2]+1 + c.stream=true + return c + end + if bin.fileExist(file) then + c.file=file + c.lock = l + c.workingfile=io.open(file,'rb+') + else + c.file=file + c.lock = l + c.workingfile=io.open(file,'w') + io.close(c.workingfile) + c.workingfile=io.open(file,'rb+') + end + c.stream=true + bin.streams[file]={c,1} + return c +end +function bin.newTempFile() + local c=bin.new() + c.file=file + c.lock = false + c.workingfile=io.tmpfile() + c.stream=true + return c +end +function bin.freshStream(file) + bin.new():tofile(file) + return bin.stream(file,false) +end +function bin.newStreamFileObject(file) + local c=bin.new() + c.Type="streamable" + c.t="streamable" + c.file="FILE_OBJECT" + c.lock = false + c.workingfile=file + c.stream=true + return c +end +-- Core Methods +function bin:canStreamWrite() + return (self.stream and not(self.lock)) +end +function bin:getSeek() + if self.stream then + return self.workingfile:seek("cur")+1 + else + return self.pos + end +end +function bin:setSeek(n) + if self.stream then + self.workingfile:seek("set",n-1) + else + self.pos=n + end +end +function bin:seek(n) + if self.stream then + if not n then return self.workingfile:seek("cur") end + local cur=self.workingfile:seek("cur") + self.workingfile:seek("set",cur+n) + else + if not n then return self.pos end + if #self.data-(self.pos-1)size then + data = data:sub(1,size) + elseif dsize255 then + nn=nn%256 + elseif nn<0 then + nn=256-math.abs(nn) + end + buf[i]=nn + end + self:setSeek(1) + self:write(buf:getData()) +end +function bin:getData(a,b,fmt) + local data="" + if a or b then + data=self:sub(a,b) + else + if self.stream then + local cur=self.workingfile:seek("cur") + self.workingfile:seek("set",0) + data=self.workingfile:read("*a") + self.workingfile:seek("set",cur) + else + data=self.data + end + end + if fmt=="%x" or fmt=="hex" then + return bin.toHex(data):lower() + elseif fmt=="%X" or fmt=="HEX" then + return bin.toHex(data) + elseif fmt=="%b" or fmt=="b64" then + return bin.toB64(data) + elseif fmt then + return bin.new(data):getBlock(fmt,#data) + end + return data +end +function bin:getSize(fmt) + local len=0 + if self.stream then + local cur=self.workingfile:seek("cur") + len=self.workingfile:seek("end") + self.workingfile:seek("set",cur) + else + len=#self.data + end + if fmt=="%b" then + return bin.toB64() + elseif fmt then + return string.format(fmt, len) + else + return len + end +end +function bin:tackE(data,size,h) + local data=bin.normalizeData(data) + local cur=self:getSize() + self:setSeek(self:getSize()+1) + self:write(data,size) + if h then + self:setSeek(cur+1) + end +end +function bin:tonumber(a,b) + local temp={} + if a then + temp.data=self:sub(a,b) + else + temp=self + end + local l,r=0,0 + local g=#temp.data + for i=1,g do + r=r+(256^(g-i))*string.byte(string.sub(temp.data,i,i)) + l=l+(256^(i-1))*string.byte(string.sub(temp.data,i,i)) + end + return r,l +end +function bin.endianflop(data) + return string.reverse(data) +end +function bin:tofile(name) + if self.stream then return end + if not name then error("Must include a filename to save as!") end + file = io.open(name, "wb") + file:write(self.data) + file:close() +end +function bin:close() + if self.stream then + if bin.streams[self.file][2]==1 then + bin.streams[self.file]=nil + self.workingfile:close() + else + bin.streams[self.file][2]=bin.streams[self.file][2]-1 + self.workingfile=io.tmpfile() + self.workingfile:close() + end + end +end +function bin:getBlock(t,n) + local data="" + if not n then + if bin.registerBlocks[t] then + return bin.registerBlocks[t][1](nil,self) + else + error("Unknown format! Cannot read from file: "..tostring(t)) + end + else + if t=="n" or t=="%e" or t=="%E" then + data=self:read(n) + local numB=bin.defualtBit.new(data) + local numL=bin.defualtBit.new(string.reverse(data)) + local little=numL:tonumber(0) + local big=numB:tonumber(0) + if t=="%E" then + return big + elseif t=="%X" then + return bin.toHex(data):upper() + elseif t=="%x" then + return bin.toHex(data):lower() + elseif t=="%b" then + return bin.toB64(data) + elseif t=="%e" then + return little + end + return big,little + elseif t=="s" then + return self:read(n) + elseif bin.registerBlocks[t] then + return bin.registerBlocks[t][1](n,self) + else + error("Unknown format! Cannot read from file: "..tostring(t)) + end + end +end +function bin:addBlock(d,fit,fmt) + if not fmt then fmt=type(d):sub(1,1) end + if bin.registerBlocks[fmt] then + self:tackE(bin.registerBlocks[fmt][2](d,fit,fmt,self,bin.registerBlocks[fmt][2])) + elseif type(d)=="number" then + local data=bin.defualtBit.numToBytes(d,fit or 4,fmt,function() + error("Overflow! Space allotted for number is smaller than the number takes up. Increase the fit!") + end) + self:tackE(data) + elseif type(d)=="string" then + local data=d:sub(1,fit or -1) + if #data<(fit or #data) then + data=data..string.rep("\0",fit-#data) + end + self:tackE(data) + end +end +bin.registerBlocks={} +function bin.registerBlock(t,funcG,funcA) + bin.registerBlocks[t]={funcG,funcA} +end +function bin.newDataBuffer(size,fill) -- fills with \0 or nul or with what you enter + local c={} + local fill=fill or "\0" + c.data={self=c} + c.Type="buffer" + c.size=size or 0 -- 0 means an infinite buffer, sometimes useful + for i=1,c.size do + c.data[i]=fill + end + local mt={ + __index=function(t,k) + if type(k)=="number" then + local data=t.data[k] + if data then + return string.byte(data) + else + error("Index out of range!") + end + elseif type(k)=="string" then + local num=tonumber(k) + if num then + local data=t.data[num] + if data then + return data + else + error("Index out of range!") + end + else + error("Only number-strings and numbers can be indexed!") + end + else + error("Only number-strings and numbers can be indexed!") + end + end, + __newindex=function(t,k,v) + if type(k)~="number" then error("Can only set a buffers data with a numeric index!") end + local data="" + if type(v)=="string" then + data=v + elseif type(v)=="number" then + data=bits.numToBytes(v) + else + -- try to normalize the data of type v + data=bin.normalizeData(v) + end + t:fillBuffer(k,data) + end, + __tostring=function(t) + return t:getData() + end, + } + function c:fillBuffer(a,data) + local len=#data + if len==1 then + self.data[a]=data + else + local i=a-1 + for d in data:gmatch(".") do + i=i+1 + if i>c.size then + return #data-i+a + end + self.data[i]=d + end + return #data-i+(a-1) + end + end + function c:getData(a,b,fmt) -- LATER + local dat=bin.new(table.concat(self.data,"",a,b)) + local n=dat:getSize() + return dat:getBlock(fmt or "s",n) + end + function c:getSize() + return #self:getData() + end + setmetatable(c,mt) + return c +end +function bin:newDataBufferFromStream(pos,size,fill) -- fills with \0 or nul or with what you enter IF the nothing exists inside the bin file. + local s=self:getSize() + if not self.stream then error("Can only created a streamed buffer on a streamable file!") end + if s==0 then + self:write(string.rep("\0",pos+size)) + end + self:setSeek(1) + local c=bin.newDataBuffer(size,fill) + rawset(c,"pos",pos) + rawset(c,"size",size) + rawset(c,"fill",fill) + rawset(c,"bin",self) + rawset(c,"sync",function(self) + local cur=self.bin:getSeek() + self.bin:setSeek(self.pos) + self.bin:write(self:getData(),size) + self.bin:setSeek(cur) + end) + c:fillBuffer(1,self:sub(pos,pos+size)) + function c:fillBuffer(a,data) + local len=#data + if len==1 then + self.data[a]=data + self:sync() + else + local i=a-1 + for d in data:gmatch(".") do + i=i+1 + if i>c.size then + self:sync() + return #data-i+a + end + self.data[i]=d + end + self:sync() + return #data-i+(a-1) + end + end + return c +end +function bin:toDataBuffer() + local s=self:getSize() + -- if self:canStreamWrite() then + -- return self:newDataBufferFromStream(0,s) + -- end + local buf=bin.newDataBuffer(s) + local data=self:read(512) + local i=1 + while data~=nil do + buf[i]=data + data=self:read(512) + i=i+512 + end + return buf +end +function bin:getHash() + if self:getSize()==0 then + return "NaN" + end + n=32 + local rand = randomGen:newND(1,self:getSize(),self:getSize()) + local h,g={},0 + for i=1,n do + g=rand:nextInt() + table.insert(h,bin.toHex(self:sub(g,g))) + end + return table.concat(h,'') +end +function bin:flipbits() + if self:canStreamWrite() then + self:setSeek(1) + for i=1,self:getSize() do + self:write(string.char(255-string.byte(self:sub(i,i)))) + end + else + local temp={} + for i=1,#self.data do + table.insert(temp,string.char(255-string.byte(string.sub(self.data,i,i)))) + end + self.data=table.concat(temp,'') + end +end +function bin:encrypt() + self:flipbits() +end +function bin:decrypt() + self:flipbits() +end +-- Use with small files! +function bin:gsub(...) + local data=self:getData() + local pos=self:getSeek() + self:setSeek(1) + self:write((data:gsub(...)) or data) + self:setSeek(loc) +end +function bin:gmatch(pat) + return self:getData():gmatch(pat) +end +function bin:match(pat) + return self:getData():match(pat) +end +function bin:trim() + local data=self:getData() + local pos=self:getSeek() + self:setSeek(1) + self:write(data:match'^()%s*$' and '' or data:match'^%s*(.*%S)') + self:setSeek(loc) +end +function bin:lines() + local t = {} + local function helper(line) table.insert(t, line) return '' end + helper((self:getData():gsub('(.-)\r?\n', helper))) + return t +end +function bin._lines(str) + local t = {} + local function helper(line) table.insert(t, line) return '' end + helper((str:gsub('(.-)\r?\n', helper))) + return t +end +function bin:wipe() + if self:canStreamWrite() then + self:close() + local c=bin.freshStream(self.file) + self.workingfile=c.workingfile + else + self.data="" + end + self:setSeek(1) +end +function bin:fullTrim(empty) + local t=self:lines() + for i=#t,1,-1 do + t[i]=bin._trim(t[i]) + if empty then + if t[i]=="" then + table.remove(t,i) + end + end + end + self:wipe() + self:write(table.concat(t,"\n")) +end +local __CURRENTVERSION=2 +bin.registerBlock("t",function(SIZE_OR_NIL,ref) + local header=ref:read(3) + if not header:match("(LT.)") then error("Not a valid table struct!") end + if bin.defualtBit.new(header:sub(3,3)):tonumber(1)>__CURRENTVERSION then error("Incompatible Version of LuaTable!") end + local len=ref:getBlock("n",4) -- hehe lets make life easier + local tab={} + local ind + local n=0 + while true do + local _dat=ref:read(2) + if _dat==nil then break end + local it,dt=_dat:match("(.)(.)") + n=n+2 + if it=="N" then -- get the index stuff out of the way first + ind=ref:getBlock("n",4) + n=n+4 + else + indL=ref:getBlock("n",1) + n=n+1+indL + ind=ref:read(indL) + end + if dt=="N" then + tab[ind]=ref:getBlock("d") + n=n+8 + elseif dt=="I" then + tab[ind]=math.huge + ref:getBlock("n",4) + n=n+4 + elseif dt=="i" then + tab[ind]=-math.huge + ref:getBlock("n",4) + n=n+4 + elseif dt=="S" then + local nn=ref:getBlock("n",4) + tab[ind]=ref:read(nn) + n=n+4+nn + elseif dt=="B" then + tab[ind]=({["\255"]=true,["\0"]=false})[ref:read(1)] + n=n+1 + elseif dt=="F" then + local nn=ref:getBlock("n",4) + tab[ind]=loadstring(ref:read(nn)) + n=n+4+nn + elseif dt=="T" then + local cur=ref:getSeek() + local size=ref:getBlock("n",4) + ref:setSeek(cur) + ref:read(4) + if size==7 then + tab[ind]={} + ref:read(7) + n=n+11 + else + local data=bin.new(ref:read(size)) + local dat=data:getBlock("t") + if dat.__RECURSIVE then + tab[ind]=tab + else + tab[ind]=dat + end + n=n+data:getSize()+4 + end + end + if n==len then break end + end + return bin.resolveType(tab) +end,function(d,fit,fmt,self,rec,tabsaw) + -- INGORE FIT WE ARE CREATING A STRUCT!!! + -- fmt will apply to all numbers + local __rem=nil + if not tabsaw then rem=true end + local tabsaw=tabsaw or {} + if rem then + table.insert(tabsaw,d) + end + local bData={} + for i,v in pairs(d) do -- this is for tables, all but userdata is fine. Depending on where you are using lua functions may or may not work + local tp=type(v):sub(1,1):upper() -- uppercase of datatype + if type(i)=="number" then -- Lets handle indexies + if v==math.huge then + tp="I" + v=0 + elseif v==-math.huge then + tp="i" + v=0 + end + table.insert(bData,"N"..tp..bin.defualtBit.numToBytes(i,4)) -- number index? + elseif type(i)=="string" then + if #i>255 then error("A string index cannot be larger than 255 bytes!") end + table.insert(bData,"S"..tp..bin.defualtBit.numToBytes(#i,1)..i) -- string index? + else + error("Only numbers and strings can be a table index!") -- throw error? + end + if type(v)=="number" then + -- How do we handle number data + local temp=bin.new() + temp:addBlock(v,nil,"d") + table.insert(bData,temp.data) + elseif type(v)=="string" then + -- Lets work on strings + table.insert(bData,bin.defualtBit.numToBytes(#v,4)) -- add length of string + table.insert(bData,v) -- add string + elseif type(v)=="boolean" then -- bools are easy :D + table.insert(bData,({[true]="\255",[false]="\0"})[v]) + elseif type(v)=="function" then -- should we allow this? why not... + local dump=string.dump(v) + table.insert(bData,bin.defualtBit.numToBytes(#dump,4)) -- add length of dumped string + table.insert(bData,dump) -- add it + elseif type(v)=="table" then -- tables... + if tabsaw[1]==v then + v={__RECURSIVE=i} + else + tabsaw[i]=v + end + local data=rec(v,nil,"t",self,rec,tabsaw) + table.insert(bData,bin.defualtBit.numToBytes(#data,4)) -- add length of string + table.insert(bData,data) -- add string + end + end + local data=table.concat(bData) + return "LT"..string.char(__CURRENTVERSION)..bin.defualtBit.numToBytes(#data,4)..data +end) +bin.registerBlock("b",function(SIZE_OR_NIL,ref) + return ({["\255"]=true,["\0"]=false})[ref:read(1)] +end,function(d) + return ({[true]="\255",[false]="\0"})[d] +end) +bin.registerBlock("f",function(SIZE_OR_NIL,ref) + local nn=ref:getBlock("n",4) + return loadstring(ref:read(nn)) +end,function(d) + local dump=string.dump(d) + return bin.defualtBit.numToBytes(#dump,4)..dump +end) +bin.registerBlock("d",function(SIZE_OR_NIL,ref) + local w,p=ref:getBlock("n",4),ref:getBlock("n",4) + p=tonumber("0."..tostring(p)) + return w+p +end,function(d,fit,fmt,self,rec,tabsaw) + local w,p = toFraction(d) + local temp=bin.new() + temp:addBlock(w,4) + temp:addBlock(p,4) + return temp.data +end) +if love then + function bin.load(file,s,r) + content, size = love.filesystem.read(file) + local temp=bin.new(content) + temp.filepath=file + return temp + end + function bin.fileExists(name) + return love.filesystem.getInfo(name) + end + function bin:tofile(filename) + if not(filename) or self.Stream then return nil end + love.filesystem.write(filename,self.data) + end + function bin.loadS(path,s,r) + local path = love.filesystem.getSaveDirectory( ).."\\"..path + if type(path) ~= "string" then error("Path must be a string!") end + local f = io.open(path, 'rb') + local content = f:read('*a') + f:close() + return bin.new(content) + end + function bin:tofileS(filename) + if self.stream then return end + local filename = love.filesystem.getSaveDirectory( ).."\\"..filename + print(#self.data,filename) + if not filename then error("Must include a filename to save as!") end + file = io.open(filename, "wb") + file:write(self.data) + file:close() + end + function bin.stream(file) + return bin.newStreamFileObject(love.filesystem.newFile(file)) + end + function bin:getSize(fmt) + local len=0 + if self.stream then + local len=self.workingfile:getSize() + else + len=#self.data + end + if fmt=="%b" then + return bin.toB64() + elseif fmt then + return string.format(fmt, len) + else + return len + end + end + function bin:getSeek() + if self.stream then + return self.workingfile:tell()+1 + else + return self.pos + end + end + function bin:setSeek(n) + if self.stream then + self.workingfile:seek(n-1) + else + self.pos=n + end + end + function bin:seek(n) + if self.stream then + self.workingfile:seek(n) + else + if not n then return self.pos end + if #self.data-(self.pos-1) 0 then + for i = bnum.len - 2 , 0 , -1 do + for j = 0 , RADIX_LEN - string.len( bnum[i] ) - 1 do + temp = temp .. '0' ; + end + temp = temp .. bnum[i] ; + end + if bnum[bnum.len - 1]==nil then + return "nil" + end + temp = bnum[bnum.len - 1] .. temp ; + if bnum.signal == '-' then + temp = bnum.signal .. temp ; + end + return temp ; + else + return "" ; + end +end + +function BigNum.mt.pow( num1 , num2 ) + local bnum1 = BigNum.new( num1 ) ; + local bnum2 = BigNum.new( num2 ) ; + return BigNum.pow( bnum1 , bnum2 ) ; +end + +function BigNum.mt.eq( num1 , num2 ) + local bnum1 = BigNum.new( num1 ) ; + local bnum2 = BigNum.new( num2 ) ; + return BigNum.eq( bnum1 , bnum2 ) ; +end + +function BigNum.mt.lt( num1 , num2 ) + local bnum1 = BigNum.new( num1 ) ; + local bnum2 = BigNum.new( num2 ) ; + return BigNum.lt( bnum1 , bnum2 ) ; +end + +function BigNum.mt.le( num1 , num2 ) + local bnum1 = BigNum.new( num1 ) ; + local bnum2 = BigNum.new( num2 ) ; + return BigNum.le( bnum1 , bnum2 ) ; +end + +function BigNum.mt.unm( num ) + local ret = BigNum.new( num ) + if ret.signal == '+' then + ret.signal = '-' + else + ret.signal = '+' + end + return ret +end + +BigNum.mt.__metatable = "hidden" +BigNum.mt.__tostring = BigNum.mt.tostring ; +BigNum.mt.__add = BigNum.mt.add ; +BigNum.mt.__sub = BigNum.mt.sub ; +BigNum.mt.__mul = BigNum.mt.mul ; +BigNum.mt.__div = BigNum.mt.div ; +BigNum.mt.__pow = BigNum.mt.pow ; +BigNum.mt.__unm = BigNum.mt.unm ; +BigNum.mt.__mod = BigNum.mt.mod ; +BigNum.mt.__eq = BigNum.mt.eq ; +BigNum.mt.__le = BigNum.mt.le ; +BigNum.mt.__lt = BigNum.mt.lt ; +setmetatable( BigNum.mt, { __index = "inexistent field", __newindex = "not available", __metatable="hidden" } ) ; +function BigNum.add( bnum1 , bnum2 , bnum3 ) + local maxlen = 0 ; + local i = 0 ; + local carry = 0 ; + local signal = '+' ; + local old_len = 0 ; + --Handle the signals + if bnum1 == nil or bnum2 == nil or bnum3 == nil then + error("Function BigNum.add: parameter nil") ; + elseif bnum1.signal == '-' and bnum2.signal == '+' then + bnum1.signal = '+' ; + BigNum.sub( bnum2 , bnum1 , bnum3 ) ; + + if not rawequal(bnum1, bnum3) then + bnum1.signal = '-' ; + end + return 0 ; + elseif bnum1.signal == '+' and bnum2.signal == '-' then + bnum2.signal = '+' ; + BigNum.sub( bnum1 , bnum2 , bnum3 ) ; + if not rawequal(bnum2, bnum3) then + bnum2.signal = '-' ; + end + return 0 ; + elseif bnum1.signal == '-' and bnum2.signal == '-' then + signal = '-' ; + end + -- + old_len = bnum3.len ; + if bnum1.len > bnum2.len then + maxlen = bnum1.len ; + else + maxlen = bnum2.len ; + bnum1 , bnum2 = bnum2 , bnum1 ; + end + --School grade sum + for i = 0 , maxlen - 1 do + if bnum2[i] ~= nil then + bnum3[i] = bnum1[i] + bnum2[i] + carry ; + else + bnum3[i] = bnum1[i] + carry ; + end + if bnum3[i] >= RADIX then + bnum3[i] = bnum3[i] - RADIX ; + carry = 1 ; + else + carry = 0 ; + end + end + --Update the answer's size + if carry == 1 then + bnum3[maxlen] = 1 ; + end + bnum3.len = maxlen + carry ; + bnum3.signal = signal ; + for i = bnum3.len, old_len do + bnum3[i] = nil ; + end + return 0 ; +end + +function BigNum.sub( bnum1 , bnum2 , bnum3 ) + local maxlen = 0 ; + local i = 0 ; + local carry = 0 ; + local old_len = 0 ; + if bnum1 == nil or bnum2 == nil or bnum3 == nil then + error("Function BigNum.sub: parameter nil") ; + elseif bnum1.signal == '-' and bnum2.signal == '+' then + bnum1.signal = '+' ; + BigNum.add( bnum1 , bnum2 , bnum3 ) ; + bnum3.signal = '-' ; + if not rawequal(bnum1, bnum3) then + bnum1.signal = '-' ; + end + return 0 ; + elseif bnum1.signal == '-' and bnum2.signal == '-' then + bnum1.signal = '+' ; + bnum2.signal = '+' ; + BigNum.sub( bnum2, bnum1 , bnum3 ) ; + if not rawequal(bnum1, bnum3) then + bnum1.signal = '-' ; + end + if not rawequal(bnum2, bnum3) then + bnum2.signal = '-' ; + end + return 0 ; + elseif bnum1.signal == '+' and bnum2.signal == '-' then + bnum2.signal = '+' ; + BigNum.add( bnum1 , bnum2 , bnum3 ) ; + if not rawequal(bnum2, bnum3) then + bnum2.signal = '-' ; + end + return 0 ; + end + --Tests if bnum2 > bnum1 + if BigNum.compareAbs( bnum1 , bnum2 ) == 2 then + BigNum.sub( bnum2 , bnum1 , bnum3 ) ; + bnum3.signal = '-' ; + return 0 ; + else + maxlen = bnum1.len ; + end + old_len = bnum3.len ; + bnum3.len = 0 ; + --School grade subtraction + for i = 0 , maxlen - 1 do + if bnum2[i] ~= nil then + bnum3[i] = bnum1[i] - bnum2[i] - carry ; + else + bnum3[i] = bnum1[i] - carry ; + end + if bnum3[i] < 0 then + bnum3[i] = RADIX + bnum3[i] ; + carry = 1 ; + else + carry = 0 ; + end + + if bnum3[i] ~= 0 then + bnum3.len = i + 1 ; + end + end + bnum3.signal = '+' ; + --Check if answer's size if zero + if bnum3.len == 0 then + bnum3.len = 1 ; + bnum3[0] = 0 ; + end + if carry == 1 then + error( "Error in function sub" ) ; + end + for i = bnum3.len , max( old_len , maxlen - 1 ) do + bnum3[i] = nil ; + end + return 0 ; +end + +function BigNum.mul( bnum1 , bnum2 , bnum3 ) + local i = 0 ; j = 0 ; + local temp = BigNum.new( ) ; + local temp2 = 0 ; + local carry = 0 ; + local oldLen = bnum3.len ; + if bnum1 == nil or bnum2 == nil or bnum3 == nil then + error("Function BigNum.mul: parameter nil") ; + --Handle the signals + elseif bnum1.signal ~= bnum2.signal then + BigNum.mul( bnum1 , -bnum2 , bnum3 ) ; + bnum3.signal = '-' ; + return 0 ; + end + bnum3.len = ( bnum1.len ) + ( bnum2.len ) ; + --Fill with zeros + for i = 1 , bnum3.len do + bnum3[i - 1] = 0 ; + end + --Places nil where passes through this + for i = bnum3.len , oldLen do + bnum3[i] = nil ; + end + --School grade multiplication + for i = 0 , bnum1.len - 1 do + for j = 0 , bnum2.len - 1 do + carry = ( bnum1[i] * bnum2[j] + carry ) ; + carry = carry + bnum3[i + j] ; + bnum3[i + j] = ( carry % RADIX ) ; + temp2 = bnum3[i + j] ; + carry = math.floor ( carry / RADIX ) ; + end + if carry ~= 0 then + bnum3[i + bnum2.len] = carry ; + end + carry = 0 ; + end + + --Update the answer's size + for i = bnum3.len - 1 , 1 , -1 do + if bnum3[i] ~= nil and bnum3[i] ~= 0 then + break ; + else + bnum3[i] = nil ; + end + bnum3.len = bnum3.len - 1 ; + end + return 0 ; +end + +function BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) + local temp = BigNum.new() ; + local temp2 = BigNum.new() ; + local one = BigNum.new( "1" ) ; + local zero = BigNum.new( "0" ) ; + --Check division by zero + if BigNum.compareAbs( bnum2 , zero ) == 0 then + error( "Function BigNum.div: Division by zero" ) ; + end + --Handle the signals + if bnum1 == nil or bnum2 == nil or bnum3 == nil or bnum4 == nil then + error( "Function BigNum.div: parameter nil" ) ; + elseif bnum1.signal == "+" and bnum2.signal == "-" then + bnum2.signal = "+" ; + BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ; + bnum2.signal = "-" ; + bnum3.signal = "-" ; + return 0 ; + elseif bnum1.signal == "-" and bnum2.signal == "+" then + bnum1.signal = "+" ; + BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ; + bnum1.signal = "-" ; + if bnum4 < zero then --Check if remainder is negative + BigNum.add( bnum3 , one , bnum3 ) ; + BigNum.sub( bnum2 , bnum4 , bnum4 ) ; + end + bnum3.signal = "-" ; + return 0 ; + elseif bnum1.signal == "-" and bnum2.signal == "-" then + bnum1.signal = "+" ; + bnum2.signal = "+" ; + BigNum.div( bnum1 , bnum2 , bnum3 , bnum4 ) ; + bnum1.signal = "-" ; + if bnum4 < zero then --Check if remainder is negative + BigNum.add( bnum3 , one , bnum3 ) ; + BigNum.sub( bnum2 , bnum4 , bnum4 ) ; + end + bnum2.signal = "-" ; + return 0 ; + end + temp.len = bnum1.len - bnum2.len - 1 ; + + --Reset variables + BigNum.change( bnum3 , "0" ) ; + BigNum.change( bnum4 , "0" ) ; + + BigNum.copy( bnum1 , bnum4 ) ; + + --Check if can continue dividing + while( BigNum.compareAbs( bnum4 , bnum2 ) ~= 2 ) do + if bnum4[bnum4.len - 1] >= bnum2[bnum2.len - 1] then + BigNum.put( temp , math.floor( bnum4[bnum4.len - 1] / bnum2[bnum2.len - 1] ) , bnum4.len - bnum2.len ) ; + temp.len = bnum4.len - bnum2.len + 1 ; + else + BigNum.put( temp , math.floor( ( bnum4[bnum4.len - 1] * RADIX + bnum4[bnum4.len - 2] ) / bnum2[bnum2.len -1] ) , bnum4.len - bnum2.len - 1 ) ; + temp.len = bnum4.len - bnum2.len ; + end + + if bnum4.signal ~= bnum2.signal then + temp.signal = "-"; + else + temp.signal = "+"; + end + BigNum.add( temp , bnum3 , bnum3 ) ; + temp = temp * bnum2 ; + BigNum.sub( bnum4 , temp , bnum4 ) ; + end + + --Update if the remainder is negative + if bnum4.signal == '-' then + decr( bnum3 ) ; + BigNum.add( bnum2 , bnum4 , bnum4 ) ; + end + return 0 ; +end + +function BigNum.pow( bnum1 , bnum2 ) + local n = BigNum.new( bnum2 ) ; + local y = BigNum.new( 1 ) ; + local z = BigNum.new( bnum1 ) ; + local zero = BigNum.new( "0" ) ; + if bnum2 < zero then + error( "Function BigNum.exp: domain error" ) ; + elseif bnum2 == zero then + return y ; + end + while 1 do + if ( n[0] % 2 ) == 0 then + n = n / 2 ; + else + n = n / 2 ; + y = z * y ; + if n == zero then + return y ; + end + end + z = z * z ; + end +end +-- Português : +BigNum.exp = BigNum.pow + +function BigNum.gcd( bnum1 , bnum2 ) + local a = {} ; + local b = {} ; + local c = {} ; + local d = {} ; + local zero = {} ; + zero = BigNum.new( "0" ) ; + if bnum1 == zero or bnum2 == zero then + return BigNum.new( "1" ) ; + end + a = BigNum.new( bnum1 ) ; + b = BigNum.new( bnum2 ) ; + a.signal = '+' ; + b.signal = '+' ; + c = BigNum.new() ; + d = BigNum.new() ; + while b > zero do + BigNum.div( a , b , c , d ) ; + a , b , d = b , d , a ; + end + return a ; +end +-- Português: +BigNum.mmc = BigNum.gcd + +function BigNum.eq( bnum1 , bnum2 ) + if BigNum.compare( bnum1 , bnum2 ) == 0 then + return true ; + else + return false ; + end +end + +function BigNum.lt( bnum1 , bnum2 ) + if BigNum.compare( bnum1 , bnum2 ) == 2 then + return true ; + else + return false ; + end +end + +function BigNum.le( bnum1 , bnum2 ) + local temp = -1 ; + temp = BigNum.compare( bnum1 , bnum2 ) + if temp == 0 or temp == 2 then + return true ; + else + return false ; + end +end + +function BigNum.compareAbs( bnum1 , bnum2 ) + if bnum1 == nil or bnum2 == nil then + error("Function compare: parameter nil") ; + elseif bnum1.len > bnum2.len then + return 1 ; + elseif bnum1.len < bnum2.len then + return 2 ; + else + local i ; + for i = bnum1.len - 1 , 0 , -1 do + if bnum1[i] > bnum2[i] then + return 1 ; + elseif bnum1[i] < bnum2[i] then + return 2 ; + end + end + end + return 0 ; +end + +function BigNum.compare( bnum1 , bnum2 ) + local signal = 0 ; + + if bnum1 == nil or bnum2 == nil then + error("Funtion BigNum.compare: parameter nil") ; + elseif bnum1.signal == '+' and bnum2.signal == '-' then + return 1 ; + elseif bnum1.signal == '-' and bnum2.signal == '+' then + return 2 ; + elseif bnum1.signal == '-' and bnum2.signal == '-' then + signal = 1 ; + end + if bnum1.len > bnum2.len then + return 1 + signal ; + elseif bnum1.len < bnum2.len then + return 2 - signal ; + else + local i ; + for i = bnum1.len - 1 , 0 , -1 do + if bnum1[i] > bnum2[i] then + return 1 + signal ; + elseif bnum1[i] < bnum2[i] then + return 2 - signal ; + end + end + end + return 0 ; +end + +function BigNum.copy( bnum1 , bnum2 ) + if bnum1 ~= nil and bnum2 ~= nil then + local i ; + for i = 0 , bnum1.len - 1 do + bnum2[i] = bnum1[i] ; + end + bnum2.len = bnum1.len ; + else + error("Function BigNum.copy: parameter nil") ; + end +end + +function BigNum.change( bnum1 , num ) + local j = 0 ; + local len = 0 ; + local num = num ; + local l ; + local oldLen = 0 ; + if bnum1 == nil then + error( "BigNum.change: parameter nil" ) ; + elseif type( bnum1 ) ~= "table" then + error( "BigNum.change: parameter error, type unexpected" ) ; + elseif num == nil then + bnum1.len = 1 ; + bnum1[0] = 0 ; + bnum1.signal = "+"; + elseif type( num ) == "table" and num.len ~= nil then --check if num is a big number + --copy given table to the new one + for i = 0 , num.len do + bnum1[i] = num[i] ; + end + if num.signal ~= '-' and num.signal ~= '+' then + bnum1.signal = '+' ; + else + bnum1.signal = num.signal ; + end + oldLen = bnum1.len ; + bnum1.len = num.len ; + elseif type( num ) == "string" or type( num ) == "number" then + if string.sub( num , 1 , 1 ) == '+' or string.sub( num , 1 , 1 ) == '-' then + bnum1.signal = string.sub( num , 1 , 1 ) ; + num = string.sub(num, 2) ; + else + bnum1.signal = '+' ; + end + num = string.gsub( num , " " , "" ) ; + local sf = string.find( num , "e" ) ; + --Handles if the number is in exp notation + if sf ~= nil then + num = string.gsub( num , "%." , "" ) ; + local e = string.sub( num , sf + 1 ) ; + e = tonumber(e) ; + if e ~= nil and e > 0 then + e = tonumber(e) ; + else + error( "Function BigNum.change: string is not a valid number" ) ; + end + num = string.sub( num , 1 , sf - 2 ) ; + for i = string.len( num ) , e do + num = num .. "0" ; + end + else + sf = string.find( num , "%." ) ; + if sf ~= nil then + num = string.sub( num , 1 , sf - 1 ) ; + end + end + + l = string.len( num ) ; + oldLen = bnum1.len ; + if (l > RADIX_LEN) then + local mod = l-( math.floor( l / RADIX_LEN ) * RADIX_LEN ) ; + for i = 1 , l-mod, RADIX_LEN do + bnum1[j] = tonumber( string.sub( num, -( i + RADIX_LEN - 1 ) , -i ) ); + --Check if string dosn't represents a number + if bnum1[j] == nil then + error( "Function BigNum.change: string is not a valid number" ) ; + bnum1.len = 0 ; + return 1 ; + end + j = j + 1 ; + len = len + 1 ; + end + if (mod ~= 0) then + bnum1[j] = tonumber( string.sub( num , 1 , mod ) ) ; + bnum1.len = len + 1 ; + else + bnum1.len = len ; + end + --Eliminate trailing zeros + for i = bnum1.len - 1 , 1 , -1 do + if bnum1[i] == 0 then + bnum1[i] = nil ; + bnum1.len = bnum1.len - 1 ; + else + break ; + end + end + + else + -- string.len(num) <= RADIX_LEN + bnum1[j] = tonumber( num ) ; + bnum1.len = 1 ; + end + else + error( "Function BigNum.change: parameter error, type unexpected" ) ; + end + + --eliminates the deprecated higher order 'algarisms' + if oldLen ~= nil then + for i = bnum1.len , oldLen do + bnum1[i] = nil ; + end + end + + return 0 ; +end + +function BigNum.put( bnum , int , pos ) + if bnum == nil then + error("Function BigNum.put: parameter nil") ; + end + local i = 0 ; + for i = 0 , pos - 1 do + bnum[i] = 0 ; + end + bnum[pos] = int ; + for i = pos + 1 , bnum.len do + bnum[i] = nil ; + end + bnum.len = pos ; + return 0 ; +end + +--printraw{{{2 +function printraw( bnum ) + local i = 0 ; + if bnum == nil then + error( "Function printraw: parameter nil" ) ; + end + while 1 == 1 do + if bnum[i] == nil then + io.write( ' len '..bnum.len ) ; + if i ~= bnum.len then + io.write( ' ERRO!!!!!!!!' ) ; + end + io.write( "\n" ) ; + return 0 ; + end + io.write( 'r'..bnum[i] ) ; + i = i + 1 ; + end +end +--max{{{2 +function max( int1 , int2 ) + if int1 > int2 then + return int1 ; + else + return int2 ; + end +end + +--decr{{{2 +function decr( bnum1 ) + local temp = {} ; + temp = BigNum.new( "1" ) ; + BigNum.sub( bnum1 , temp , bnum1 ) ; + return 0 ; +end diff --git a/bin/bin/numbers/BigRat.lua b/bin/bin/numbers/BigRat.lua new file mode 100644 index 0000000..40e9777 --- /dev/null +++ b/bin/bin/numbers/BigRat.lua @@ -0,0 +1,227 @@ +require( "bin.numbers.BigNum" ) ; + +BigRat = {} ; +BigRat.mt = {} ; +function BigRat.new( num1 , num2 ) --{{{2 + local bigrat = {} ; + local f ; + setmetatable(bigrat, BigRat.mt) ; + if type( num1 ) == "table" then + if num1.num ~= nil and num1.den ~= nil then + bigrat.num = BigNum.new( num1.num ) ; + bigrat.den = BigNum.new( num1.den ) ; + else + bigrat.num = BigNum.new( num1 ) ; + bigrat.den = BigNum.new( "1" ) ; + end + elseif num1 ~= nil then + if num2 == nil then + bigrat.den = BigNum.new( "1" ) ; + else + bigrat.den = BigNum.new( num2 ) ; + end + bigrat.num = BigNum.new( num1 ) ; + else + bigrat.den = BigNum.new( ) ; + bigrat.num = BigNum.new( ) ; + end + + --Update the signals + if bigrat.den.signal == "-" then + if bigrat.num.signal == "-" then + bigrat.num.signal = "+" ; + else + bigrat.num.signal = "-" ; + end + bigrat.den.signal = "+" ; + end + + return bigrat ; +end + +function BigRat.mt.sub( num1 , num2 ) + local temp = BigRat.new() ; + local brat1 = BigRat.new( num1 ) ; + local brat2 = BigRat.new( num2 ) ; + BigRat.sub( brat1 , brat2 , temp ) ; + return temp ; +end + +function BigRat.mt.add( num1 , num2 ) + local temp = BigRat.new() ; + local brat1 = BigRat.new( num1 ) ; + local brat2 = BigRat.new( num2 ) ; + BigRat.add( brat1 , brat2 , temp ) ; + return temp ; +end + +function BigRat.mt.mul( num1 , num2 ) + local temp = BigRat.new() ; + local brat1 = BigRat.new( num1 ) ; + local brat2 = BigRat.new( num2 ) ; + BigRat.mul( brat1 , brat2 , temp ) ; + return temp ; +end + +function BigRat.mt.div( num1 , num2 ) + local brat1 = BigRat.new( num1 ) ; + local brat2 = BigRat.new( num2 ) ; + local brat3 = BigRat.new() ; + local brat4 = BigRat.new() ; + BigRat.div( brat1 , brat2 , brat3 , brat4 ) ; + return brat3 , brat4 ; +end + +function BigRat.mt.tostring( brat ) + BigRat.simplify( brat ) ; + return BigNum.mt.tostring( brat.num ) .. " / " .. BigNum.mt.tostring( brat.den ) ; +end + +function BigRat.mt.pow ( num1 , num2 ) + local brat1 = BigRat.new( num1 ) ; + local brat2 = BigRat.new( num2 ) ; + return BigRat.pow( brat1 , brat2 ) +end + +function BigRat.mt.eq ( num1 , num2 ) + return BigRat.eq( num1 , num2 ) +end + +function BigRat.mt.lt ( num1 , num2 ) + return BigRat.lt( num1 , num2 ) +end + +function BigRat.mt.le ( num1 , num2 ) + return BigRat.le( num1 , num2 ) +end + +function BigRat.mt.unm ( num ) + local ret = BigRat.new( num ) + if ret.num.signal == '-' then + ret.num.signal = '+' + else + ret.num.signal = '-' + end + return ret +end + +BigRat.mt.__metatable = "hidden" +BigRat.mt.__tostring = BigRat.mt.tostring +BigRat.mt.__add = BigRat.mt.add +BigRat.mt.__sub = BigRat.mt.sub +BigRat.mt.__mul = BigRat.mt.mul +BigRat.mt.__div = BigRat.mt.div +BigRat.mt.__pow = BigRat.mt.pow +BigRat.mt.__unm = BigRat.mt.unm +BigRat.mt.__eq = BigRat.mt.eq +BigRat.mt.__le = BigRat.mt.le +BigRat.mt.__lt = BigRat.mt.lt +setmetatable( BigRat.mt, { __index = "inexistent field", __newindex = "not available", __metatable="hidden" } ) ; +function BigRat.add( brat1 , brat2 , brat3 ) + brat3.den = brat1.den * brat2.den ; + brat3.num = ( brat1.num * brat2.den ) + ( brat2.num * brat1.den ) ; + return brat3 ; +end +function BigRat.sub( brat1 , brat2 , brat3 ) + brat3.den = brat1.den * brat2.den ; + brat3.num = ( brat1.num * brat2.den ) - ( brat2.num * brat1.den ) ; + return brat3 ; +end + +function BigRat.mul( brat1 , brat2 , brat3 ) + brat3.num = brat1.num * brat2.num ; + brat3.den = brat1.den * brat2.den ; + return 0 ; +end + +function BigRat.div( brat1 , brat2 , brat3 ) + brat3.num = brat1.num * brat2.den ; + brat3.den = brat1.den * brat2.num ; + return brat3 ; +end + +function BigRat.pow( bnum1 , bnum2 ) + if bnum1 == nil or bnum2 == nil then + error( "Function BigRat.pow: parameter nil" ) ; + end + local x = BigRat.new( "8" ) ; + local n = BigRat.new( bnum2.den ) ; + local n2 ; + local y = BigRat.new( ) ; + local i ; + local temp = BigRat.new( ) ; + + BigRat.simplify( bnum2 ) ; + temp.num = BigNum.exp( bnum1.num , bnum2.num ) ; + temp.den = BigNum.exp( bnum1.den , bnum2.num ) ; + n2 = n - 1 ; + + for i = 0 , 4 do + y.num = x.num ^ n2.num ; + y.den = x.den ^ n2.num ; + x = (( temp / y ) + ( n2 * x )) / n ; + end + return x ; +end + +function BigRat.simplify( brat ) + if brat == nil then + error( "Function BigRat.simplify: parameter nil" ) ; + end + local gcd = BigNum.new( ) ; + local temp = BigRat.new( brat ) ; + local devnull = BigNum.new( ) ; + local zero = BigNum.new( "0" ) ; + --Check if numerator is zero + if BigNum.compareAbs( brat.num , zero ) == 0 then + brat.den = BigNum.new( "1" ) ; + return 0 ; + end + gcd = BigNum.gcd( brat.num , brat.den ) ; + BigNum.div( temp.num , gcd , brat.num , devnull ) ; + BigNum.div( temp.den , gcd , brat.den , devnull ) ; + --Update the signal + if brat.num.signal == '-' and brat.den.signal == '-' then + brat.num.signal = '+' ; + brat.den.signal = '+' ; + end + return 0 ; +end + +function BigRat.eq( brat1 , brat2 ) + if BigRat.compare( brat1 , brat2 ) == 0 then + return true ; + else + return false ; + end +end + +function BigRat.lt( brat1 , brat2 ) + if BigRat.compare( brat1 , brat2 ) == 2 then + return true ; + else + return false ; + end +end + +function BigRat.le( brat1 , brat2 ) + local temp = -1 ; + temp = BigRat.compare( brat1 , brat2 ) + if temp == 0 or temp == 2 then + return true ; + else + return false ; + end +end + +function BigRat.compare( bnum1 , bnum2 ) + local temp ; + temp = bnum1 - bnum2 ; + if temp.num[0] == 0 and temp.num.len == 1 then --Check if is zero + return 0 ; + elseif temp.num.signal == "-" then + return 2 ; + else + return 1 ; + end +end diff --git a/bin/bin/numbers/no_jit_bit.lua b/bin/bin/numbers/no_jit_bit.lua new file mode 100644 index 0000000..2cfdc1d --- /dev/null +++ b/bin/bin/numbers/no_jit_bit.lua @@ -0,0 +1,333 @@ +--[[ +LICENSE + + (c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT). +--]] + +local M = {_TYPE='module', _NAME='bit.numberlua', _VERSION='0.3.1.20120131'} + +local floor = math.floor + +local MOD = 2^32 +local MODM = MOD-1 + +local function memoize(f) + local mt = {} + local t = setmetatable({}, mt) + function mt:__index(k) + local v = f(k); t[k] = v + return v + end + return t +end + +local function make_bitop_uncached(t, m) + local function bitop(a, b) + local res,p = 0,1 + while a ~= 0 and b ~= 0 do + local am, bm = a%m, b%m + res = res + t[am][bm]*p + a = (a - am) / m + b = (b - bm) / m + p = p*m + end + res = res + (a+b)*p + return res + end + return bitop +end + +local function make_bitop(t) + local op1 = make_bitop_uncached(t,2^1) + local op2 = memoize(function(a) + return memoize(function(b) + return op1(a, b) + end) + end) + return make_bitop_uncached(op2, 2^(t.n or 1)) +end + +-- ok? probably not if running on a 32-bit int Lua number type platform +function M.tobit(x) + return x % 2^32 +end + +M.bxor = make_bitop {[0]={[0]=0,[1]=1},[1]={[0]=1,[1]=0}, n=4} +local bxor = M.bxor + +function M.bnot(a) return MODM - a end +local bnot = M.bnot + +function M.band(a,b) return ((a+b) - bxor(a,b))/2 end +local band = M.band + +function M.bor(a,b) return MODM - band(MODM - a, MODM - b) end +local bor = M.bor + +local lshift, rshift -- forward declare + +function M.rshift(a,disp) -- Lua5.2 insipred + if disp < 0 then return lshift(a,-disp) end + return floor(a % 2^32 / 2^disp) +end +rshift = M.rshift + +function M.lshift(a,disp) -- Lua5.2 inspired + if disp < 0 then return rshift(a,-disp) end + return (a * 2^disp) % 2^32 +end +lshift = M.lshift + +function M.tohex(x, n) -- BitOp style + n = n or 8 + local up + if n <= 0 then + if n == 0 then return '' end + up = true + n = - n + end + x = band(x, 16^n-1) + return ('%0'..n..(up and 'X' or 'x')):format(x) +end +local tohex = M.tohex + +function M.extract(n, field, width) -- Lua5.2 inspired + width = width or 1 + return band(rshift(n, field), 2^width-1) +end +local extract = M.extract + +function M.replace(n, v, field, width) -- Lua5.2 inspired + width = width or 1 + local mask1 = 2^width-1 + v = band(v, mask1) -- required by spec? + local mask = bnot(lshift(mask1, field)) + return band(n, mask) + lshift(v, field) +end +local replace = M.replace + +function M.bswap(x) -- BitOp style + local a = band(x, 0xff); x = rshift(x, 8) + local b = band(x, 0xff); x = rshift(x, 8) + local c = band(x, 0xff); x = rshift(x, 8) + local d = band(x, 0xff) + return lshift(lshift(lshift(a, 8) + b, 8) + c, 8) + d +end +local bswap = M.bswap + +function M.rrotate(x, disp) -- Lua5.2 inspired + disp = disp % 32 + local low = band(x, 2^disp-1) + return rshift(x, disp) + lshift(low, 32-disp) +end +local rrotate = M.rrotate + +function M.lrotate(x, disp) -- Lua5.2 inspired + return rrotate(x, -disp) +end +local lrotate = M.lrotate + +M.rol = M.lrotate -- LuaOp inspired +M.ror = M.rrotate -- LuaOp insipred + + +function M.arshift(x, disp) -- Lua5.2 inspired + local z = rshift(x, disp) + if x >= 0x80000000 then z = z + lshift(2^disp-1, 32-disp) end + return z +end +local arshift = M.arshift + +function M.btest(x, y) -- Lua5.2 inspired + return band(x, y) ~= 0 +end + +M.bit32 = {} -- Lua 5.2 'bit32' compatibility + + +local function bit32_bnot(x) + return (-1 - x) % MOD +end +M.bit32.bnot = bit32_bnot + +local function bit32_bxor(a, b, c, ...) + local z + if b then + a = a % MOD + b = b % MOD + z = bxor(a, b) + if c then + z = bit32_bxor(z, c, ...) + end + return z + elseif a then + return a % MOD + else + return 0 + end +end +M.bit32.bxor = bit32_bxor + +local function bit32_band(a, b, c, ...) + local z + if b then + a = a % MOD + b = b % MOD + z = ((a+b) - bxor(a,b)) / 2 + if c then + z = bit32_band(z, c, ...) + end + return z + elseif a then + return a % MOD + else + return MODM + end +end +M.bit32.band = bit32_band + +local function bit32_bor(a, b, c, ...) + local z + if b then + a = a % MOD + b = b % MOD + z = MODM - band(MODM - a, MODM - b) + if c then + z = bit32_bor(z, c, ...) + end + return z + elseif a then + return a % MOD + else + return 0 + end +end +M.bit32.bor = bit32_bor + +function M.bit32.btest(...) + return bit32_band(...) ~= 0 +end + +function M.bit32.lrotate(x, disp) + return lrotate(x % MOD, disp) +end + +function M.bit32.rrotate(x, disp) + return rrotate(x % MOD, disp) +end + +function M.bit32.lshift(x,disp) + if disp > 31 or disp < -31 then return 0 end + return lshift(x % MOD, disp) +end + +function M.bit32.rshift(x,disp) + if disp > 31 or disp < -31 then return 0 end + return rshift(x % MOD, disp) +end + +function M.bit32.arshift(x,disp) + x = x % MOD + if disp >= 0 then + if disp > 31 then + return (x >= 0x80000000) and MODM or 0 + else + local z = rshift(x, disp) + if x >= 0x80000000 then z = z + lshift(2^disp-1, 32-disp) end + return z + end + else + return lshift(x, -disp) + end +end + +function M.bit32.extract(x, field, ...) + local width = ... or 1 + if field < 0 or field > 31 or width < 0 or field+width > 32 then error 'out of range' end + x = x % MOD + return extract(x, field, ...) +end + +function M.bit32.replace(x, v, field, ...) + local width = ... or 1 + if field < 0 or field > 31 or width < 0 or field+width > 32 then error 'out of range' end + x = x % MOD + v = v % MOD + return replace(x, v, field, ...) +end + +M.bit = {} -- LuaBitOp "bit" compatibility + +function M.bit.tobit(x) + x = x % MOD + if x >= 0x80000000 then x = x - MOD end + return x +end +local bit_tobit = M.bit.tobit + +function M.bit.tohex(x, ...) + return tohex(x % MOD, ...) +end + +function M.bit.bnot(x) + return bit_tobit(bnot(x % MOD)) +end + +local function bit_bor(a, b, c, ...) + if c then + return bit_bor(bit_bor(a, b), c, ...) + elseif b then + return bit_tobit(bor(a % MOD, b % MOD)) + else + return bit_tobit(a) + end +end +M.bit.bor = bit_bor + +local function bit_band(a, b, c, ...) + if c then + return bit_band(bit_band(a, b), c, ...) + elseif b then + return bit_tobit(band(a % MOD, b % MOD)) + else + return bit_tobit(a) + end +end +M.bit.band = bit_band + +local function bit_bxor(a, b, c, ...) + if c then + return bit_bxor(bit_bxor(a, b), c, ...) + elseif b then + return bit_tobit(bxor(a % MOD, b % MOD)) + else + return bit_tobit(a) + end +end +M.bit.bxor = bit_bxor + +function M.bit.lshift(x, n) + return bit_tobit(lshift(x % MOD, n % 32)) +end + +function M.bit.rshift(x, n) + return bit_tobit(rshift(x % MOD, n % 32)) +end + +function M.bit.arshift(x, n) + return bit_tobit(arshift(x % MOD, n % 32)) +end + +function M.bit.rol(x, n) + return bit_tobit(lrotate(x % MOD, n % 32)) +end + +function M.bit.ror(x, n) + return bit_tobit(rrotate(x % MOD, n % 32)) +end + +function M.bit.bswap(x) + return bit_tobit(bswap(x % MOD)) +end + +return M diff --git a/bin/bin/numbers/random.lua b/bin/bin/numbers/random.lua new file mode 100644 index 0000000..091a7f5 --- /dev/null +++ b/bin/bin/numbers/random.lua @@ -0,0 +1,232 @@ +--[[---------------------------------------- +Random +Not all of this is mine +------------------------------------------]] +--[[------------------------------------ +RandomLua v0.3.1 +Pure Lua Pseudo-Random Numbers Generator +Under the MIT license. +copyright(c) 2011 linux-man +--]]------------------------------------ + +local math_floor = math.floor + +local function normalize(n) + return n % 0x80000000 +end + +local function bit_and(a, b) + local r = 0 + local m = 0 + for m = 0, 31 do + if (a % 2 == 1) and (b % 2 == 1) then r = r + 2^m end + if a % 2 ~= 0 then a = a - 1 end + if b % 2 ~= 0 then b = b - 1 end + a = a / 2 b = b / 2 + end + return normalize(r) +end + +local function bit_or(a, b) + local r = 0 + local m = 0 + for m = 0, 31 do + if (a % 2 == 1) or (b % 2 == 1) then r = r + 2^m end + if a % 2 ~= 0 then a = a - 1 end + if b % 2 ~= 0 then b = b - 1 end + a = a / 2 b = b / 2 + end + return normalize(r) +end + +local function bit_xor(a, b) + local r = 0 + local m = 0 + for m = 0, 31 do + if a % 2 ~= b % 2 then r = r + 2^m end + if a % 2 ~= 0 then a = a - 1 end + if b % 2 ~= 0 then b = b - 1 end + a = a / 2 b = b / 2 + end + return normalize(r) +end + +local function seed() + return normalize(os.time()) +end + +--Mersenne twister +local mersenne_twister = {} +mersenne_twister.__index = mersenne_twister + +function mersenne_twister:randomseed(s) + if not s then s = seed() end + self.mt[0] = normalize(s) + for i = 1, 623 do + self.mt[i] = normalize(0x6c078965 * bit_xor(self.mt[i-1], math_floor(self.mt[i-1] / 0x40000000)) + i) + end +end + +function mersenne_twister:random(a, b) + local y + if self.index == 0 then + for i = 0, 623 do + y = self.mt[(i + 1) % 624] % 0x80000000 + self.mt[i] = bit_xor(self.mt[(i + 397) % 624], math_floor(y / 2)) + if y % 2 ~= 0 then self.mt[i] = bit_xor(self.mt[i], 0x9908b0df) end + end + end + y = self.mt[self.index] + y = bit_xor(y, math_floor(y / 0x800)) + y = bit_xor(y, bit_and(normalize(y * 0x80), 0x9d2c5680)) + y = bit_xor(y, bit_and(normalize(y * 0x8000), 0xefc60000)) + y = bit_xor(y, math_floor(y / 0x40000)) + self.index = (self.index + 1) % 624 + if not a then return y / 0x80000000 + elseif not b then + if a == 0 then return y + else return 1 + (y % a) + end + else + return a + (y % (b - a + 1)) + end +end + +local function twister(s) + local temp = {} + setmetatable(temp, mersenne_twister) + temp.mt = {} + temp.index = 0 + temp:randomseed(s) + return temp +end + +--Linear Congruential Generator +local linear_congruential_generator = {} +linear_congruential_generator.__index = linear_congruential_generator + +function linear_congruential_generator:random(a, b) + local y = (self.a * self.x + self.c) % self.m + self.x = y + if not a then return y / 0x10000 + elseif not b then + if a == 0 then return y + else return 1 + (y % a) end + else + return a + (y % (b - a + 1)) + end +end + +function linear_congruential_generator:randomseed(s) + if not s then s = seed() end + self.x = normalize(s) +end + +local function lcg(s, r) + local temp = {} + setmetatable(temp, linear_congruential_generator) + temp.a, temp.c, temp.m = 1103515245, 12345, 0x10000 --from Ansi C + if r then + if r == 'nr' then temp.a, temp.c, temp.m = 1664525, 1013904223, 0x10000 --from Numerical Recipes. + elseif r == 'mvc' then temp.a, temp.c, temp.m = 214013, 2531011, 0x10000 end--from MVC + end + temp:randomseed(s) + return temp +end + +-- Multiply-with-carry +local multiply_with_carry = {} +multiply_with_carry.__index = multiply_with_carry + +function multiply_with_carry:random(a, b) + local m = self.m + local t = self.a * self.x + self.c + local y = t % m + self.x = y + self.c = math_floor(t / m) + if not a then return y / 0x10000 + elseif not b then + if a == 0 then return y + else return 1 + (y % a) end + else + return a + (y % (b - a + 1)) + end +end + +function multiply_with_carry:randomseed(s) + if not s then s = seed() end + self.c = self.ic + self.x = normalize(s) +end + +local function mwc(s, r) + local temp = {} + setmetatable(temp, multiply_with_carry) + temp.a, temp.c, temp.m = 1103515245, 12345, 0x10000 --from Ansi C + if r then + if r == 'nr' then temp.a, temp.c, temp.m = 1664525, 1013904223, 0x10000 --from Numerical Recipes. + elseif r == 'mvc' then temp.a, temp.c, temp.m = 214013, 2531011, 0x10000 end--from MVC + end + temp.ic = temp.c + temp:randomseed(s) + return temp +end +-- Little bind for the methods: My code starts +local randomGen={} +randomGen.__index=randomGen +function randomGen:new(s) + local temp={} + setmetatable(temp,randomGen) + temp[1]=twister() + temp[2]=lcg() + temp[3]=mwc() + temp.pos=1 + for i=1,3 do + temp[i]:randomseed(s) + end + return temp +end +function randomGen:randomseed(s) + self.pos=1 + self[1]:randomseed(s) + self[2]:randomseed(s) + self[3]:randomseed(s) +end +function randomGen:randomInt(a,b) + local t=self[self.pos]:random(a,b) + self.pos=self.pos+1 + if self.pos>3 then + self.pos=1 + end + return t +end +function randomGen:newND(a,b,s) + if not(a) or not(b) then error('You must include a range!') end + local temp=randomGen:new(s) + temp.a=a + temp.b=b + temp.range=b-a+1 + temp.dups={no=0} + function temp:nextInt() + local t=self:randomInt(self.a,self.b) + if self.dups[t]==nil then + self.dups[t]=true + self.dups.no=self.dups.no+1 + else + return self:nextInt() + end + if self.dups.no==self.range then + function self:nextInt() + return 1,true + end + return t + else + return t + end + end + function temp:nextIInt() + return function() return self:nextInt() end + end + return temp +end +return randomGen diff --git a/bin/bin/support/vfs.lua b/bin/bin/support/vfs.lua new file mode 100644 index 0000000..dae0b43 --- /dev/null +++ b/bin/bin/support/vfs.lua @@ -0,0 +1 @@ +local bin = require("bin") \ No newline at end of file diff --git a/bin/freetype6.dll b/bin/freetype6.dll new file mode 100644 index 0000000..f0f5cf7 Binary files /dev/null and b/bin/freetype6.dll differ diff --git a/bin/gd.dll b/bin/gd.dll new file mode 100644 index 0000000..db6add4 Binary files /dev/null and b/bin/gd.dll differ diff --git a/bin/jpeg62.dll b/bin/jpeg62.dll new file mode 100644 index 0000000..21c03d5 Binary files /dev/null and b/bin/jpeg62.dll differ diff --git a/bin/json.lua b/bin/json.lua new file mode 100644 index 0000000..47d2192 --- /dev/null +++ b/bin/json.lua @@ -0,0 +1,1442 @@ +-- -*- coding: utf-8 -*- +-- +-- Simple JSON encoding and decoding in pure Lua. +-- +-- Copyright 2010-2016 Jeffrey Friedl +-- http://regex.info/blog/ +-- Latest version: http://regex.info/blog/lua/json +-- +-- This code is released under a Creative Commons CC-BY "Attribution" License: +-- http://creativecommons.org/licenses/by/3.0/deed.en_US +-- +-- It can be used for any purpose so long as: +-- 1) the copyright notice above is maintained +-- 2) the web-page links above are maintained +-- 3) the 'AUTHOR_NOTE' string below is maintained +-- +local VERSION = '20161109.21' -- version history at end of file +local AUTHOR_NOTE = "-[ JSON.lua package by Jeffrey Friedl (http://regex.info/blog/lua/json) version 20161109.21 ]-" + +-- +-- The 'AUTHOR_NOTE' variable exists so that information about the source +-- of the package is maintained even in compiled versions. It's also +-- included in OBJDEF below mostly to quiet warnings about unused variables. +-- +local OBJDEF = { + VERSION = VERSION, + AUTHOR_NOTE = AUTHOR_NOTE, +} + + +-- +-- Simple JSON encoding and decoding in pure Lua. +-- JSON definition: http://www.json.org/ +-- +-- +-- JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines +-- +-- local lua_value = JSON:decode(raw_json_text) +-- +-- local raw_json_text = JSON:encode(lua_table_or_value) +-- local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability +-- +-- +-- +-- DECODING (from a JSON string to a Lua table) +-- +-- +-- JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines +-- +-- local lua_value = JSON:decode(raw_json_text) +-- +-- If the JSON text is for an object or an array, e.g. +-- { "what": "books", "count": 3 } +-- or +-- [ "Larry", "Curly", "Moe" ] +-- +-- the result is a Lua table, e.g. +-- { what = "books", count = 3 } +-- or +-- { "Larry", "Curly", "Moe" } +-- +-- +-- The encode and decode routines accept an optional second argument, +-- "etc", which is not used during encoding or decoding, but upon error +-- is passed along to error handlers. It can be of any type (including nil). +-- +-- +-- +-- ERROR HANDLING +-- +-- With most errors during decoding, this code calls +-- +-- JSON:onDecodeError(message, text, location, etc) +-- +-- with a message about the error, and if known, the JSON text being +-- parsed and the byte count where the problem was discovered. You can +-- replace the default JSON:onDecodeError() with your own function. +-- +-- The default onDecodeError() merely augments the message with data +-- about the text and the location if known (and if a second 'etc' +-- argument had been provided to decode(), its value is tacked onto the +-- message as well), and then calls JSON.assert(), which itself defaults +-- to Lua's built-in assert(), and can also be overridden. +-- +-- For example, in an Adobe Lightroom plugin, you might use something like +-- +-- function JSON:onDecodeError(message, text, location, etc) +-- LrErrors.throwUserError("Internal Error: invalid JSON data") +-- end +-- +-- or even just +-- +-- function JSON.assert(message) +-- LrErrors.throwUserError("Internal Error: " .. message) +-- end +-- +-- If JSON:decode() is passed a nil, this is called instead: +-- +-- JSON:onDecodeOfNilError(message, nil, nil, etc) +-- +-- and if JSON:decode() is passed HTML instead of JSON, this is called: +-- +-- JSON:onDecodeOfHTMLError(message, text, nil, etc) +-- +-- The use of the fourth 'etc' argument allows stronger coordination +-- between decoding and error reporting, especially when you provide your +-- own error-handling routines. Continuing with the the Adobe Lightroom +-- plugin example: +-- +-- function JSON:onDecodeError(message, text, location, etc) +-- local note = "Internal Error: invalid JSON data" +-- if type(etc) = 'table' and etc.photo then +-- note = note .. " while processing for " .. etc.photo:getFormattedMetadata('fileName') +-- end +-- LrErrors.throwUserError(note) +-- end +-- +-- : +-- : +-- +-- for i, photo in ipairs(photosToProcess) do +-- : +-- : +-- local data = JSON:decode(someJsonText, { photo = photo }) +-- : +-- : +-- end +-- +-- +-- +-- If the JSON text passed to decode() has trailing garbage (e.g. as with the JSON "[123]xyzzy"), +-- the method +-- +-- JSON:onTrailingGarbage(json_text, location, parsed_value, etc) +-- +-- is invoked, where: +-- +-- json_text is the original JSON text being parsed, +-- location is the count of bytes into json_text where the garbage starts (6 in the example), +-- parsed_value is the Lua result of what was successfully parsed ({123} in the example), +-- etc is as above. +-- +-- If JSON:onTrailingGarbage() does not abort, it should return the value decode() should return, +-- or nil + an error message. +-- +-- local new_value, error_message = JSON:onTrailingGarbage() +-- +-- The default handler just invokes JSON:onDecodeError("trailing garbage"...), but you can have +-- this package ignore trailing garbage via +-- +-- function JSON:onTrailingGarbage(json_text, location, parsed_value, etc) +-- return parsed_value +-- end +-- +-- +-- DECODING AND STRICT TYPES +-- +-- Because both JSON objects and JSON arrays are converted to Lua tables, +-- it's not normally possible to tell which original JSON type a +-- particular Lua table was derived from, or guarantee decode-encode +-- round-trip equivalency. +-- +-- However, if you enable strictTypes, e.g. +-- +-- JSON = assert(loadfile "JSON.lua")() --load the routines +-- JSON.strictTypes = true +-- +-- then the Lua table resulting from the decoding of a JSON object or +-- JSON array is marked via Lua metatable, so that when re-encoded with +-- JSON:encode() it ends up as the appropriate JSON type. +-- +-- (This is not the default because other routines may not work well with +-- tables that have a metatable set, for example, Lightroom API calls.) +-- +-- +-- ENCODING (from a lua table to a JSON string) +-- +-- JSON = assert(loadfile "JSON.lua")() -- one-time load of the routines +-- +-- local raw_json_text = JSON:encode(lua_table_or_value) +-- local pretty_json_text = JSON:encode_pretty(lua_table_or_value) -- "pretty printed" version for human readability +-- local custom_pretty = JSON:encode(lua_table_or_value, etc, { pretty = true, indent = "| ", align_keys = false }) +-- +-- On error during encoding, this code calls: +-- +-- JSON:onEncodeError(message, etc) +-- +-- which you can override in your local JSON object. +-- +-- The 'etc' in the error call is the second argument to encode() +-- and encode_pretty(), or nil if it wasn't provided. +-- +-- +-- ENCODING OPTIONS +-- +-- An optional third argument, a table of options, can be provided to encode(). +-- +-- encode_options = { +-- -- options for making "pretty" human-readable JSON (see "PRETTY-PRINTING" below) +-- pretty = true, +-- indent = " ", +-- align_keys = false, +-- array_newline = false, +-- +-- -- other output-related options +-- null = "\0", -- see "ENCODING JSON NULL VALUES" below +-- stringsAreUtf8 = false, -- see "HANDLING UNICODE LINE AND PARAGRAPH SEPARATORS FOR JAVA" below +-- } +-- +-- json_string = JSON:encode(mytable, etc, encode_options) +-- +-- +-- +-- For reference, the defaults are: +-- +-- pretty = false +-- null = nil, +-- stringsAreUtf8 = false, +-- array_newline = false, +-- +-- +-- +-- PRETTY-PRINTING +-- +-- Enabling the 'pretty' encode option helps generate human-readable JSON. +-- +-- pretty = JSON:encode(val, etc, { +-- pretty = true, +-- indent = " ", +-- align_keys = false, +-- }) +-- +-- encode_pretty() is also provided: it's identical to encode() except +-- that encode_pretty() provides a default options table if none given in the call: +-- +-- { pretty = true, align_keys = false, indent = " " } +-- +-- For example, if +-- +-- JSON:encode(data) +-- +-- produces: +-- +-- {"city":"Kyoto","climate":{"avg_temp":16,"humidity":"high","snowfall":"minimal"},"country":"Japan","wards":11} +-- +-- then +-- +-- JSON:encode_pretty(data) +-- +-- produces: +-- +-- { +-- "city": "Kyoto", +-- "climate": { +-- "avg_temp": 16, +-- "humidity": "high", +-- "snowfall": "minimal" +-- }, +-- "country": "Japan", +-- "wards": 11 +-- } +-- +-- The following three lines return identical results: +-- JSON:encode_pretty(data) +-- JSON:encode_pretty(data, nil, { pretty = true, align_keys = false, indent = " " }) +-- JSON:encode (data, nil, { pretty = true, align_keys = false, indent = " " }) +-- +-- An example of setting your own indent string: +-- +-- JSON:encode_pretty(data, nil, { pretty = true, indent = "| " }) +-- +-- produces: +-- +-- { +-- | "city": "Kyoto", +-- | "climate": { +-- | | "avg_temp": 16, +-- | | "humidity": "high", +-- | | "snowfall": "minimal" +-- | }, +-- | "country": "Japan", +-- | "wards": 11 +-- } +-- +-- An example of setting align_keys to true: +-- +-- JSON:encode_pretty(data, nil, { pretty = true, indent = " ", align_keys = true }) +-- +-- produces: +-- +-- { +-- "city": "Kyoto", +-- "climate": { +-- "avg_temp": 16, +-- "humidity": "high", +-- "snowfall": "minimal" +-- }, +-- "country": "Japan", +-- "wards": 11 +-- } +-- +-- which I must admit is kinda ugly, sorry. This was the default for +-- encode_pretty() prior to version 20141223.14. +-- +-- +-- HANDLING UNICODE LINE AND PARAGRAPH SEPARATORS FOR JAVA +-- +-- If the 'stringsAreUtf8' encode option is set to true, consider Lua strings not as a sequence of bytes, +-- but as a sequence of UTF-8 characters. +-- +-- Currently, the only practical effect of setting this option is that Unicode LINE and PARAGRAPH +-- separators, if found in a string, are encoded with a JSON escape instead of being dumped as is. +-- The JSON is valid either way, but encoding this way, apparently, allows the resulting JSON +-- to also be valid Java. +-- +-- AMBIGUOUS SITUATIONS DURING THE ENCODING +-- +-- During the encode, if a Lua table being encoded contains both string +-- and numeric keys, it fits neither JSON's idea of an object, nor its +-- idea of an array. To get around this, when any string key exists (or +-- when non-positive numeric keys exist), numeric keys are converted to +-- strings. +-- +-- For example, +-- JSON:encode({ "one", "two", "three", SOMESTRING = "some string" })) +-- produces the JSON object +-- {"1":"one","2":"two","3":"three","SOMESTRING":"some string"} +-- +-- To prohibit this conversion and instead make it an error condition, set +-- JSON.noKeyConversion = true +-- +-- +-- ENCODING JSON NULL VALUES +-- +-- Lua tables completely omit keys whose value is nil, so without special handling there's +-- no way to get a field in a JSON object with a null value. For example +-- JSON:encode({ username = "admin", password = nil }) +-- produces +-- {"username":"admin"} +-- +-- In order to actually produce +-- {"username":"admin", "password":null} +-- one can include a string value for a "null" field in the options table passed to encode().... +-- any Lua table entry with that value becomes null in the JSON output: +-- JSON:encode({ username = "admin", password = "xyzzy" }, nil, { null = "xyzzy" }) +-- produces +-- {"username":"admin", "password":null} +-- +-- Just be sure to use a string that is otherwise unlikely to appear in your data. +-- The string "\0" (a string with one null byte) may well be appropriate for many applications. +-- +-- The "null" options also applies to Lua tables that become JSON arrays. +-- JSON:encode({ "one", "two", nil, nil }) +-- produces +-- ["one","two"] +-- while +-- NULL = "\0" +-- JSON:encode({ "one", "two", NULL, NULL}, nil, { null = NULL }) +-- produces +-- ["one","two",null,null] +-- +-- +-- +-- +-- HANDLING LARGE AND/OR PRECISE NUMBERS +-- +-- +-- Without special handling, numbers in JSON can lose precision in Lua. +-- For example: +-- +-- T = JSON:decode('{ "small":12345, "big":12345678901234567890123456789, "precise":9876.67890123456789012345 }') +-- +-- print("small: ", type(T.small), T.small) +-- print("big: ", type(T.big), T.big) +-- print("precise: ", type(T.precise), T.precise) +-- +-- produces +-- +-- small: number 12345 +-- big: number 1.2345678901235e+28 +-- precise: number 9876.6789012346 +-- +-- Precision is lost with both 'big' and 'precise'. +-- +-- This package offers ways to try to handle this better (for some definitions of "better")... +-- +-- The most precise method is by setting the global: +-- +-- JSON.decodeNumbersAsObjects = true +-- +-- When this is set, numeric JSON data is encoded into Lua in a form that preserves the exact +-- JSON numeric presentation when re-encoded back out to JSON, or accessed in Lua as a string. +-- +-- (This is done by encoding the numeric data with a Lua table/metatable that returns +-- the possibly-imprecise numeric form when accessed numerically, but the original precise +-- representation when accessed as a string. You can also explicitly access +-- via JSON:forceString() and JSON:forceNumber()) +-- +-- Consider the example above, with this option turned on: +-- +-- JSON.decodeNumbersAsObjects = true +-- +-- T = JSON:decode('{ "small":12345, "big":12345678901234567890123456789, "precise":9876.67890123456789012345 }') +-- +-- print("small: ", type(T.small), T.small) +-- print("big: ", type(T.big), T.big) +-- print("precise: ", type(T.precise), T.precise) +-- +-- This now produces: +-- +-- small: table 12345 +-- big: table 12345678901234567890123456789 +-- precise: table 9876.67890123456789012345 +-- +-- However, within Lua you can still use the values (e.g. T.precise in the example above) in numeric +-- contexts. In such cases you'll get the possibly-imprecise numeric version, but in string contexts +-- and when the data finds its way to this package's encode() function, the original full-precision +-- representation is used. +-- +-- Even without using the JSON.decodeNumbersAsObjects option, you can encode numbers +-- in your Lua table that retain high precision upon encoding to JSON, by using the JSON:asNumber() +-- function: +-- +-- T = { +-- imprecise = 123456789123456789.123456789123456789, +-- precise = JSON:asNumber("123456789123456789.123456789123456789") +-- } +-- +-- print(JSON:encode_pretty(T)) +-- +-- This produces: +-- +-- { +-- "precise": 123456789123456789.123456789123456789, +-- "imprecise": 1.2345678912346e+17 +-- } +-- +-- +-- +-- A different way to handle big/precise JSON numbers is to have decode() merely return +-- the exact string representation of the number instead of the number itself. +-- This approach might be useful when the numbers are merely some kind of opaque +-- object identifier and you want to work with them in Lua as strings anyway. +-- +-- This approach is enabled by setting +-- +-- JSON.decodeIntegerStringificationLength = 10 +-- +-- The value is the number of digits (of the integer part of the number) at which to stringify numbers. +-- +-- Consider our previous example with this option set to 10: +-- +-- JSON.decodeIntegerStringificationLength = 10 +-- +-- T = JSON:decode('{ "small":12345, "big":12345678901234567890123456789, "precise":9876.67890123456789012345 }') +-- +-- print("small: ", type(T.small), T.small) +-- print("big: ", type(T.big), T.big) +-- print("precise: ", type(T.precise), T.precise) +-- +-- This produces: +-- +-- small: number 12345 +-- big: string 12345678901234567890123456789 +-- precise: number 9876.6789012346 +-- +-- The long integer of the 'big' field is at least JSON.decodeIntegerStringificationLength digits +-- in length, so it's converted not to a Lua integer but to a Lua string. Using a value of 0 or 1 ensures +-- that all JSON numeric data becomes strings in Lua. +-- +-- Note that unlike +-- JSON.decodeNumbersAsObjects = true +-- this stringification is simple and unintelligent: the JSON number simply becomes a Lua string, and that's the end of it. +-- If the string is then converted back to JSON, it's still a string. After running the code above, adding +-- print(JSON:encode(T)) +-- produces +-- {"big":"12345678901234567890123456789","precise":9876.6789012346,"small":12345} +-- which is unlikely to be desired. +-- +-- There's a comparable option for the length of the decimal part of a number: +-- +-- JSON.decodeDecimalStringificationLength +-- +-- This can be used alone or in conjunction with +-- +-- JSON.decodeIntegerStringificationLength +-- +-- to trip stringification on precise numbers with at least JSON.decodeIntegerStringificationLength digits after +-- the decimal point. +-- +-- This example: +-- +-- JSON.decodeIntegerStringificationLength = 10 +-- JSON.decodeDecimalStringificationLength = 5 +-- +-- T = JSON:decode('{ "small":12345, "big":12345678901234567890123456789, "precise":9876.67890123456789012345 }') +-- +-- print("small: ", type(T.small), T.small) +-- print("big: ", type(T.big), T.big) +-- print("precise: ", type(T.precise), T.precise) +-- +-- produces: +-- +-- small: number 12345 +-- big: string 12345678901234567890123456789 +-- precise: string 9876.67890123456789012345 +-- +-- +-- +-- +-- +-- SUMMARY OF METHODS YOU CAN OVERRIDE IN YOUR LOCAL LUA JSON OBJECT +-- +-- assert +-- onDecodeError +-- onDecodeOfNilError +-- onDecodeOfHTMLError +-- onTrailingGarbage +-- onEncodeError +-- +-- If you want to create a separate Lua JSON object with its own error handlers, +-- you can reload JSON.lua or use the :new() method. +-- +--------------------------------------------------------------------------- + +local default_pretty_indent = " " +local default_pretty_options = { pretty = true, align_keys = false, indent = default_pretty_indent } + +local isArray = { __tostring = function() return "JSON array" end } isArray.__index = isArray +local isObject = { __tostring = function() return "JSON object" end } isObject.__index = isObject + +function OBJDEF:newArray(tbl) + return setmetatable(tbl or {}, isArray) +end + +function OBJDEF:newObject(tbl) + return setmetatable(tbl or {}, isObject) +end + + + + +local function getnum(op) + return type(op) == 'number' and op or op.N +end + +local isNumber = { + __tostring = function(T) return T.S end, + __unm = function(op) return getnum(op) end, + + __concat = function(op1, op2) return tostring(op1) .. tostring(op2) end, + __add = function(op1, op2) return getnum(op1) + getnum(op2) end, + __sub = function(op1, op2) return getnum(op1) - getnum(op2) end, + __mul = function(op1, op2) return getnum(op1) * getnum(op2) end, + __div = function(op1, op2) return getnum(op1) / getnum(op2) end, + __mod = function(op1, op2) return getnum(op1) % getnum(op2) end, + __pow = function(op1, op2) return getnum(op1) ^ getnum(op2) end, + __lt = function(op1, op2) return getnum(op1) < getnum(op2) end, + __eq = function(op1, op2) return getnum(op1) == getnum(op2) end, + __le = function(op1, op2) return getnum(op1) <= getnum(op2) end, +} +isNumber.__index = isNumber + +function OBJDEF:asNumber(item) + + if getmetatable(item) == isNumber then + -- it's already a JSON number object. + return item + elseif type(item) == 'table' and type(item.S) == 'string' and type(item.N) == 'number' then + -- it's a number-object table that lost its metatable, so give it one + return setmetatable(item, isNumber) + else + -- the normal situation... given a number or a string representation of a number.... + local holder = { + S = tostring(item), -- S is the representation of the number as a string, which remains precise + N = tonumber(item), -- N is the number as a Lua number. + } + return setmetatable(holder, isNumber) + end +end + +-- +-- Given an item that might be a normal string or number, or might be an 'isNumber' object defined above, +-- return the string version. This shouldn't be needed often because the 'isNumber' object should autoconvert +-- to a string in most cases, but it's here to allow it to be forced when needed. +-- +function OBJDEF:forceString(item) + if type(item) == 'table' and type(item.S) == 'string' then + return item.S + else + return tostring(item) + end +end + +-- +-- Given an item that might be a normal string or number, or might be an 'isNumber' object defined above, +-- return the numeric version. +-- +function OBJDEF:forceNumber(item) + if type(item) == 'table' and type(item.N) == 'number' then + return item.N + else + return tonumber(item) + end +end + + +local function unicode_codepoint_as_utf8(codepoint) + -- + -- codepoint is a number + -- + if codepoint <= 127 then + return string.char(codepoint) + + elseif codepoint <= 2047 then + -- + -- 110yyyxx 10xxxxxx <-- useful notation from http://en.wikipedia.org/wiki/Utf8 + -- + local highpart = math.floor(codepoint / 0x40) + local lowpart = codepoint - (0x40 * highpart) + return string.char(0xC0 + highpart, + 0x80 + lowpart) + + elseif codepoint <= 65535 then + -- + -- 1110yyyy 10yyyyxx 10xxxxxx + -- + local highpart = math.floor(codepoint / 0x1000) + local remainder = codepoint - 0x1000 * highpart + local midpart = math.floor(remainder / 0x40) + local lowpart = remainder - 0x40 * midpart + + highpart = 0xE0 + highpart + midpart = 0x80 + midpart + lowpart = 0x80 + lowpart + + -- + -- Check for an invalid character (thanks Andy R. at Adobe). + -- See table 3.7, page 93, in http://www.unicode.org/versions/Unicode5.2.0/ch03.pdf#G28070 + -- + if ( highpart == 0xE0 and midpart < 0xA0 ) or + ( highpart == 0xED and midpart > 0x9F ) or + ( highpart == 0xF0 and midpart < 0x90 ) or + ( highpart == 0xF4 and midpart > 0x8F ) + then + return "?" + else + return string.char(highpart, + midpart, + lowpart) + end + + else + -- + -- 11110zzz 10zzyyyy 10yyyyxx 10xxxxxx + -- + local highpart = math.floor(codepoint / 0x40000) + local remainder = codepoint - 0x40000 * highpart + local midA = math.floor(remainder / 0x1000) + remainder = remainder - 0x1000 * midA + local midB = math.floor(remainder / 0x40) + local lowpart = remainder - 0x40 * midB + + return string.char(0xF0 + highpart, + 0x80 + midA, + 0x80 + midB, + 0x80 + lowpart) + end +end + +function OBJDEF:onDecodeError(message, text, location, etc) + if text then + if location then + message = string.format("%s at byte %d of: %s", message, location, text) + else + message = string.format("%s: %s", message, text) + end + end + + if etc ~= nil then + message = message .. " (" .. OBJDEF:encode(etc) .. ")" + end + + if self.assert then + self.assert(false, message) + else + assert(false, message) + end +end + +function OBJDEF:onTrailingGarbage(json_text, location, parsed_value, etc) + return self:onDecodeError("trailing garbage", json_text, location, etc) +end + +OBJDEF.onDecodeOfNilError = OBJDEF.onDecodeError +OBJDEF.onDecodeOfHTMLError = OBJDEF.onDecodeError + +function OBJDEF:onEncodeError(message, etc) + if etc ~= nil then + message = message .. " (" .. OBJDEF:encode(etc) .. ")" + end + + if self.assert then + self.assert(false, message) + else + assert(false, message) + end +end + +local function grok_number(self, text, start, options) + -- + -- Grab the integer part + -- + local integer_part = text:match('^-?[1-9]%d*', start) + or text:match("^-?0", start) + + if not integer_part then + self:onDecodeError("expected number", text, start, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible + end + + local i = start + integer_part:len() + + -- + -- Grab an optional decimal part + -- + local decimal_part = text:match('^%.%d+', i) or "" + + i = i + decimal_part:len() + + -- + -- Grab an optional exponential part + -- + local exponent_part = text:match('^[eE][-+]?%d+', i) or "" + + i = i + exponent_part:len() + + local full_number_text = integer_part .. decimal_part .. exponent_part + + if options.decodeNumbersAsObjects then + return OBJDEF:asNumber(full_number_text), i + end + + -- + -- If we're told to stringify under certain conditions, so do. + -- We punt a bit when there's an exponent by just stringifying no matter what. + -- I suppose we should really look to see whether the exponent is actually big enough one + -- way or the other to trip stringification, but I'll be lazy about it until someone asks. + -- + if (options.decodeIntegerStringificationLength + and + (integer_part:len() >= options.decodeIntegerStringificationLength or exponent_part:len() > 0)) + + or + + (options.decodeDecimalStringificationLength + and + (decimal_part:len() >= options.decodeDecimalStringificationLength or exponent_part:len() > 0)) + then + return full_number_text, i -- this returns the exact string representation seen in the original JSON + end + + + + local as_number = tonumber(full_number_text) + + if not as_number then + self:onDecodeError("bad number", text, start, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible + end + + return as_number, i +end + + +local function grok_string(self, text, start, options) + + if text:sub(start,start) ~= '"' then + self:onDecodeError("expected string's opening quote", text, start, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible + end + + local i = start + 1 -- +1 to bypass the initial quote + local text_len = text:len() + local VALUE = "" + while i <= text_len do + local c = text:sub(i,i) + if c == '"' then + return VALUE, i + 1 + end + if c ~= '\\' then + VALUE = VALUE .. c + i = i + 1 + elseif text:match('^\\b', i) then + VALUE = VALUE .. "\b" + i = i + 2 + elseif text:match('^\\f', i) then + VALUE = VALUE .. "\f" + i = i + 2 + elseif text:match('^\\n', i) then + VALUE = VALUE .. "\n" + i = i + 2 + elseif text:match('^\\r', i) then + VALUE = VALUE .. "\r" + i = i + 2 + elseif text:match('^\\t', i) then + VALUE = VALUE .. "\t" + i = i + 2 + else + local hex = text:match('^\\u([0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i) + if hex then + i = i + 6 -- bypass what we just read + + -- We have a Unicode codepoint. It could be standalone, or if in the proper range and + -- followed by another in a specific range, it'll be a two-code surrogate pair. + local codepoint = tonumber(hex, 16) + if codepoint >= 0xD800 and codepoint <= 0xDBFF then + -- it's a hi surrogate... see whether we have a following low + local lo_surrogate = text:match('^\\u([dD][cdefCDEF][0123456789aAbBcCdDeEfF][0123456789aAbBcCdDeEfF])', i) + if lo_surrogate then + i = i + 6 -- bypass the low surrogate we just read + codepoint = 0x2400 + (codepoint - 0xD800) * 0x400 + tonumber(lo_surrogate, 16) + else + -- not a proper low, so we'll just leave the first codepoint as is and spit it out. + end + end + VALUE = VALUE .. unicode_codepoint_as_utf8(codepoint) + + else + + -- just pass through what's escaped + VALUE = VALUE .. text:match('^\\(.)', i) + i = i + 2 + end + end + end + + self:onDecodeError("unclosed string", text, start, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible +end + +local function skip_whitespace(text, start) + + local _, match_end = text:find("^[ \n\r\t]+", start) -- [http://www.ietf.org/rfc/rfc4627.txt] Section 2 + if match_end then + return match_end + 1 + else + return start + end +end + +local grok_one -- assigned later + +local function grok_object(self, text, start, options) + + if text:sub(start,start) ~= '{' then + self:onDecodeError("expected '{'", text, start, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible + end + + local i = skip_whitespace(text, start + 1) -- +1 to skip the '{' + + local VALUE = self.strictTypes and self:newObject { } or { } + + if text:sub(i,i) == '}' then + return VALUE, i + 1 + end + local text_len = text:len() + while i <= text_len do + local key, new_i = grok_string(self, text, i, options) + + i = skip_whitespace(text, new_i) + + if text:sub(i, i) ~= ':' then + self:onDecodeError("expected colon", text, i, options.etc) + return nil, i -- in case the error method doesn't abort, return something sensible + end + + i = skip_whitespace(text, i + 1) + + local new_val, new_i = grok_one(self, text, i, options) + + VALUE[key] = new_val + + -- + -- Expect now either '}' to end things, or a ',' to allow us to continue. + -- + i = skip_whitespace(text, new_i) + + local c = text:sub(i,i) + + if c == '}' then + return VALUE, i + 1 + end + + if text:sub(i, i) ~= ',' then + self:onDecodeError("expected comma or '}'", text, i, options.etc) + return nil, i -- in case the error method doesn't abort, return something sensible + end + + i = skip_whitespace(text, i + 1) + end + + self:onDecodeError("unclosed '{'", text, start, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible +end + +local function grok_array(self, text, start, options) + if text:sub(start,start) ~= '[' then + self:onDecodeError("expected '['", text, start, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible + end + + local i = skip_whitespace(text, start + 1) -- +1 to skip the '[' + local VALUE = self.strictTypes and self:newArray { } or { } + if text:sub(i,i) == ']' then + return VALUE, i + 1 + end + + local VALUE_INDEX = 1 + + local text_len = text:len() + while i <= text_len do + local val, new_i = grok_one(self, text, i, options) + + -- can't table.insert(VALUE, val) here because it's a no-op if val is nil + VALUE[VALUE_INDEX] = val + VALUE_INDEX = VALUE_INDEX + 1 + + i = skip_whitespace(text, new_i) + + -- + -- Expect now either ']' to end things, or a ',' to allow us to continue. + -- + local c = text:sub(i,i) + if c == ']' then + return VALUE, i + 1 + end + if text:sub(i, i) ~= ',' then + self:onDecodeError("expected comma or ']'", text, i, options.etc) + return nil, i -- in case the error method doesn't abort, return something sensible + end + i = skip_whitespace(text, i + 1) + end + self:onDecodeError("unclosed '['", text, start, options.etc) + return nil, i -- in case the error method doesn't abort, return something sensible +end + + +grok_one = function(self, text, start, options) + -- Skip any whitespace + start = skip_whitespace(text, start) + + if start > text:len() then + self:onDecodeError("unexpected end of string", text, nil, options.etc) + return nil, start -- in case the error method doesn't abort, return something sensible + end + + if text:find('^"', start) then + return grok_string(self, text, start, options.etc) + + elseif text:find('^[-0123456789 ]', start) then + return grok_number(self, text, start, options) + + elseif text:find('^%{', start) then + return grok_object(self, text, start, options) + + elseif text:find('^%[', start) then + return grok_array(self, text, start, options) + + elseif text:find('^true', start) then + return true, start + 4 + + elseif text:find('^false', start) then + return false, start + 5 + + elseif text:find('^null', start) then + return nil, start + 4 + + else + self:onDecodeError("can't parse JSON", text, start, options.etc) + return nil, 1 -- in case the error method doesn't abort, return something sensible + end +end + +function OBJDEF:decode(text, etc, options) + -- + -- If the user didn't pass in a table of decode options, make an empty one. + -- + if type(options) ~= 'table' then + options = {} + end + + -- + -- If they passed in an 'etc' argument, stuff it into the options. + -- (If not, any 'etc' field in the options they passed in remains to be used) + -- + if etc ~= nil then + options.etc = etc + end + + + if type(self) ~= 'table' or self.__index ~= OBJDEF then + local error_message = "JSON:decode must be called in method format" + OBJDEF:onDecodeError(error_message, nil, nil, options.etc) + return nil, error_message -- in case the error method doesn't abort, return something sensible + end + + if text == nil then + local error_message = "nil passed to JSON:decode()" + self:onDecodeOfNilError(error_message, nil, nil, options.etc) + return nil, error_message -- in case the error method doesn't abort, return something sensible + + elseif type(text) ~= 'string' then + local error_message = "expected string argument to JSON:decode()" + self:onDecodeError(string.format("%s, got %s", error_message, type(text)), nil, nil, options.etc) + return nil, error_message -- in case the error method doesn't abort, return something sensible + end + + if text:match('^%s*$') then + -- an empty string is nothing, but not an error + return nil + end + + if text:match('^%s*<') then + -- Can't be JSON... we'll assume it's HTML + local error_message = "HTML passed to JSON:decode()" + self:onDecodeOfHTMLError(error_message, text, nil, options.etc) + return nil, error_message -- in case the error method doesn't abort, return something sensible + end + + -- + -- Ensure that it's not UTF-32 or UTF-16. + -- Those are perfectly valid encodings for JSON (as per RFC 4627 section 3), + -- but this package can't handle them. + -- + if text:sub(1,1):byte() == 0 or (text:len() >= 2 and text:sub(2,2):byte() == 0) then + local error_message = "JSON package groks only UTF-8, sorry" + self:onDecodeError(error_message, text, nil, options.etc) + return nil, error_message -- in case the error method doesn't abort, return something sensible + end + + -- + -- apply global options + -- + if options.decodeNumbersAsObjects == nil then + options.decodeNumbersAsObjects = self.decodeNumbersAsObjects + end + if options.decodeIntegerStringificationLength == nil then + options.decodeIntegerStringificationLength = self.decodeIntegerStringificationLength + end + if options.decodeDecimalStringificationLength == nil then + options.decodeDecimalStringificationLength = self.decodeDecimalStringificationLength + end + + -- + -- Finally, go parse it + -- + local success, value, next_i = pcall(grok_one, self, text, 1, options) + + if success then + + local error_message = nil + if next_i ~= #text + 1 then + -- something's left over after we parsed the first thing.... whitespace is allowed. + next_i = skip_whitespace(text, next_i) + + -- if we have something left over now, it's trailing garbage + if next_i ~= #text + 1 then + value, error_message = self:onTrailingGarbage(text, next_i, value, options.etc) + end + end + return value, error_message + + else + + -- If JSON:onDecodeError() didn't abort out of the pcall, we'll have received + -- the error message here as "value", so pass it along as an assert. + local error_message = value + if self.assert then + self.assert(false, error_message) + else + assert(false, error_message) + end + -- ...and if we're still here (because the assert didn't throw an error), + -- return a nil and throw the error message on as a second arg + return nil, error_message + + end +end + +local function backslash_replacement_function(c) + if c == "\n" then + return "\\n" + elseif c == "\r" then + return "\\r" + elseif c == "\t" then + return "\\t" + elseif c == "\b" then + return "\\b" + elseif c == "\f" then + return "\\f" + elseif c == '"' then + return '\\"' + elseif c == '\\' then + return '\\\\' + else + return string.format("\\u%04x", c:byte()) + end +end + +local chars_to_be_escaped_in_JSON_string + = '[' + .. '"' -- class sub-pattern to match a double quote + .. '%\\' -- class sub-pattern to match a backslash + .. '%z' -- class sub-pattern to match a null + .. '\001' .. '-' .. '\031' -- class sub-pattern to match control characters + .. ']' + + +local LINE_SEPARATOR_as_utf8 = unicode_codepoint_as_utf8(0x2028) +local PARAGRAPH_SEPARATOR_as_utf8 = unicode_codepoint_as_utf8(0x2029) +local function json_string_literal(value, options) + local newval = value:gsub(chars_to_be_escaped_in_JSON_string, backslash_replacement_function) + if options.stringsAreUtf8 then + -- + -- This feels really ugly to just look into a string for the sequence of bytes that we know to be a particular utf8 character, + -- but utf8 was designed purposefully to make this kind of thing possible. Still, feels dirty. + -- I'd rather decode the byte stream into a character stream, but it's not technically needed so + -- not technically worth it. + -- + newval = newval:gsub(LINE_SEPARATOR_as_utf8, '\\u2028'):gsub(PARAGRAPH_SEPARATOR_as_utf8,'\\u2029') + end + return '"' .. newval .. '"' +end + +local function object_or_array(self, T, etc) + -- + -- We need to inspect all the keys... if there are any strings, we'll convert to a JSON + -- object. If there are only numbers, it's a JSON array. + -- + -- If we'll be converting to a JSON object, we'll want to sort the keys so that the + -- end result is deterministic. + -- + local string_keys = { } + local number_keys = { } + local number_keys_must_be_strings = false + local maximum_number_key + + for key in pairs(T) do + if type(key) == 'string' then + table.insert(string_keys, key) + elseif type(key) == 'number' then + table.insert(number_keys, key) + if key <= 0 or key >= math.huge then + number_keys_must_be_strings = true + elseif not maximum_number_key or key > maximum_number_key then + maximum_number_key = key + end + else + self:onEncodeError("can't encode table with a key of type " .. type(key), etc) + end + end + + if #string_keys == 0 and not number_keys_must_be_strings then + -- + -- An empty table, or a numeric-only array + -- + if #number_keys > 0 then + return nil, maximum_number_key -- an array + elseif tostring(T) == "JSON array" then + return nil + elseif tostring(T) == "JSON object" then + return { } + else + -- have to guess, so we'll pick array, since empty arrays are likely more common than empty objects + return nil + end + end + + table.sort(string_keys) + + local map + if #number_keys > 0 then + -- + -- If we're here then we have either mixed string/number keys, or numbers inappropriate for a JSON array + -- It's not ideal, but we'll turn the numbers into strings so that we can at least create a JSON object. + -- + + if self.noKeyConversion then + self:onEncodeError("a table with both numeric and string keys could be an object or array; aborting", etc) + end + + -- + -- Have to make a shallow copy of the source table so we can remap the numeric keys to be strings + -- + map = { } + for key, val in pairs(T) do + map[key] = val + end + + table.sort(number_keys) + + -- + -- Throw numeric keys in there as strings + -- + for _, number_key in ipairs(number_keys) do + local string_key = tostring(number_key) + if map[string_key] == nil then + table.insert(string_keys , string_key) + map[string_key] = T[number_key] + else + self:onEncodeError("conflict converting table with mixed-type keys into a JSON object: key " .. number_key .. " exists both as a string and a number.", etc) + end + end + end + + return string_keys, nil, map +end + +-- +-- Encode +-- +-- 'options' is nil, or a table with possible keys: +-- +-- pretty -- If true, return a pretty-printed version. +-- +-- indent -- A string (usually of spaces) used to indent each nested level. +-- +-- align_keys -- If true, align all the keys when formatting a table. +-- +-- null -- If this exists with a string value, table elements with this value are output as JSON null. +-- +-- stringsAreUtf8 -- If true, consider Lua strings not as a sequence of bytes, but as a sequence of UTF-8 characters. +-- (Currently, the only practical effect of setting this option is that Unicode LINE and PARAGRAPH +-- separators, if found in a string, are encoded with a JSON escape instead of as raw UTF-8. +-- The JSON is valid either way, but encoding this way, apparently, allows the resulting JSON +-- to also be valid Java.) +-- +-- +local encode_value -- must predeclare because it calls itself +function encode_value(self, value, parents, etc, options, indent, for_key) + + -- + -- keys in a JSON object can never be null, so we don't even consider options.null when converting a key value + -- + if value == nil or (not for_key and options and options.null and value == options.null) then + return 'null' + + elseif type(value) == 'string' then + return json_string_literal(value, options) + + elseif type(value) == 'number' then + if value ~= value then + -- + -- NaN (Not a Number). + -- JSON has no NaN, so we have to fudge the best we can. This should really be a package option. + -- + return "null" + elseif value >= math.huge then + -- + -- Positive infinity. JSON has no INF, so we have to fudge the best we can. This should + -- really be a package option. Note: at least with some implementations, positive infinity + -- is both ">= math.huge" and "<= -math.huge", which makes no sense but that's how it is. + -- Negative infinity is properly "<= -math.huge". So, we must be sure to check the ">=" + -- case first. + -- + return "1e+9999" + elseif value <= -math.huge then + -- + -- Negative infinity. + -- JSON has no INF, so we have to fudge the best we can. This should really be a package option. + -- + return "-1e+9999" + else + return tostring(value) + end + + elseif type(value) == 'boolean' then + return tostring(value) + + elseif type(value) ~= 'table' then + self:onEncodeError("can't convert " .. type(value) .. " to JSON", etc) + + elseif getmetatable(value) == isNumber then + return tostring(value) + else + -- + -- A table to be converted to either a JSON object or array. + -- + local T = value + + if type(options) ~= 'table' then + options = {} + end + if type(indent) ~= 'string' then + indent = "" + end + + if parents[T] then + self:onEncodeError("table " .. tostring(T) .. " is a child of itself", etc) + else + parents[T] = true + end + + local result_value + + local object_keys, maximum_number_key, map = object_or_array(self, T, etc) + if maximum_number_key then + -- + -- An array... + -- + local ITEMS = { } + local key_indent = indent .. tostring(options.indent or "") + for i = 1, maximum_number_key do + if not options.array_newline then + table.insert(ITEMS, encode_value(self, T[i], parents, etc, options, indent)) + else + table.insert(ITEMS, encode_value(self, T[i], parents, etc, options, key_indent)) + end + end + + if options.pretty then + if not options.array_newline then + result_value = "[ " .. table.concat(ITEMS, ", ") .. " ]" + else + result_value = "[\n" .. key_indent .. table.concat(ITEMS, ",\n" .. key_indent) .. "\n" .. indent .. "]" + end + else + result_value = "[" .. table.concat(ITEMS, ",") .. "]" + end + + elseif object_keys then + -- + -- An object + -- + local TT = map or T + + if options.pretty then + + local KEYS = { } + local max_key_length = 0 + for _, key in ipairs(object_keys) do + local encoded = encode_value(self, tostring(key), parents, etc, options, indent, true) + if options.align_keys then + max_key_length = math.max(max_key_length, #encoded) + end + table.insert(KEYS, encoded) + end + local key_indent = indent .. tostring(options.indent or "") + local subtable_indent = key_indent .. string.rep(" ", max_key_length) .. (options.align_keys and " " or "") + local FORMAT = "%s%" .. string.format("%d", max_key_length) .. "s: %s" + + local COMBINED_PARTS = { } + for i, key in ipairs(object_keys) do + local encoded_val = encode_value(self, TT[key], parents, etc, options, subtable_indent) + table.insert(COMBINED_PARTS, string.format(FORMAT, key_indent, KEYS[i], encoded_val)) + end + result_value = "{\n" .. table.concat(COMBINED_PARTS, ",\n") .. "\n" .. indent .. "}" + + else + + local PARTS = { } + for _, key in ipairs(object_keys) do + local encoded_val = encode_value(self, TT[key], parents, etc, options, indent) + local encoded_key = encode_value(self, tostring(key), parents, etc, options, indent, true) + table.insert(PARTS, string.format("%s:%s", encoded_key, encoded_val)) + end + result_value = "{" .. table.concat(PARTS, ",") .. "}" + + end + else + -- + -- An empty array/object... we'll treat it as an array, though it should really be an option + -- + result_value = "[]" + end + + parents[T] = false + return result_value + end +end + +local function top_level_encode(self, value, etc, options) + local val = encode_value(self, value, {}, etc, options) + if val == nil then + --PRIVATE("may need to revert to the previous public verison if I can't figure out what the guy wanted") + return val + else + return val + end +end + +function OBJDEF:encode(value, etc, options) + if type(self) ~= 'table' or self.__index ~= OBJDEF then + OBJDEF:onEncodeError("JSON:encode must be called in method format", etc) + end + + -- + -- If the user didn't pass in a table of decode options, make an empty one. + -- + if type(options) ~= 'table' then + options = {} + end + + return top_level_encode(self, value, etc, options) +end + +function OBJDEF:encode_pretty(value, etc, options) + if type(self) ~= 'table' or self.__index ~= OBJDEF then + OBJDEF:onEncodeError("JSON:encode_pretty must be called in method format", etc) + end + + -- + -- If the user didn't pass in a table of decode options, use the default pretty ones + -- + if type(options) ~= 'table' then + options = default_pretty_options + end + + return top_level_encode(self, value, etc, options) +end + +function OBJDEF.__tostring() + return "JSON encode/decode package" +end + +OBJDEF.__index = OBJDEF + +function OBJDEF:new(args) + local new = { } + + if args then + for key, val in pairs(args) do + new[key] = val + end + end + + return setmetatable(new, OBJDEF) +end + +return OBJDEF:new() \ No newline at end of file diff --git a/bin/lfs.dll b/bin/lfs.dll new file mode 100644 index 0000000..c89e4fa Binary files /dev/null and b/bin/lfs.dll differ diff --git a/bin/libgd2.dll b/bin/libgd2.dll new file mode 100644 index 0000000..5ef2275 Binary files /dev/null and b/bin/libgd2.dll differ diff --git a/bin/libiconv2.dll b/bin/libiconv2.dll new file mode 100644 index 0000000..747073f Binary files /dev/null and b/bin/libiconv2.dll differ diff --git a/bin/libpng13.dll b/bin/libpng13.dll new file mode 100644 index 0000000..e328d39 Binary files /dev/null and b/bin/libpng13.dll differ diff --git a/bin/lua5.1.dll b/bin/lua5.1.dll new file mode 100644 index 0000000..b87f3b6 Binary files /dev/null and b/bin/lua5.1.dll differ diff --git a/bin/lua5.1.exe b/bin/lua5.1.exe new file mode 100644 index 0000000..dc1c2c3 Binary files /dev/null and b/bin/lua5.1.exe differ diff --git a/bin/lua51.dll b/bin/lua51.dll new file mode 100644 index 0000000..27ab265 Binary files /dev/null and b/bin/lua51.dll differ diff --git a/bin/main.lua b/bin/main.lua new file mode 100644 index 0000000..3515c78 --- /dev/null +++ b/bin/main.lua @@ -0,0 +1,396 @@ +package.cpath = "?.dll;".. package.cpath +package.path = "?.lua;./?/init.lua;" .. package.path +-- local multi, thread = require("multi"):init() +-- GLOBAL, THREAD = require("multi.integration.threading"):init() +bin = require("bin") +json = require("json") +lfs = require("lfs") +gd = require("gd") +md5 = require("bin/hashes/md5") +random = require("bin/numbers/random") +song_list = {} +name_list = {} +track_list = {} +local pack_version = 8 +local version = "1.0" + +function isDir(name) + if type(name)~="string" then return false end + local cd = lfs.currentdir() + local is = lfs.chdir(name) and true or false + lfs.chdir(cd) + return is +end + +function isFile(name) + if type(name)~="string" then return false end + if not isDir(name) then + return os.rename(name,name) and true or false + end + return false +end + +function tprint (tbl, indent) + if not indent then indent = 0 end + for k, v in pairs(tbl) do + formatting = string.rep(" ", indent) .. k .. ": " + if type(v) == "table" then + print(formatting) + tprint(v, indent+1) + elseif type(v) == 'boolean' then + print(formatting .. tostring(v)) + else + print(formatting .. v) + end + end +end + +for song in lfs.dir("../music") do + if isFile("../music/"..song) then + table.insert(track_list, song:match("(.+)%.ogg")) + table.insert(name_list, song:lower():gsub("%s",""):match(".+%-(%S+)%.")) + table.insert(song_list, song) + end +end + +if isFile("index.json") then + -- We need to keep indexes for the old records + local data = json:decode(bin.load("index.json").data) + local old = {} + local new = {} + for i,v in pairs(track_list) do + if data[v] then + table.insert(old,"../music/"..v..".ogg") + else + table.insert(new,"../music/"..v..".ogg") + end + end + for i,v in pairs(new) do + table.insert(old,v) + end + track_list = {} + name_list = {} + song_list = {} + for _,song in pairs(old) do + if isFile(song) then + song = song:match("../music/(.+)") + table.insert(track_list, song:match("(.+)%.ogg")) + table.insert(name_list, song:lower():gsub("%s",""):match(".+%-(%S+)%.")) + table.insert(song_list, song) + end + end + local data = {} + for i = 1,#track_list do + data[track_list[i]] = i + end + bin.new(json:encode_pretty(data)):tofile("index.json") +else + local data = {} + for i = 1,#track_list do + data[track_list[i]] = i + end + bin.new(json:encode_pretty(data)):tofile("index.json") +end + +function string.split(inputstr, sep) + if sep == nil then + sep = "%s" + end + local t={} + for str in string.gmatch(inputstr, "([^"..sep.."]+)") do + table.insert(t, str) + end + return t +end + +function CreateDisk(path) + local hash = md5.sumhexa(path) + hash = hash:sub(1,math.floor(#hash/4)) + seed = tonumber(hash,16) + rand = random:new(seed) + for i = 1,300 do + rand:randomInt(0,0) + end + function RandomColor() + return {rand:randomInt(0,255),rand:randomInt(0,255),rand:randomInt(0,255)} + end + function LightenColor(color,v) + return {color[1] + (255 - color[1]) * v,color[2] + (255 - color[2]) * v,color[3] + (255 - color[3]) * v} + end + disc = gd.createFromPng("source.png") + color = RandomColor() + lcolor = LightenColor(color,.35) + bg = disc:colorAllocate(0,0,0) + dark = disc:colorAllocate(unpack(color)) + light = disc:colorAllocate(unpack(lcolor)) + + disc:colorTransparent(bg) + + color_dark = { + {6,6},{7,6},{8,6},{5,7},{6,7} + } + color_light = { + {8,7},{9,7},{6,8},{7,8},{8,8} + } + + for _,p in pairs(color_dark) do + disc:setPixel(p[1],p[2],dark) + end + + for _,p in pairs(color_light) do + disc:setPixel(p[1],p[2],light) + end + + disc:png(path) +end + +function buildPath(path) + local paths = path:split("/") + local p = {} + for i = 1,#paths do + table.insert(p,paths[i]) + lfs.mkdir(table.concat(p,"/")) + end +end + +function writeMCFunction(name,fname,file) + file:tofile("../"..name.."/data/"..name.."/functions/"..fname..".mcfunction") +end + +function BuildDatapack(name) + os.execute("rmdir /s /q ../"..name) + buildPath("../"..name .. "/data/minecraft/tags/functions") + buildPath("../"..name .. "/data/minecraft/loot_tables/entities") + buildPath("../"..name .. "/data/"..name.."/functions") + + -- Write 'pack.mcmeta' + bin.new(json:encode_pretty({pack={ + pack_format = 7; + description = "Adds " .. #name_list .. " custom music discs to minecraft" + }})):tofile("../"..name.."/pack.mcmeta") + + -- Write 'load.json' + bin.new(json:encode_pretty({values={ + name..":setup_load" + }})):tofile("../"..name.."/data/minecraft/tags/functions/load.json") + + -- Write 'tick.json' + bin.new(json:encode_pretty({values={ + name..":detect_play_tick", + name..":detect_stop_tick" + }})):tofile("../"..name.."/data/minecraft/tags/functions/tick.json") + + -- Write 'setup_load.mcfunction' + writeMCFunction(name,'setup_load',bin.new([[ +scoreboard objectives add usedDisc minecraft.used:minecraft.music_disc_11 +scoreboard objectives add heldDisc dummy + +tellraw @a {"text":"Custom Music Discs V]]..version..[[!","color":"yellow"} + ]])) + + -- Write 'detect_play_tick.mcfunction' + writeMCFunction(name,'detect_play_tick',bin.new([[ +execute as @a[scores={usedDisc=0}] run scoreboard players set @s heldDisc -1 +execute as @a[scores={usedDisc=0},nbt={Inventory:[{Slot:-106b,id:"minecraft:music_disc_11"}]}] store result score @s heldDisc run data get entity @s Inventory[{Slot:-106b}].tag.CustomModelData +execute as @a[scores={usedDisc=0},nbt={SelectedItem:{id:"minecraft:music_disc_11"}}] store result score @s heldDisc run data get entity @s SelectedItem.tag.CustomModelData +execute as @a[scores={usedDisc=2}] run function ]]..name..[[:disc_play + +execute as @a run scoreboard players add @s usedDisc 0 +execute as @a[scores={usedDisc=2..}] run scoreboard players set @s usedDisc 0 +scoreboard players add @a[scores={usedDisc=1}] usedDisc 1 + ]])) + + -- Write 'disc_play.mcfunction' + disc_play = bin.new() + for i,v in pairs(name_list) do + disc_play:tackE("execute as @s[scores={heldDisc="..i.."}] run function "..name..":play_"..v.."\n") + end + writeMCFunction(name,'disc_play',disc_play) + + -- Write 'detect_stop_tick.mcfunction' + writeMCFunction(name,'detect_stop_tick',bin.new([[ +execute as @e[type=item, nbt={Item:{id:"minecraft:music_disc_11"}}] at @s unless entity @s[tag=old] if block ~ ~-1 ~ minecraft:jukebox run function ]]..name..[[:disc_stop +execute as @e[type=item, nbt={Item:{id:"minecraft:music_disc_11"}}] at @s unless entity @s[tag=old] if block ~ ~ ~ minecraft:jukebox run function ]]..name..[[:disc_stop +execute as @e[type=item, nbt={Item:{id:"minecraft:music_disc_11"}}] at @s unless entity @s[tag=old] run tag @s add old + ]])) + + -- Write 'disc_stop.mcfunction' + disc_stop = bin.new() + for i,v in pairs(name_list) do + disc_stop:tackE("execute as @s[nbt={Item:{tag:{CustomModelData:"..i.."}}}] at @s run stopsound @a[distance=..64] record minecraft:music_disc."..v.."\n") + end + writeMCFunction(name,'disc_stop',disc_stop) + + -- Write 'set_disc_track.mcfunction' + set_disc_track = bin.new() + for i,v in pairs(track_list) do + set_disc_track:tackE('execute as @s[nbt={SelectedItem:{id:"minecraft:music_disc_11", tag:{CustomModelData:'..i..'}}}] run item replace entity @s weapon.mainhand with minecraft:music_disc_11{CustomModelData:'..i..', HideFlags:32, display:{Lore:[\"\\\"\\\\u00a77'..(v:gsub('"',""))..'\\\"\"]}}\n') + end + writeMCFunction(name,"set_disc_track",set_disc_track) + + -- Write 'play_*.mcfunction' files + for i,v in pairs(name_list) do + writeMCFunction(name,"play_"..v,bin.new([[ +execute as @s at @s run title @a[distance=..64] actionbar {"text":"Now Playing: ]]..(track_list[i]:gsub('"',""))..[[","color":"green"} +execute as @s at @s run stopsound @a[distance=..64] record minecraft:music_disc.11 +execute as @s at @s run playsound minecraft:music_disc.]]..v..[[ record @a[distance=..64] ~ ~ ~ 4 1 + ]])) + end + + -- Write 'give_*_disc.mcfunction' files + for i,v in pairs(track_list) do + writeMCFunction(name,"give_"..name_list[i],bin.new([[execute as @s at @s run summon item ~ ~ ~ {Item:{id:"minecraft:music_disc_11", Count:1b, tag:{CustomModelData:]]..i..[[, HideFlags:32, display:{Lore:["\"\\u00a77]]..v..[[\""]}}}}]])) + end + + -- Write 'give_all_discs.mcfunction' + give_all = bin.new() + for i,v in pairs(track_list) do + give_all:tackE([[execute as @s at @s run summon item ~ ~ ~ {Item:{id:"minecraft:music_disc_11", Count:1b, tag:{CustomModelData:]]..i..[[, HideFlags:32, display:{Lore:["\"\\u00a77]]..v..[[\""]}}}}]].."\n") + end + writeMCFunction(name,"give_all_discs",give_all) + + -- Write 'creeper.json' + creeper_mdentries = {} + table.insert(creeper_mdentries,{ + type="minecraft:tag", + weight = 1, + name = "minecraft:creeper_drop_music_discs", + expand = true + }) + for i,track in pairs(track_list) do + table.insert( creeper_mdentries, { + type = 'minecraft:item', + weight = 1, + name = 'minecraft:music_disc_11', + functions = { + { + ['function']='minecraft:set_nbt', + tag='{CustomModelData:'..i..', HideFlags:32, display:{Lore:[\"\\\"\\\\u00a77'..(track:gsub('"',''))..'\\\"\"]}}' + } + } + }) + end + creeper_normentries = { + { + type = 'minecraft:item', + functions = { + { + ['function']='minecraft:set_count', + count={ + min=0, + max=2, + type='minecraft:uniform' + } + }, + { + ['function']='minecraft:looting_enchant', + count={ + min = 0, + max = 1 + } + } + }, + name = 'minecraft:gunpowder' + } + } + creeper = bin.new(json:encode_pretty({ + type = 'minecraft:entity', + pools={ + { + rolls=1, + entries = creeper_normentries + }, + { + rolls=1, + entries = creeper_mdentries, + conditions = { + { + condition='minecraft:entity_properties', + predicate={ + type='#minecraft:skeletons' + }, + entity='killer' + } + } + } + } + })) + creeper:tofile("../"..name.."/data/minecraft/loot_tables/entities/creeper.json") + bin.load("pack.png"):tofile("../"..name.."/pack.png") +end +function BuildResourcepack(name) + os.execute("rmdir /s /q ../"..name) + buildPath("../"..name .. "/assets/minecraft/models/item") + buildPath("../"..name .. "/assets/minecraft/sounds/records") + buildPath("../"..name .. "/assets/minecraft/textures/item") + + -- Write 'pack.mcmets' + bin.new(json:encode_pretty({ + pack = { + pack_format = pack_version, + description = "Adds " .. #name_list .. " custom music discs to minecraft" + } + })):tofile("../"..name .. "/pack.mcmeta") + + -- Write 'sounds.json' + pack = bin.new('{') + for i,v in pairs(name_list) do + pack:tackE('\n"music_disc.'..v..'":') + pack:tackE(json:encode_pretty({ + sounds = { + { + name='records/'..v, + stream = true + } + } + })) + if i < #name_list then + pack:tackE(",\n") + end + end + pack:tackE('\n}') + pack:tofile("../"..name.."/assets/minecraft/sounds.json") + + -- Write 'music_disc_11.json' + music_disc_11 = bin.new() + json_list = {} + for i,v in pairs(name_list) do + table.insert(json_list,{ + predicate={ + custom_model_data = i + }, + model = 'item/music_disc_'..v + }) + end + + music_disc_11:tackE(json:encode_pretty({ + parent='item/generated', + textures={ + layer0 = 'item/music_disc_11' + }, + overrides=json_list + })) + + music_disc_11:tofile("../"..name.."/assets/minecraft/models/item/music_disc_11.json") + + -- Write 'music_disc_*.json' files + for i,v in pairs(name_list) do + bin.new(json:encode_pretty({ + parent='item/generated', + textures={ + layer0='item/music_disc_'..v + } + })):tofile("../"..name.."/assets/minecraft/models/item/music_disc_"..v..".json") + end + + -- Copy sound and texture files + for i,v in pairs(name_list) do + bin.load("../music/"..song_list[i]):tofile("../"..name.."/assets/minecraft/sounds/records/"..v..".ogg") + CreateDisk("../"..name.."/assets/minecraft/textures/item/music_disc_"..v..".png") + end + + -- Copy pack.png + bin.load("pack.png"):tofile("../"..name.."/pack.png") +end +BuildDatapack("cmd_dp") +BuildResourcepack("cmd_rp") \ No newline at end of file diff --git a/bin/pack.png b/bin/pack.png new file mode 100644 index 0000000..db0a4d7 Binary files /dev/null and b/bin/pack.png differ diff --git a/bin/source.png b/bin/source.png new file mode 100644 index 0000000..081bc96 Binary files /dev/null and b/bin/source.png differ diff --git a/bin/xpm4.dll b/bin/xpm4.dll new file mode 100644 index 0000000..0414d84 Binary files /dev/null and b/bin/xpm4.dll differ diff --git a/bin/zlib1.dll b/bin/zlib1.dll new file mode 100644 index 0000000..31996cd Binary files /dev/null and b/bin/zlib1.dll differ diff --git a/run.bat b/run.bat new file mode 100644 index 0000000..211eec6 --- /dev/null +++ b/run.bat @@ -0,0 +1,2 @@ +cd bin +lua5.1.exe main.lua \ No newline at end of file