mirror of
https://github.com/kepler155c/opus
synced 2025-10-21 02:37:48 +00:00
reorganization
This commit is contained in:
@@ -1,135 +0,0 @@
|
||||
-- Base64 Encoder / Decoder
|
||||
-- By KillaVanilla
|
||||
-- see: http://www.computercraft.info/forums2/index.php?/topic/12450-killavanillas-various-apis/
|
||||
|
||||
Base64 = { }
|
||||
|
||||
local alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||
|
||||
local function sixBitToBase64(input)
|
||||
return string.sub(alphabet, input+1, input+1)
|
||||
end
|
||||
|
||||
local function base64ToSixBit(input)
|
||||
for i=1, 64 do
|
||||
if input == string.sub(alphabet, i, i) then
|
||||
return i-1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function octetToBase64(o1, o2, o3)
|
||||
local i1 = sixBitToBase64(bit.brshift(bit.band(o1, 0xFC), 2))
|
||||
local i2 = "A"
|
||||
local i3 = "="
|
||||
local i4 = "="
|
||||
if o2 then
|
||||
i2 = sixBitToBase64(bit.bor( bit.blshift(bit.band(o1, 3), 4), bit.brshift(bit.band(o2, 0xF0), 4) ))
|
||||
if not o3 then
|
||||
i3 = sixBitToBase64(bit.blshift(bit.band(o2, 0x0F), 2))
|
||||
else
|
||||
i3 = sixBitToBase64(bit.bor( bit.blshift(bit.band(o2, 0x0F), 2), bit.brshift(bit.band(o3, 0xC0), 6) ))
|
||||
end
|
||||
else
|
||||
i2 = sixBitToBase64(bit.blshift(bit.band(o1, 3), 4))
|
||||
end
|
||||
if o3 then
|
||||
i4 = sixBitToBase64(bit.band(o3, 0x3F))
|
||||
end
|
||||
|
||||
return i1..i2..i3..i4
|
||||
end
|
||||
|
||||
-- octet 1 needs characters 1/2
|
||||
-- octet 2 needs characters 2/3
|
||||
-- octet 3 needs characters 3/4
|
||||
|
||||
local function base64ToThreeOctet(s1)
|
||||
local c1 = base64ToSixBit(string.sub(s1, 1, 1))
|
||||
local c2 = base64ToSixBit(string.sub(s1, 2, 2))
|
||||
local c3 = 0
|
||||
local c4 = 0
|
||||
local o1 = 0
|
||||
local o2 = 0
|
||||
local o3 = 0
|
||||
if string.sub(s1, 3, 3) == "=" then
|
||||
c3 = nil
|
||||
c4 = nil
|
||||
elseif string.sub(s1, 4, 4) == "=" then
|
||||
c3 = base64ToSixBit(string.sub(s1, 3, 3))
|
||||
c4 = nil
|
||||
else
|
||||
c3 = base64ToSixBit(string.sub(s1, 3, 3))
|
||||
c4 = base64ToSixBit(string.sub(s1, 4, 4))
|
||||
end
|
||||
o1 = bit.bor( bit.blshift(c1, 2), bit.brshift(bit.band( c2, 0x30 ), 4) )
|
||||
if c3 then
|
||||
o2 = bit.bor( bit.blshift(bit.band(c2, 0x0F), 4), bit.brshift(bit.band( c3, 0x3C ), 2) )
|
||||
else
|
||||
o2 = nil
|
||||
end
|
||||
if c4 then
|
||||
o3 = bit.bor( bit.blshift(bit.band(c3, 3), 6), c4 )
|
||||
else
|
||||
o3 = nil
|
||||
end
|
||||
return o1, o2, o3
|
||||
end
|
||||
|
||||
local function splitIntoBlocks(bytes)
|
||||
local blockNum = 1
|
||||
local blocks = {}
|
||||
for i=1, #bytes, 3 do
|
||||
blocks[blockNum] = {bytes[i], bytes[i+1], bytes[i+2]}
|
||||
--[[
|
||||
if #blocks[blockNum] < 3 then
|
||||
for j=#blocks[blockNum]+1, 3 do
|
||||
table.insert(blocks[blockNum], 0)
|
||||
end
|
||||
end
|
||||
]]
|
||||
blockNum = blockNum+1
|
||||
end
|
||||
return blocks
|
||||
end
|
||||
|
||||
function Base64.encode(bytes)
|
||||
local blocks = splitIntoBlocks(bytes)
|
||||
local output = ""
|
||||
for i=1, #blocks do
|
||||
output = output..octetToBase64( unpack(blocks[i]) )
|
||||
end
|
||||
return output
|
||||
end
|
||||
|
||||
function Base64.decode(str)
|
||||
local bytes = {}
|
||||
local blocks = {}
|
||||
local blockNum = 1
|
||||
for i=1, #str, 4 do
|
||||
blocks[blockNum] = string.sub(str, i, i+3)
|
||||
blockNum = blockNum+1
|
||||
end
|
||||
for i=1, #blocks do
|
||||
local o1, o2, o3 = base64ToThreeOctet(blocks[i])
|
||||
table.insert(bytes, o1)
|
||||
table.insert(bytes, o2)
|
||||
table.insert(bytes, o3)
|
||||
if (i % 1000) == 0 then
|
||||
os.sleep(0)
|
||||
end
|
||||
end
|
||||
-- Remove padding:
|
||||
--[[
|
||||
for i=#bytes, 1, -1 do
|
||||
if bytes[i] ~= 0 then
|
||||
break
|
||||
else
|
||||
bytes[i] = nil
|
||||
end
|
||||
end
|
||||
]]
|
||||
return bytes
|
||||
end
|
||||
|
||||
return Base64
|
@@ -1,612 +0,0 @@
|
||||
local class = require('class')
|
||||
local Util = require('util')
|
||||
local TableDB = require('tableDB')
|
||||
local JSON = require('json')
|
||||
|
||||
-- see https://github.com/Khroki/MCEdit-Unified/blob/master/pymclevel/minecraft.yaml
|
||||
-- see https://github.com/Khroki/MCEdit-Unified/blob/master/Items/minecraft/blocks.json
|
||||
|
||||
--[[-- nameDB --]]--
|
||||
local nameDB = TableDB({
|
||||
fileName = 'blocknames.db'
|
||||
})
|
||||
function nameDB:load(dir, blockDB)
|
||||
self.fileName = fs.combine(dir, self.fileName)
|
||||
if fs.exists(self.fileName) then
|
||||
TableDB.load(self)
|
||||
end
|
||||
self.blockDB = blockDB
|
||||
end
|
||||
|
||||
function nameDB:getName(id, dmg)
|
||||
return self:lookupName(id, dmg) or id .. ':' .. dmg
|
||||
end
|
||||
|
||||
function nameDB:lookupName(id, dmg)
|
||||
-- is it in the name db ?
|
||||
local name = self:get({ id, dmg })
|
||||
if name then
|
||||
return name
|
||||
end
|
||||
|
||||
-- is it in the block db ?
|
||||
for _,v in pairs(self.blockDB.data) do
|
||||
if v.strId == id and v.dmg == dmg then
|
||||
return v.name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[-- blockDB --]]--
|
||||
local blockDB = TableDB()
|
||||
|
||||
function blockDB:load()
|
||||
|
||||
local blocks = JSON.decodeFromFile(fs.combine('sys/etc', 'blocks.json'))
|
||||
|
||||
if not blocks then
|
||||
error('Unable to read blocks.json')
|
||||
end
|
||||
|
||||
for strId, block in pairs(blocks) do
|
||||
strId = 'minecraft:' .. strId
|
||||
if type(block.name) == 'string' then
|
||||
self:add(block.id, 0, block.name, strId, block.place)
|
||||
else
|
||||
for nid,name in pairs(block.name) do
|
||||
self:add(block.id, nid - 1, name, strId, block.place)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function blockDB:lookup(id, dmg)
|
||||
if not id then
|
||||
return
|
||||
end
|
||||
|
||||
return self.data[id .. ':' .. dmg]
|
||||
end
|
||||
|
||||
function blockDB:add(id, dmg, name, strId, place)
|
||||
local key = id .. ':' .. dmg
|
||||
|
||||
TableDB.add(self, key, {
|
||||
id = id,
|
||||
dmg = dmg,
|
||||
name = name,
|
||||
strId = strId,
|
||||
place = place,
|
||||
})
|
||||
end
|
||||
|
||||
--[[-- placementDB --]]--
|
||||
-- in memory table that expands the standardBlock and blockType tables for each item/dmg/placement combination
|
||||
local placementDB = TableDB()
|
||||
|
||||
function placementDB:load(sbDB, btDB)
|
||||
|
||||
for k,blockType in pairs(sbDB.data) do
|
||||
local bt = btDB.data[blockType]
|
||||
if not bt then
|
||||
error('missing block type: ' .. blockType)
|
||||
end
|
||||
local id, dmg = string.match(k, '(%d+):*(%d+)')
|
||||
self:addSubsForBlockType(tonumber(id), tonumber(dmg), bt)
|
||||
end
|
||||
end
|
||||
|
||||
function placementDB:load2(sbDB, btDB)
|
||||
|
||||
for k,v in pairs(sbDB.data) do
|
||||
if v.place then
|
||||
local bt = btDB.data[v.place]
|
||||
if not bt then
|
||||
error('missing block type: ' .. v.place)
|
||||
end
|
||||
local id, dmg = string.match(k, '(%d+):*(%d+)')
|
||||
self:addSubsForBlockType(tonumber(id), tonumber(dmg), bt)
|
||||
end
|
||||
end
|
||||
|
||||
-- special case for quartz pillars
|
||||
self:addSubsForBlockType(155, 2, btDB.data['quartz-pillar'])
|
||||
end
|
||||
|
||||
|
||||
function placementDB:addSubsForBlockType(id, dmg, bt)
|
||||
for _,sub in pairs(bt) do
|
||||
local odmg = sub.odmg
|
||||
if type(sub.odmg) == 'string' then
|
||||
odmg = dmg + tonumber(string.match(odmg, '+(%d+)'))
|
||||
end
|
||||
|
||||
local b = blockDB:lookup(id, dmg)
|
||||
local strId = tostring(id)
|
||||
if b then
|
||||
strId = b.strId
|
||||
end
|
||||
|
||||
self:add(
|
||||
id,
|
||||
odmg,
|
||||
sub.sid or strId,
|
||||
sub.sdmg or dmg,
|
||||
sub.dir,
|
||||
sub.extra)
|
||||
end
|
||||
end
|
||||
|
||||
function placementDB:add(id, dmg, sid, sdmg, direction, extra)
|
||||
if direction and #direction == 0 then
|
||||
direction = nil
|
||||
end
|
||||
|
||||
local entry = {
|
||||
oid = id, -- numeric ID
|
||||
odmg = dmg, -- dmg with placement info
|
||||
id = sid, -- string ID
|
||||
dmg = sdmg, -- dmg without placement info
|
||||
direction = direction,
|
||||
}
|
||||
if extra then
|
||||
Util.merge(entry, extra)
|
||||
end
|
||||
|
||||
self.data[id .. ':' .. dmg] = entry
|
||||
end
|
||||
|
||||
--[[-- BlockTypeDB --]]--
|
||||
local blockTypeDB = TableDB()
|
||||
|
||||
function blockTypeDB:addTemp(blockType, subs)
|
||||
local bt = self.data[blockType]
|
||||
if not bt then
|
||||
bt = { }
|
||||
self.data[blockType] = bt
|
||||
end
|
||||
for _,sub in pairs(subs) do
|
||||
table.insert(bt, {
|
||||
odmg = sub[1],
|
||||
sid = sub[2],
|
||||
sdmg = sub[3],
|
||||
dir = sub[4],
|
||||
extra = sub[5]
|
||||
})
|
||||
end
|
||||
self.dirty = true
|
||||
end
|
||||
|
||||
function blockTypeDB:load()
|
||||
|
||||
blockTypeDB:addTemp('stairs', {
|
||||
{ 0, nil, 0, 'east-up' },
|
||||
{ 1, nil, 0, 'west-up' },
|
||||
{ 2, nil, 0, 'south-up' },
|
||||
{ 3, nil, 0, 'north-up' },
|
||||
{ 4, nil, 0, 'east-down' },
|
||||
{ 5, nil, 0, 'west-down' },
|
||||
{ 6, nil, 0, 'south-down' },
|
||||
{ 7, nil, 0, 'north-down' },
|
||||
})
|
||||
blockTypeDB:addTemp('gate', {
|
||||
{ 0, nil, 0, 'north' },
|
||||
{ 1, nil, 0, 'east' },
|
||||
{ 2, nil, 0, 'south' },
|
||||
{ 3, nil, 0, 'west' },
|
||||
{ 4, nil, 0, 'north' },
|
||||
{ 5, nil, 0, 'east' },
|
||||
{ 6, nil, 0, 'south' },
|
||||
{ 7, nil, 0, 'west' },
|
||||
})
|
||||
blockTypeDB:addTemp('pumpkin', {
|
||||
{ 0, nil, 0, 'north-block' },
|
||||
{ 1, nil, 0, 'east-block' },
|
||||
{ 2, nil, 0, 'south-block' },
|
||||
{ 3, nil, 0, 'west-block' },
|
||||
{ 4, nil, 0, 'north-block' },
|
||||
{ 5, nil, 0, 'east-block' },
|
||||
{ 6, nil, 0, 'south-block' },
|
||||
{ 7, nil, 0, 'west-block' },
|
||||
})
|
||||
blockTypeDB:addTemp('anvil', {
|
||||
{ 0, nil, 0, 'south' },
|
||||
{ 1, nil, 0, 'east' },
|
||||
{ 2, nil, 0, 'south'},
|
||||
{ 3, nil, 0, 'east' },
|
||||
{ 4, nil, 0, 'south' },
|
||||
{ 5, nil, 0, 'east' },
|
||||
{ 6, nil, 0, 'east' },
|
||||
{ 7, nil, 0, 'south' },
|
||||
{ 8, nil, 0, 'south' },
|
||||
{ 9, nil, 0, 'east' },
|
||||
{ 10, nil, 0, 'east' },
|
||||
{ 11, nil, 0, 'south' },
|
||||
{ 12, nil, 0 },
|
||||
{ 13, nil, 0 },
|
||||
{ 14, nil, 0 },
|
||||
{ 15, nil, 0 },
|
||||
})
|
||||
blockTypeDB:addTemp('bed', {
|
||||
{ 0, nil, 0, 'south' },
|
||||
{ 1, nil, 0, 'west' },
|
||||
{ 2, nil, 0, 'north' },
|
||||
{ 3, nil, 0, 'east' },
|
||||
{ 4, nil, 0, 'south' },
|
||||
{ 5, nil, 0, 'west' },
|
||||
{ 6, nil, 0, 'north' },
|
||||
{ 7, nil, 0, 'east' },
|
||||
{ 8, 'minecraft:air', 0 },
|
||||
{ 9, 'minecraft:air', 0 },
|
||||
{ 10, 'minecraft:air', 0 },
|
||||
{ 11, 'minecraft:air', 0 },
|
||||
{ 12, 'minecraft:air', 0 },
|
||||
{ 13, 'minecraft:air', 0 },
|
||||
{ 14, 'minecraft:air', 0 },
|
||||
{ 15, 'minecraft:air', 0 },
|
||||
})
|
||||
blockTypeDB:addTemp('comparator', {
|
||||
{ 0, nil, 0, 'south' },
|
||||
{ 1, nil, 0, 'west' },
|
||||
{ 2, nil, 0, 'north' },
|
||||
{ 3, nil, 0, 'east' },
|
||||
{ 4, nil, 0, 'south' },
|
||||
{ 5, nil, 0, 'west' },
|
||||
{ 6, nil, 0, 'north' },
|
||||
{ 7, nil, 0, 'east' },
|
||||
{ 8, nil, 0, 'south' },
|
||||
{ 9, nil, 0, 'west' },
|
||||
{ 10, nil, 0, 'north' },
|
||||
{ 11, nil, 0, 'east' },
|
||||
{ 12, nil, 0, 'south' },
|
||||
{ 13, nil, 0, 'west' },
|
||||
{ 14, nil, 0, 'north' },
|
||||
{ 15, nil, 0, 'east' },
|
||||
})
|
||||
blockTypeDB:addTemp('quartz-pillar', {
|
||||
{ 2, nil, 2 },
|
||||
{ 3, nil, 2, 'north-south-block' },
|
||||
{ 4, nil, 2, 'east-west-block' }, -- should be east-west-block
|
||||
})
|
||||
blockTypeDB:addTemp('hay-bale', {
|
||||
{ 0, nil, 0 },
|
||||
{ 4, nil, 0, 'east-west-block' }, -- should be east-west-block
|
||||
{ 8, nil, 0, 'north-south-block' },
|
||||
})
|
||||
blockTypeDB:addTemp('button', {
|
||||
{ 1, nil, 0, 'west-block' },
|
||||
{ 2, nil, 0, 'east-block' },
|
||||
{ 3, nil, 0, 'north-block' },
|
||||
{ 4, nil, 0, 'south-block' },
|
||||
{ 5, nil, 0 }, -- block top
|
||||
})
|
||||
blockTypeDB:addTemp('cauldron', {
|
||||
{ 0, nil, 0 },
|
||||
{ 1, nil, 0 },
|
||||
{ 2, nil, 0 },
|
||||
{ 3, nil, 0 },
|
||||
})
|
||||
blockTypeDB:addTemp('dispenser', {
|
||||
{ 0, nil, 0, 'wrench-down' },
|
||||
{ 1, nil, 0, 'wrench-up' },
|
||||
{ 2, nil, 0, 'south' },
|
||||
{ 3, nil, 0, 'north' },
|
||||
{ 4, nil, 0, 'east' },
|
||||
{ 5, nil, 0, 'west' },
|
||||
{ 9, nil, 0 },
|
||||
})
|
||||
blockTypeDB:addTemp('end_rod', {
|
||||
{ 0, nil, 0, 'wrench-down' },
|
||||
{ 1, nil, 0, 'wrench-up' },
|
||||
{ 2, nil, 0, 'south-block-flip' },
|
||||
{ 3, nil, 0, 'north-block-flip' },
|
||||
{ 4, nil, 0, 'east-block-flip' },
|
||||
{ 5, nil, 0, 'west-block-flip' },
|
||||
{ 9, nil, 0 },
|
||||
})
|
||||
blockTypeDB:addTemp('hopper', {
|
||||
{ 0, nil, 0 },
|
||||
{ 1, nil, 0 },
|
||||
{ 2, nil, 0, 'south-block' },
|
||||
{ 3, nil, 0, 'north-block' },
|
||||
{ 4, nil, 0, 'east-block' },
|
||||
{ 5, nil, 0, 'west-block' },
|
||||
{ 8, nil, 0 },
|
||||
{ 9, nil, 0 },
|
||||
{ 10, nil, 0 },
|
||||
{ 11, nil, 0, 'south-block' },
|
||||
{ 12, nil, 0, 'north-block' },
|
||||
{ 13, nil, 0, 'east-block' },
|
||||
{ 14, nil, 0, 'west-block' },
|
||||
})
|
||||
blockTypeDB:addTemp('mobhead', {
|
||||
{ 0, nil, 0 },
|
||||
{ 1, nil, 0 },
|
||||
{ 2, nil, 0, 'south-block' },
|
||||
{ 3, nil, 0, 'north-block' },
|
||||
{ 4, nil, 0, 'west-block' },
|
||||
{ 5, nil, 0, 'east-block' },
|
||||
})
|
||||
blockTypeDB:addTemp('rail', {
|
||||
{ 0, nil, 0, 'south' },
|
||||
{ 1, nil, 0, 'east' },
|
||||
{ 2, nil, 0, 'east' },
|
||||
{ 3, nil, 0, 'east' },
|
||||
{ 4, nil, 0, 'south' },
|
||||
{ 5, nil, 0, 'south' },
|
||||
{ 6, nil, 0, 'east' },
|
||||
{ 7, nil, 0, 'south' },
|
||||
{ 8, nil, 0, 'east' },
|
||||
{ 9, nil, 0, 'south' },
|
||||
})
|
||||
blockTypeDB:addTemp('adp-rail', {
|
||||
{ 0, nil, 0, 'south' },
|
||||
{ 1, nil, 0, 'east' },
|
||||
{ 2, nil, 0, 'east' },
|
||||
{ 3, nil, 0, 'east' },
|
||||
{ 4, nil, 0, 'south' },
|
||||
{ 5, nil, 0, 'south' },
|
||||
{ 8, nil, 0, 'south' },
|
||||
{ 9, nil, 0, 'east' },
|
||||
{ 10, nil, 0, 'east' },
|
||||
{ 11, nil, 0, 'east' },
|
||||
{ 12, nil, 0, 'south' },
|
||||
{ 13, nil, 0, 'south' },
|
||||
})
|
||||
blockTypeDB:addTemp('signpost', {
|
||||
{ 0, nil, 0, 'north' },
|
||||
{ 1, nil, 0, 'north', { facing = 1 } },
|
||||
{ 2, nil, 0, 'north', { facing = 2 } },
|
||||
{ 3, nil, 0, 'north', { facing = 3 } },
|
||||
{ 4, nil, 0, 'east' },
|
||||
{ 5, nil, 0, 'east', { facing = 1 } },
|
||||
{ 6, nil, 0, 'east', { facing = 2 } },
|
||||
{ 7, nil, 0, 'east', { facing = 3 } },
|
||||
{ 8, nil, 0, 'south' },
|
||||
{ 9, nil, 0, 'south', { facing = 1 } },
|
||||
{ 10, nil, 0, 'south', { facing = 2 } },
|
||||
{ 11, nil, 0, 'south', { facing = 3 } },
|
||||
{ 12, nil, 0, 'west' },
|
||||
{ 13, nil, 0, 'west', { facing = 1 } },
|
||||
{ 14, nil, 0, 'west', { facing = 2 } },
|
||||
{ 15, nil, 0, 'west', { facing = 3 } },
|
||||
})
|
||||
blockTypeDB:addTemp('vine', {
|
||||
{ 0, nil, 0 },
|
||||
{ 1, nil, 0, 'south-block-vine' },
|
||||
{ 2, nil, 0, 'west-block-vine' },
|
||||
{ 3, nil, 0, 'south-block-vine' },
|
||||
{ 4, nil, 0, 'north-block-vine' },
|
||||
{ 5, nil, 0, 'south-block-vine' },
|
||||
{ 6, nil, 0, 'north-block-vine' },
|
||||
{ 7, nil, 0, 'south-block-vine' },
|
||||
{ 8, nil, 0, 'east-block-vine' },
|
||||
{ 9, nil, 0, 'south-block-vine' },
|
||||
{ 10, nil, 0, 'east-block-vine' },
|
||||
{ 11, nil, 0, 'east-block-vine' },
|
||||
{ 12, nil, 0, 'east-block-vine' },
|
||||
{ 13, nil, 0, 'east-block-vine' },
|
||||
{ 14, nil, 0, 'east-block-vine' },
|
||||
{ 15, nil, 0, 'east-block-vine' },
|
||||
})
|
||||
blockTypeDB:addTemp('torch', {
|
||||
{ 0, nil, 0 },
|
||||
{ 1, nil, 0, 'west-block' },
|
||||
{ 2, nil, 0, 'east-block' },
|
||||
{ 3, nil, 0, 'north-block' },
|
||||
{ 4, nil, 0, 'south-block' },
|
||||
{ 5, nil, 0 },
|
||||
})
|
||||
blockTypeDB:addTemp('tripwire', {
|
||||
{ 0, nil, 0, 'north-block' },
|
||||
{ 1, nil, 0, 'east-block' },
|
||||
{ 2, nil, 0, 'south-block' },
|
||||
{ 3, nil, 0, 'west-block' },
|
||||
})
|
||||
blockTypeDB:addTemp('trapdoor', {
|
||||
{ 0, nil, 0, 'south-block' },
|
||||
{ 1, nil, 0, 'north-block' },
|
||||
{ 2, nil, 0, 'east-block' },
|
||||
{ 3, nil, 0, 'west-block' },
|
||||
{ 4, nil, 0, 'south-block' },
|
||||
{ 5, nil, 0, 'north-block' },
|
||||
{ 6, nil, 0, 'east-block' },
|
||||
{ 7, nil, 0, 'west-block' },
|
||||
{ 8, nil, 0, 'south-block' },
|
||||
{ 9, nil, 0, 'north-block' },
|
||||
{ 10, nil, 0, 'east-block' },
|
||||
{ 11, nil, 0, 'west-block' },
|
||||
{ 12, nil, 0, 'south-block' },
|
||||
{ 13, nil, 0, 'north-block' },
|
||||
{ 14, nil, 0, 'east-block' },
|
||||
{ 15, nil, 0, 'west-block' },
|
||||
})
|
||||
blockTypeDB:addTemp('piston', { -- piston placement is broken in 1.7 -- need to add work around
|
||||
{ 0, nil, 0, 'piston-down' },
|
||||
{ 1, nil, 0, 'piston-up' },
|
||||
{ 2, nil, 0, 'piston-north' },
|
||||
{ 3, nil, 0, 'piston-south' },
|
||||
{ 4, nil, 0, 'piston-west' },
|
||||
{ 5, nil, 0, 'piston-east' },
|
||||
{ 8, nil, 0, 'piston-down' },
|
||||
{ 9, nil, 0, 'piston-up' },
|
||||
{ 10, nil, 0, 'piston-north' },
|
||||
{ 11, nil, 0, 'piston-south' },
|
||||
{ 12, nil, 0, 'piston-west' },
|
||||
{ 13, nil, 0, 'piston-east' },
|
||||
})
|
||||
blockTypeDB:addTemp('lever', {
|
||||
{ 0, nil, 0, 'up' },
|
||||
{ 1, nil, 0, 'west-block' },
|
||||
{ 2, nil, 0, 'east-block' },
|
||||
{ 3, nil, 0, 'north-block' },
|
||||
{ 4, nil, 0, 'south-block' },
|
||||
{ 5, nil, 0, 'north' },
|
||||
{ 6, nil, 0, 'west' },
|
||||
{ 7, nil, 0, 'up' },
|
||||
{ 8, nil, 0, 'up' },
|
||||
{ 9, nil, 0, 'west-block' },
|
||||
{ 10, nil, 0, 'east-block' },
|
||||
{ 11, nil, 0, 'north-block' },
|
||||
{ 12, nil, 0, 'south-block' },
|
||||
{ 13, nil, 0, 'north' },
|
||||
{ 14, nil, 0, 'west' },
|
||||
{ 15, nil, 0, 'up' },
|
||||
})
|
||||
blockTypeDB:addTemp('wallsign-ladder', {
|
||||
{ 0, nil, 0 },
|
||||
{ 1, nil, 0 },
|
||||
{ 2, nil, 0, 'south-block' },
|
||||
{ 3, nil, 0, 'north-block' },
|
||||
{ 4, nil, 0, 'east-block' },
|
||||
{ 5, nil, 0, 'west-block' },
|
||||
})
|
||||
blockTypeDB:addTemp('chest-furnace', {
|
||||
{ 0, nil, 0 },
|
||||
{ 2, nil, 0, 'south' },
|
||||
{ 3, nil, 0, 'north' },
|
||||
{ 4, nil, 0, 'east' },
|
||||
{ 5, nil, 0, 'west' },
|
||||
})
|
||||
blockTypeDB:addTemp('repeater', {
|
||||
{ 0, nil, 0, 'north' },
|
||||
{ 1, nil, 0, 'east' },
|
||||
{ 2, nil, 0, 'south' },
|
||||
{ 3, nil, 0, 'west' },
|
||||
{ 4, nil, 0, 'north' },
|
||||
{ 5, nil, 0, 'east' },
|
||||
{ 6, nil, 0, 'south' },
|
||||
{ 7, nil, 0, 'west' },
|
||||
{ 8, nil, 0, 'north' },
|
||||
{ 9, nil, 0, 'east' },
|
||||
{ 10, nil, 0, 'south' },
|
||||
{ 11, nil, 0, 'west' },
|
||||
{ 12, nil, 0, 'north' },
|
||||
{ 13, nil, 0, 'east' },
|
||||
{ 14, nil, 0, 'south' },
|
||||
{ 15, nil, 0, 'west' },
|
||||
})
|
||||
blockTypeDB:addTemp('flatten', {
|
||||
{ 0, nil, 0 },
|
||||
{ 1, nil, 0 },
|
||||
{ 2, nil, 0 },
|
||||
{ 3, nil, 0 },
|
||||
{ 4, nil, 0 },
|
||||
{ 5, nil, 0 },
|
||||
{ 6, nil, 0 },
|
||||
{ 7, nil, 0 },
|
||||
{ 8, nil, 0 },
|
||||
{ 9, nil, 0 },
|
||||
{ 10, nil, 0 },
|
||||
{ 11, nil, 0 },
|
||||
{ 12, nil, 0 },
|
||||
{ 13, nil, 0 },
|
||||
{ 14, nil, 0 },
|
||||
{ 15, nil, 0 },
|
||||
})
|
||||
blockTypeDB:addTemp('sapling', {
|
||||
{ '+0', nil, nil },
|
||||
{ '+8', nil, nil },
|
||||
})
|
||||
blockTypeDB:addTemp('leaves', {
|
||||
{ '+0', nil, nil },
|
||||
{ '+4', nil, nil },
|
||||
{ '+8', nil, nil },
|
||||
{ '+12', nil, nil },
|
||||
})
|
||||
blockTypeDB:addTemp('slab', {
|
||||
{ '+0', nil, nil, 'bottom' },
|
||||
{ '+8', nil, nil, 'top' },
|
||||
})
|
||||
blockTypeDB:addTemp('largeplant', {
|
||||
{ '+0', nil, nil, 'east-door', { twoHigh = true } }, -- should use a generic double tall keyword
|
||||
{ '+8', 'minecraft:air', 0 },
|
||||
})
|
||||
blockTypeDB:addTemp('wood', {
|
||||
{ '+0', nil, nil },
|
||||
{ '+4', nil, nil, 'east-west-block' },
|
||||
{ '+8', nil, nil, 'north-south-block' },
|
||||
{ '+12', nil, nil },
|
||||
})
|
||||
blockTypeDB:addTemp('door', {
|
||||
{ 0, nil, 0, 'east-door', { twoHigh = true } },
|
||||
{ 1, nil, 0, 'south-door', { twoHigh = true } },
|
||||
{ 2, nil, 0, 'west-door', { twoHigh = true } },
|
||||
{ 3, nil, 0, 'north-door', { twoHigh = true } },
|
||||
{ 4, nil, 0, 'east-door', { twoHigh = true } },
|
||||
{ 5, nil, 0, 'south-door', { twoHigh = true } },
|
||||
{ 6, nil, 0, 'west-door', { twoHigh = true } },
|
||||
{ 7, nil, 0, 'north-door', { twoHigh = true } },
|
||||
{ 8,'minecraft:air', 0 },
|
||||
{ 9,'minecraft:air', 0 },
|
||||
{ 10,'minecraft:air', 0 },
|
||||
{ 11,'minecraft:air', 0 },
|
||||
{ 12,'minecraft:air', 0 },
|
||||
{ 13,'minecraft:air', 0 },
|
||||
{ 14,'minecraft:air', 0 },
|
||||
{ 15,'minecraft:air', 0 },
|
||||
})
|
||||
blockTypeDB:addTemp('cocoa', {
|
||||
{ 0, nil, 0, 'south-block' },
|
||||
{ 1, nil, 0, 'west-block' },
|
||||
{ 2, nil, 0, 'north-block' },
|
||||
{ 3, nil, 0, 'east-block' },
|
||||
{ 4, nil, 0, 'south-block' },
|
||||
{ 5, nil, 0, 'west-block' },
|
||||
{ 6, nil, 0, 'north-block' },
|
||||
{ 7, nil, 0, 'east-block' },
|
||||
{ 8, nil, 0, 'south-block' },
|
||||
{ 9, nil, 0, 'west-block' },
|
||||
{ 10, nil, 0, 'north-block' },
|
||||
{ 11, nil, 0, 'east-block' },
|
||||
})
|
||||
end
|
||||
|
||||
local Blocks = class()
|
||||
function Blocks:init(args)
|
||||
|
||||
Util.merge(self, args)
|
||||
self.blockDB = blockDB
|
||||
self.nameDB = nameDB
|
||||
|
||||
blockDB:load()
|
||||
-- standardBlockDB:load()
|
||||
blockTypeDB:load()
|
||||
nameDB:load(self.dir, blockDB)
|
||||
-- placementDB:load(standardBlockDB, blockTypeDB)
|
||||
placementDB:load2(blockDB, blockTypeDB)
|
||||
|
||||
-- _G._b = blockDB
|
||||
-- _G._s = standardBlockDB
|
||||
-- _G._bt = blockTypeDB
|
||||
-- _G._p = placementDB
|
||||
|
||||
-- Util.writeTable('pb1.lua', placementDB.data)
|
||||
|
||||
-- placementDB.data = { }
|
||||
|
||||
-- Util.writeTable('pb2.lua', placementDB.data)
|
||||
end
|
||||
|
||||
-- for an ID / dmg (with placement info) - return the correct block (without the placment info embedded in the dmg)
|
||||
function Blocks:getPlaceableBlock(id, dmg)
|
||||
|
||||
local p = placementDB:get({id, dmg})
|
||||
if p then
|
||||
return Util.shallowCopy(p)
|
||||
end
|
||||
|
||||
local b = blockDB:get({id, dmg})
|
||||
if b then
|
||||
return { id = b.strId, dmg = b.dmg }
|
||||
end
|
||||
|
||||
b = blockDB:get({id, 0})
|
||||
if b then
|
||||
return { id = b.strId, dmg = b.dmg }
|
||||
end
|
||||
|
||||
return { id = id, dmg = dmg }
|
||||
end
|
||||
|
||||
return Blocks
|
@@ -1,111 +0,0 @@
|
||||
local class = require('class')
|
||||
local Logger = require('logger')
|
||||
|
||||
local ChestProvider = class()
|
||||
|
||||
function ChestProvider:init(args)
|
||||
|
||||
args = args or { }
|
||||
|
||||
self.stacks = {}
|
||||
self.name = 'chest'
|
||||
self.direction = args.direction or 'up'
|
||||
self.wrapSide = args.wrapSide or 'bottom'
|
||||
self.p = peripheral.wrap(self.wrapSide)
|
||||
end
|
||||
|
||||
function ChestProvider:isValid()
|
||||
return self.p and self.p.getAllStacks
|
||||
end
|
||||
|
||||
function ChestProvider:refresh()
|
||||
if self.p then
|
||||
self.p.condenseItems()
|
||||
self.stacks = self.p.getAllStacks(false)
|
||||
local t = { }
|
||||
for _,s in ipairs(self.stacks) do
|
||||
local key = s.id .. ':' .. s.dmg
|
||||
if t[key] and t[key].qty < 64 then
|
||||
t[key].max_size = t[key].qty
|
||||
else
|
||||
t[key] = {
|
||||
qty = s.qty
|
||||
}
|
||||
end
|
||||
end
|
||||
for _,s in ipairs(self.stacks) do
|
||||
local key = s.id .. ':' .. s.dmg
|
||||
if t[key].max_size then
|
||||
s.max_size = t[key].qty
|
||||
else
|
||||
s.max_size = 64
|
||||
end
|
||||
end
|
||||
end
|
||||
return self.stacks
|
||||
end
|
||||
|
||||
function ChestProvider:getItemInfo(id, dmg)
|
||||
local item = { id = id, dmg = dmg, qty = 0, max_size = 64 }
|
||||
for _,stack in pairs(self.stacks) do
|
||||
if stack.id == id and stack.dmg == dmg then
|
||||
item.name = stack.display_name
|
||||
item.qty = item.qty + stack.qty
|
||||
item.max_size = stack.max_size
|
||||
end
|
||||
end
|
||||
if item.name then
|
||||
return item
|
||||
end
|
||||
end
|
||||
|
||||
function ChestProvider:craft(id, dmg, qty)
|
||||
return false
|
||||
end
|
||||
|
||||
function ChestProvider:craftItems(items)
|
||||
end
|
||||
|
||||
function ChestProvider:provide(item, qty, slot)
|
||||
if self.p then
|
||||
self.stacks = self.p.getAllStacks(false)
|
||||
for key,stack in pairs(self.stacks) do
|
||||
if stack.id == item.id and stack.dmg == item.dmg then
|
||||
local amount = math.min(qty, stack.qty)
|
||||
self.p.pushItemIntoSlot(self.direction, key, amount, slot)
|
||||
qty = qty - amount
|
||||
if qty <= 0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ChestProvider:extract(slot, qty)
|
||||
if self.p then
|
||||
self.p.pushItem(self.direction, slot, qty)
|
||||
end
|
||||
end
|
||||
|
||||
function ChestProvider:insert(slot, qty)
|
||||
if self.p then
|
||||
local s, m = pcall(function() self.p.pullItem(self.direction, slot, qty) end)
|
||||
if not s and m then
|
||||
print('chestProvider:pullItem')
|
||||
print(m)
|
||||
Logger.log('chestProvider', 'Insert failed, trying again')
|
||||
sleep(1)
|
||||
s, m = pcall(function() self.p.pullItem(self.direction, slot, qty) end)
|
||||
if not s and m then
|
||||
print('chestProvider:pullItem')
|
||||
print(m)
|
||||
Logger.log('chestProvider', 'Insert failed again')
|
||||
else
|
||||
Logger.log('chestProvider', 'Insert successful')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return ChestProvider
|
@@ -1,140 +0,0 @@
|
||||
local class = require('class')
|
||||
local Util = require('util')
|
||||
local itemDB = require('itemDB')
|
||||
local Peripheral = require('peripheral')
|
||||
|
||||
local ChestAdapter = class()
|
||||
|
||||
local keys = Util.transpose({
|
||||
'damage',
|
||||
'displayName',
|
||||
'maxCount',
|
||||
'maxDamage',
|
||||
'name',
|
||||
'nbtHash',
|
||||
})
|
||||
|
||||
function ChestAdapter:init(args)
|
||||
local defaults = {
|
||||
items = { },
|
||||
name = 'chest',
|
||||
direction = 'up',
|
||||
wrapSide = 'bottom',
|
||||
}
|
||||
Util.merge(self, defaults)
|
||||
Util.merge(self, args)
|
||||
|
||||
local chest = Peripheral.getBySide(self.wrapSide)
|
||||
if not chest then
|
||||
chest = Peripheral.getByMethod('list')
|
||||
end
|
||||
if chest then
|
||||
Util.merge(self, chest)
|
||||
end
|
||||
end
|
||||
|
||||
function ChestAdapter:isValid()
|
||||
return not not self.list
|
||||
end
|
||||
|
||||
function ChestAdapter:getCachedItemDetails(item, k)
|
||||
local key = { item.name, item.damage, item.nbtHash }
|
||||
|
||||
local detail = itemDB:get(key)
|
||||
if not detail then
|
||||
pcall(function() detail = self.getItemMeta(k) end)
|
||||
if not detail then
|
||||
return
|
||||
end
|
||||
-- NOT SUFFICIENT
|
||||
if detail.name ~= item.name then
|
||||
return
|
||||
end
|
||||
|
||||
for _,k in ipairs(Util.keys(detail)) do
|
||||
if not keys[k] then
|
||||
detail[k] = nil
|
||||
end
|
||||
end
|
||||
|
||||
itemDB:add(key, detail)
|
||||
end
|
||||
if detail then
|
||||
return Util.shallowCopy(detail)
|
||||
end
|
||||
end
|
||||
|
||||
function ChestAdapter:refresh(throttle)
|
||||
return self:listItems(throttle)
|
||||
end
|
||||
|
||||
-- provide a consolidated list of items
|
||||
function ChestAdapter:listItems(throttle)
|
||||
self.cache = { }
|
||||
local items = { }
|
||||
|
||||
throttle = throttle or Util.throttle()
|
||||
|
||||
for k,v in pairs(self.list()) do
|
||||
local key = table.concat({ v.name, v.damage, v.nbtHash }, ':')
|
||||
|
||||
local entry = self.cache[key]
|
||||
if not entry then
|
||||
entry = self:getCachedItemDetails(v, k)
|
||||
if entry then
|
||||
entry.count = 0
|
||||
self.cache[key] = entry
|
||||
table.insert(items, entry)
|
||||
end
|
||||
end
|
||||
|
||||
if entry then
|
||||
entry.count = entry.count + v.count
|
||||
end
|
||||
throttle()
|
||||
end
|
||||
|
||||
itemDB:flush()
|
||||
|
||||
return items
|
||||
end
|
||||
|
||||
function ChestAdapter:getItemInfo(name, damage, nbtHash)
|
||||
if not self.cache then
|
||||
self:listItems()
|
||||
end
|
||||
local key = table.concat({ name, damage, nbtHash }, ':')
|
||||
return self.cache[key]
|
||||
end
|
||||
|
||||
function ChestAdapter:craft(name, damage, qty)
|
||||
end
|
||||
|
||||
function ChestAdapter:craftItems(items)
|
||||
end
|
||||
|
||||
function ChestAdapter:provide(item, qty, slot, direction)
|
||||
local stacks = self.list()
|
||||
for key,stack in pairs(stacks) do
|
||||
if stack.name == item.name and stack.damage == item.damage then
|
||||
local amount = math.min(qty, stack.count)
|
||||
if amount > 0 then
|
||||
self.pushItems(direction or self.direction, key, amount, slot)
|
||||
end
|
||||
qty = qty - amount
|
||||
if qty <= 0 then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ChestAdapter:extract(slot, qty, toSlot)
|
||||
self.pushItems(self.direction, slot, qty, toSlot)
|
||||
end
|
||||
|
||||
function ChestAdapter:insert(slot, qty)
|
||||
self.pullItems(self.direction, slot, qty)
|
||||
end
|
||||
|
||||
return ChestAdapter
|
@@ -1,870 +0,0 @@
|
||||
--[[
|
||||
|
||||
LUA MODULE
|
||||
|
||||
compress.deflatelua - deflate (and gunzip/zlib) implemented in Lua.
|
||||
|
||||
SYNOPSIS
|
||||
|
||||
local DEFLATE = require 'compress.deflatelua'
|
||||
-- uncompress gzip file
|
||||
local fh = assert(io.open'foo.txt.gz', 'rb')
|
||||
local ofh = assert(io.open'foo.txt', 'wb')
|
||||
DEFLATE.gunzip {input=fh, output=ofh}
|
||||
fh:close(); ofh:close()
|
||||
-- can also uncompress from string including zlib and raw DEFLATE formats.
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
This is a pure Lua implementation of decompressing the DEFLATE format,
|
||||
including the related zlib and gzip formats.
|
||||
|
||||
Note: This library only supports decompression.
|
||||
Compression is not currently implemented.
|
||||
|
||||
API
|
||||
|
||||
Note: in the following functions, input stream `fh` may be
|
||||
a file handle, string, or an iterator function that returns strings.
|
||||
Output stream `ofh` may be a file handle or a function that
|
||||
consumes one byte (number 0..255) per call.
|
||||
|
||||
DEFLATE.inflate {input=fh, output=ofh}
|
||||
|
||||
Decompresses input stream `fh` in the DEFLATE format
|
||||
while writing to output stream `ofh`.
|
||||
DEFLATE is detailed in http://tools.ietf.org/html/rfc1951 .
|
||||
|
||||
DEFLATE.gunzip {input=fh, output=ofh, disable_crc=disable_crc}
|
||||
|
||||
Decompresses input stream `fh` with the gzip format
|
||||
while writing to output stream `ofh`.
|
||||
`disable_crc` (defaults to `false`) will disable CRC-32 checking
|
||||
to increase speed.
|
||||
gzip is detailed in http://tools.ietf.org/html/rfc1952 .
|
||||
|
||||
DEFLATE.inflate_zlib {input=fh, output=ofh, disable_crc=disable_crc}
|
||||
|
||||
Decompresses input stream `fh` with the zlib format
|
||||
while writing to output stream `ofh`.
|
||||
`disable_crc` (defaults to `false`) will disable CRC-32 checking
|
||||
to increase speed.
|
||||
zlib is detailed in http://tools.ietf.org/html/rfc1950 .
|
||||
|
||||
DEFLATE.adler32(byte, crc) --> rcrc
|
||||
|
||||
Returns adler32 checksum of byte `byte` (number 0..255) appended
|
||||
to string with adler32 checksum `crc`. This is internally used by
|
||||
`inflate_zlib`.
|
||||
ADLER32 in detailed in http://tools.ietf.org/html/rfc1950 .
|
||||
|
||||
COMMAND LINE UTILITY
|
||||
|
||||
A `gunziplua` command line utility (in folder `bin`) is also provided.
|
||||
This mimicks the *nix `gunzip` utility but is a pure Lua implementation
|
||||
that invokes this library. For help do
|
||||
|
||||
gunziplua -h
|
||||
|
||||
DEPENDENCIES
|
||||
|
||||
Requires 'digest.crc32lua' (used for optional CRC-32 checksum checks).
|
||||
https://github.com/davidm/lua-digest-crc32lua
|
||||
|
||||
Will use a bit library ('bit', 'bit32', 'bit.numberlua') if available. This
|
||||
is not that critical for this library but is required by digest.crc32lua.
|
||||
|
||||
'pythonic.optparse' is only required by the optional `gunziplua`
|
||||
command-line utilty for command line parsing.
|
||||
https://github.com/davidm/lua-pythonic-optparse
|
||||
|
||||
INSTALLATION
|
||||
|
||||
Copy the `compress` directory into your LUA_PATH.
|
||||
|
||||
REFERENCES
|
||||
|
||||
[1] DEFLATE Compressed Data Format Specification version 1.3
|
||||
http://tools.ietf.org/html/rfc1951
|
||||
[2] GZIP file format specification version 4.3
|
||||
http://tools.ietf.org/html/rfc1952
|
||||
[3] http://en.wikipedia.org/wiki/DEFLATE
|
||||
[4] pyflate, by Paul Sladen
|
||||
http://www.paul.sladen.org/projects/pyflate/
|
||||
[5] Compress::Zlib::Perl - partial pure Perl implementation of
|
||||
Compress::Zlib
|
||||
http://search.cpan.org/~nwclark/Compress-Zlib-Perl/Perl.pm
|
||||
|
||||
LICENSE
|
||||
|
||||
(c) 2008-2011 David Manura. Licensed under the same terms as Lua (MIT).
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
(end license)
|
||||
--]]
|
||||
|
||||
local M = {_TYPE='module', _NAME='compress.deflatelua', _VERSION='0.3.20111128'}
|
||||
|
||||
local assert = assert
|
||||
local error = error
|
||||
local ipairs = ipairs
|
||||
local pairs = pairs
|
||||
local print = print
|
||||
local require = require
|
||||
local tostring = tostring
|
||||
local type = type
|
||||
local setmetatable = setmetatable
|
||||
local io = io
|
||||
local math = math
|
||||
local table_sort = table.sort
|
||||
local math_max = math.max
|
||||
local string_char = string.char
|
||||
|
||||
--[[
|
||||
Requires the first module listed that exists, else raises like `require`.
|
||||
If a non-string is encountered, it is returned.
|
||||
Second return value is module name loaded (or '').
|
||||
--]]
|
||||
local function requireany(...)
|
||||
local errs = {}
|
||||
for i = 1, select('#', ...) do local name = select(i, ...)
|
||||
if type(name) ~= 'string' then return name, '' end
|
||||
local ok, mod = pcall(require, name)
|
||||
if ok then return mod, name end
|
||||
errs[#errs+1] = mod
|
||||
end
|
||||
error(table.concat(errs, '\n'), 2)
|
||||
end
|
||||
|
||||
|
||||
--local crc32 = require "digest.crc32lua" . crc32_byte
|
||||
--local bit, name_ = requireany('bit', 'bit32', 'bit.numberlua', nil)
|
||||
local bit
|
||||
local crc32
|
||||
|
||||
local DEBUG = false
|
||||
|
||||
-- Whether to use `bit` library functions in current module.
|
||||
-- Unlike the crc32 library, it doesn't make much difference in this module.
|
||||
local NATIVE_BITOPS = (bit ~= nil)
|
||||
|
||||
local band, lshift, rshift
|
||||
if NATIVE_BITOPS then
|
||||
band = bit.band
|
||||
lshift = bit.lshift
|
||||
rshift = bit.rshift
|
||||
end
|
||||
|
||||
|
||||
local function warn(s)
|
||||
io.stderr:write(s, '\n')
|
||||
end
|
||||
|
||||
|
||||
local function debug(...)
|
||||
print('DEBUG', ...)
|
||||
end
|
||||
|
||||
|
||||
local function runtime_error(s, level)
|
||||
level = level or 1
|
||||
error({s}, level+1)
|
||||
end
|
||||
|
||||
|
||||
local function make_outstate(outbs)
|
||||
local outstate = {}
|
||||
outstate.outbs = outbs
|
||||
outstate.window = {}
|
||||
outstate.window_pos = 1
|
||||
return outstate
|
||||
end
|
||||
|
||||
|
||||
local function output(outstate, byte)
|
||||
-- debug('OUTPUT:', s)
|
||||
local window_pos = outstate.window_pos
|
||||
outstate.outbs(byte)
|
||||
outstate.window[window_pos] = byte
|
||||
outstate.window_pos = window_pos % 32768 + 1 -- 32K
|
||||
end
|
||||
|
||||
|
||||
local function noeof(val)
|
||||
return assert(val, 'unexpected end of file')
|
||||
end
|
||||
|
||||
|
||||
local function hasbit(bits, bit)
|
||||
return bits % (bit + bit) >= bit
|
||||
end
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
-- small optimization (lookup table for powers of 2)
|
||||
local pow2 = memoize(function(n) return 2^n end)
|
||||
|
||||
--local tbits = memoize(
|
||||
-- function(bits)
|
||||
-- return memoize( function(bit) return getbit(bits, bit) end )
|
||||
-- end )
|
||||
|
||||
|
||||
-- weak metatable marking objects as bitstream type
|
||||
local is_bitstream = setmetatable({}, {__mode='k'})
|
||||
|
||||
|
||||
-- DEBUG
|
||||
-- prints LSB first
|
||||
--[[
|
||||
local function bits_tostring(bits, nbits)
|
||||
local s = ''
|
||||
local tmp = bits
|
||||
local function f()
|
||||
local b = tmp % 2 == 1 and 1 or 0
|
||||
s = s .. b
|
||||
tmp = (tmp - b) / 2
|
||||
end
|
||||
if nbits then
|
||||
for i=1,nbits do f() end
|
||||
else
|
||||
while tmp ~= 0 do f() end
|
||||
end
|
||||
|
||||
return s
|
||||
end
|
||||
--]]
|
||||
|
||||
local function bytestream_from_file(fh)
|
||||
local o = {}
|
||||
function o:read()
|
||||
local sb = fh:read(1)
|
||||
if sb then return sb:byte() end
|
||||
end
|
||||
return o
|
||||
end
|
||||
|
||||
|
||||
local function bytestream_from_string(s)
|
||||
local i = 1
|
||||
local o = {}
|
||||
function o:read()
|
||||
local by
|
||||
if i <= #s then
|
||||
by = s:byte(i)
|
||||
i = i + 1
|
||||
end
|
||||
return by
|
||||
end
|
||||
return o
|
||||
end
|
||||
|
||||
|
||||
local function bytestream_from_function(f)
|
||||
local i = 0
|
||||
local buffer = ''
|
||||
local o = {}
|
||||
function o:read()
|
||||
return f()
|
||||
-- i = i + 1
|
||||
-- if i > #buffer then
|
||||
-- buffer = f()
|
||||
-- if not buffer then return end
|
||||
-- i = 1
|
||||
-- end
|
||||
-- return buffer:byte(i,i)
|
||||
end
|
||||
return o
|
||||
end
|
||||
|
||||
|
||||
local function bitstream_from_bytestream(bys)
|
||||
local buf_byte = 0
|
||||
local buf_nbit = 0
|
||||
local o = {}
|
||||
|
||||
function o:nbits_left_in_byte()
|
||||
return buf_nbit
|
||||
end
|
||||
|
||||
if NATIVE_BITOPS then
|
||||
function o:read(nbits)
|
||||
nbits = nbits or 1
|
||||
while buf_nbit < nbits do
|
||||
local byte = bys:read()
|
||||
if not byte then return end -- note: more calls also return nil
|
||||
buf_byte = buf_byte + lshift(byte, buf_nbit)
|
||||
buf_nbit = buf_nbit + 8
|
||||
end
|
||||
local bits
|
||||
if nbits == 0 then
|
||||
bits = 0
|
||||
elseif nbits == 32 then
|
||||
bits = buf_byte
|
||||
buf_byte = 0
|
||||
else
|
||||
bits = band(buf_byte, rshift(0xffffffff, 32 - nbits))
|
||||
buf_byte = rshift(buf_byte, nbits)
|
||||
end
|
||||
buf_nbit = buf_nbit - nbits
|
||||
return bits
|
||||
end
|
||||
else
|
||||
function o:read(nbits)
|
||||
nbits = nbits or 1
|
||||
while buf_nbit < nbits do
|
||||
local byte = bys:read()
|
||||
if not byte then return end -- note: more calls also return nil
|
||||
buf_byte = buf_byte + pow2[buf_nbit] * byte
|
||||
buf_nbit = buf_nbit + 8
|
||||
end
|
||||
local m = pow2[nbits]
|
||||
local bits = buf_byte % m
|
||||
buf_byte = (buf_byte - bits) / m
|
||||
buf_nbit = buf_nbit - nbits
|
||||
return bits
|
||||
end
|
||||
end
|
||||
|
||||
is_bitstream[o] = true
|
||||
|
||||
return o
|
||||
end
|
||||
|
||||
|
||||
local function get_bitstream(o)
|
||||
local bs
|
||||
if is_bitstream[o] then
|
||||
return o
|
||||
elseif io.type(o) == 'file' then
|
||||
bs = bitstream_from_bytestream(bytestream_from_file(o))
|
||||
elseif type(o) == 'string' then
|
||||
bs = bitstream_from_bytestream(bytestream_from_string(o))
|
||||
elseif type(o) == 'function' then
|
||||
bs = bitstream_from_bytestream(bytestream_from_function(o))
|
||||
else
|
||||
runtime_error 'unrecognized type'
|
||||
end
|
||||
return bs
|
||||
end
|
||||
|
||||
|
||||
local function get_obytestream(o)
|
||||
local bs
|
||||
if io.type(o) == 'file' then
|
||||
bs = function(sbyte) o:write(string_char(sbyte)) end
|
||||
elseif type(o) == 'function' then
|
||||
bs = o
|
||||
else
|
||||
runtime_error('unrecognized type: ' .. tostring(o))
|
||||
end
|
||||
return bs
|
||||
end
|
||||
|
||||
|
||||
local function HuffmanTable(init, is_full)
|
||||
local t = {}
|
||||
if is_full then
|
||||
for val,nbits in pairs(init) do
|
||||
if nbits ~= 0 then
|
||||
t[#t+1] = {val=val, nbits=nbits}
|
||||
--debug('*',val,nbits)
|
||||
end
|
||||
end
|
||||
else
|
||||
for i=1,#init-2,2 do
|
||||
local firstval, nbits, nextval = init[i], init[i+1], init[i+2]
|
||||
--debug(val, nextval, nbits)
|
||||
if nbits ~= 0 then
|
||||
for val=firstval,nextval-1 do
|
||||
t[#t+1] = {val=val, nbits=nbits}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
table_sort(t, function(a,b)
|
||||
return a.nbits == b.nbits and a.val < b.val or a.nbits < b.nbits
|
||||
end)
|
||||
|
||||
-- assign codes
|
||||
local code = 1 -- leading 1 marker
|
||||
local nbits = 0
|
||||
for i,s in ipairs(t) do
|
||||
if s.nbits ~= nbits then
|
||||
code = code * pow2[s.nbits - nbits]
|
||||
nbits = s.nbits
|
||||
end
|
||||
s.code = code
|
||||
--debug('huffman code:', i, s.nbits, s.val, code, bits_tostring(code))
|
||||
code = code + 1
|
||||
end
|
||||
|
||||
local minbits = math.huge
|
||||
local look = {}
|
||||
for i,s in ipairs(t) do
|
||||
minbits = math.min(minbits, s.nbits)
|
||||
look[s.code] = s.val
|
||||
end
|
||||
|
||||
--for _,o in ipairs(t) do
|
||||
-- debug(':', o.nbits, o.val)
|
||||
--end
|
||||
|
||||
-- function t:lookup(bits) return look[bits] end
|
||||
|
||||
local msb = NATIVE_BITOPS and function(bits, nbits)
|
||||
local res = 0
|
||||
for i=1,nbits do
|
||||
res = lshift(res, 1) + band(bits, 1)
|
||||
bits = rshift(bits, 1)
|
||||
end
|
||||
return res
|
||||
end or function(bits, nbits)
|
||||
local res = 0
|
||||
for i=1,nbits do
|
||||
local b = bits % 2
|
||||
bits = (bits - b) / 2
|
||||
res = res * 2 + b
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local tfirstcode = memoize(
|
||||
function(bits) return pow2[minbits] + msb(bits, minbits) end)
|
||||
|
||||
function t:read(bs)
|
||||
local code = 1 -- leading 1 marker
|
||||
local nbits = 0
|
||||
while 1 do
|
||||
if nbits == 0 then -- small optimization (optional)
|
||||
code = tfirstcode[noeof(bs:read(minbits))]
|
||||
nbits = nbits + minbits
|
||||
else
|
||||
local b = noeof(bs:read())
|
||||
nbits = nbits + 1
|
||||
code = code * 2 + b -- MSB first
|
||||
--[[NATIVE_BITOPS
|
||||
code = lshift(code, 1) + b -- MSB first
|
||||
--]]
|
||||
end
|
||||
--debug('code?', code, bits_tostring(code))
|
||||
local val = look[code]
|
||||
if val then
|
||||
--debug('FOUND', val)
|
||||
return val
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
|
||||
local function parse_gzip_header(bs)
|
||||
-- local FLG_FTEXT = 2^0
|
||||
local FLG_FHCRC = 2^1
|
||||
local FLG_FEXTRA = 2^2
|
||||
local FLG_FNAME = 2^3
|
||||
local FLG_FCOMMENT = 2^4
|
||||
|
||||
local id1 = bs:read(8)
|
||||
local id2 = bs:read(8)
|
||||
if id1 ~= 31 or id2 ~= 139 then
|
||||
runtime_error 'not in gzip format'
|
||||
end
|
||||
local cm = bs:read(8) -- compression method
|
||||
local flg = bs:read(8) -- FLaGs
|
||||
local mtime = bs:read(32) -- Modification TIME
|
||||
local xfl = bs:read(8) -- eXtra FLags
|
||||
local os = bs:read(8) -- Operating System
|
||||
|
||||
if DEBUG then
|
||||
debug("CM=", cm)
|
||||
debug("FLG=", flg)
|
||||
debug("MTIME=", mtime)
|
||||
-- debug("MTIME_str=",os.date("%Y-%m-%d %H:%M:%S",mtime)) -- non-portable
|
||||
debug("XFL=", xfl)
|
||||
debug("OS=", os)
|
||||
end
|
||||
|
||||
if not os then runtime_error 'invalid header' end
|
||||
|
||||
if hasbit(flg, FLG_FEXTRA) then
|
||||
local xlen = bs:read(16)
|
||||
local extra = 0
|
||||
for i=1,xlen do
|
||||
extra = bs:read(8)
|
||||
end
|
||||
if not extra then runtime_error 'invalid header' end
|
||||
end
|
||||
|
||||
local function parse_zstring(bs)
|
||||
repeat
|
||||
local by = bs:read(8)
|
||||
if not by then runtime_error 'invalid header' end
|
||||
until by == 0
|
||||
end
|
||||
|
||||
if hasbit(flg, FLG_FNAME) then
|
||||
parse_zstring(bs)
|
||||
end
|
||||
|
||||
if hasbit(flg, FLG_FCOMMENT) then
|
||||
parse_zstring(bs)
|
||||
end
|
||||
|
||||
if hasbit(flg, FLG_FHCRC) then
|
||||
local crc16 = bs:read(16)
|
||||
if not crc16 then runtime_error 'invalid header' end
|
||||
-- IMPROVE: check CRC. where is an example .gz file that
|
||||
-- has this set?
|
||||
if DEBUG then
|
||||
debug("CRC16=", crc16)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function parse_zlib_header(bs)
|
||||
local cm = bs:read(4) -- Compression Method
|
||||
local cinfo = bs:read(4) -- Compression info
|
||||
local fcheck = bs:read(5) -- FLaGs: FCHECK (check bits for CMF and FLG)
|
||||
local fdict = bs:read(1) -- FLaGs: FDICT (present dictionary)
|
||||
local flevel = bs:read(2) -- FLaGs: FLEVEL (compression level)
|
||||
local cmf = cinfo * 16 + cm -- CMF (Compresion Method and flags)
|
||||
local flg = fcheck + fdict * 32 + flevel * 64 -- FLaGs
|
||||
|
||||
if cm ~= 8 then -- not "deflate"
|
||||
runtime_error("unrecognized zlib compression method: " + cm)
|
||||
end
|
||||
if cinfo > 7 then
|
||||
runtime_error("invalid zlib window size: cinfo=" + cinfo)
|
||||
end
|
||||
local window_size = 2^(cinfo + 8)
|
||||
|
||||
if (cmf*256 + flg) % 31 ~= 0 then
|
||||
runtime_error("invalid zlib header (bad fcheck sum)")
|
||||
end
|
||||
|
||||
if fdict == 1 then
|
||||
runtime_error("FIX:TODO - FDICT not currently implemented")
|
||||
local dictid_ = bs:read(32)
|
||||
end
|
||||
|
||||
return window_size
|
||||
end
|
||||
|
||||
local function parse_huffmantables(bs)
|
||||
local hlit = bs:read(5) -- # of literal/length codes - 257
|
||||
local hdist = bs:read(5) -- # of distance codes - 1
|
||||
local hclen = noeof(bs:read(4)) -- # of code length codes - 4
|
||||
|
||||
local ncodelen_codes = hclen + 4
|
||||
local codelen_init = {}
|
||||
local codelen_vals = {
|
||||
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}
|
||||
for i=1,ncodelen_codes do
|
||||
local nbits = bs:read(3)
|
||||
local val = codelen_vals[i]
|
||||
codelen_init[val] = nbits
|
||||
end
|
||||
local codelentable = HuffmanTable(codelen_init, true)
|
||||
|
||||
local function decode(ncodes)
|
||||
local init = {}
|
||||
local nbits
|
||||
local val = 0
|
||||
while val < ncodes do
|
||||
local codelen = codelentable:read(bs)
|
||||
--FIX:check nil?
|
||||
local nrepeat
|
||||
if codelen <= 15 then
|
||||
nrepeat = 1
|
||||
nbits = codelen
|
||||
--debug('w', nbits)
|
||||
elseif codelen == 16 then
|
||||
nrepeat = 3 + noeof(bs:read(2))
|
||||
-- nbits unchanged
|
||||
elseif codelen == 17 then
|
||||
nrepeat = 3 + noeof(bs:read(3))
|
||||
nbits = 0
|
||||
elseif codelen == 18 then
|
||||
nrepeat = 11 + noeof(bs:read(7))
|
||||
nbits = 0
|
||||
else
|
||||
error 'ASSERT'
|
||||
end
|
||||
for i=1,nrepeat do
|
||||
init[val] = nbits
|
||||
val = val + 1
|
||||
end
|
||||
end
|
||||
local huffmantable = HuffmanTable(init, true)
|
||||
return huffmantable
|
||||
end
|
||||
|
||||
local nlit_codes = hlit + 257
|
||||
local ndist_codes = hdist + 1
|
||||
|
||||
local littable = decode(nlit_codes)
|
||||
local disttable = decode(ndist_codes)
|
||||
|
||||
return littable, disttable
|
||||
end
|
||||
|
||||
|
||||
local tdecode_len_base
|
||||
local tdecode_len_nextrabits
|
||||
local tdecode_dist_base
|
||||
local tdecode_dist_nextrabits
|
||||
local function parse_compressed_item(bs, outstate, littable, disttable)
|
||||
local val = littable:read(bs)
|
||||
--debug(val, val < 256 and string_char(val))
|
||||
if val < 256 then -- literal
|
||||
output(outstate, val)
|
||||
elseif val == 256 then -- end of block
|
||||
return true
|
||||
else
|
||||
if not tdecode_len_base then
|
||||
local t = {[257]=3}
|
||||
local skip = 1
|
||||
for i=258,285,4 do
|
||||
for j=i,i+3 do t[j] = t[j-1] + skip end
|
||||
if i ~= 258 then skip = skip * 2 end
|
||||
end
|
||||
t[285] = 258
|
||||
tdecode_len_base = t
|
||||
--for i=257,285 do debug('T1',i,t[i]) end
|
||||
end
|
||||
if not tdecode_len_nextrabits then
|
||||
local t = {}
|
||||
if NATIVE_BITOPS then
|
||||
for i=257,285 do
|
||||
local j = math_max(i - 261, 0)
|
||||
t[i] = rshift(j, 2)
|
||||
end
|
||||
else
|
||||
for i=257,285 do
|
||||
local j = math_max(i - 261, 0)
|
||||
t[i] = (j - (j % 4)) / 4
|
||||
end
|
||||
end
|
||||
t[285] = 0
|
||||
tdecode_len_nextrabits = t
|
||||
--for i=257,285 do debug('T2',i,t[i]) end
|
||||
end
|
||||
local len_base = tdecode_len_base[val]
|
||||
local nextrabits = tdecode_len_nextrabits[val]
|
||||
local extrabits = bs:read(nextrabits)
|
||||
local len = len_base + extrabits
|
||||
|
||||
if not tdecode_dist_base then
|
||||
local t = {[0]=1}
|
||||
local skip = 1
|
||||
for i=1,29,2 do
|
||||
for j=i,i+1 do t[j] = t[j-1] + skip end
|
||||
if i ~= 1 then skip = skip * 2 end
|
||||
end
|
||||
tdecode_dist_base = t
|
||||
--for i=0,29 do debug('T3',i,t[i]) end
|
||||
end
|
||||
if not tdecode_dist_nextrabits then
|
||||
local t = {}
|
||||
if NATIVE_BITOPS then
|
||||
for i=0,29 do
|
||||
local j = math_max(i - 2, 0)
|
||||
t[i] = rshift(j, 1)
|
||||
end
|
||||
else
|
||||
for i=0,29 do
|
||||
local j = math_max(i - 2, 0)
|
||||
t[i] = (j - (j % 2)) / 2
|
||||
end
|
||||
end
|
||||
tdecode_dist_nextrabits = t
|
||||
--for i=0,29 do debug('T4',i,t[i]) end
|
||||
end
|
||||
local dist_val = disttable:read(bs)
|
||||
local dist_base = tdecode_dist_base[dist_val]
|
||||
local dist_nextrabits = tdecode_dist_nextrabits[dist_val]
|
||||
local dist_extrabits = bs:read(dist_nextrabits)
|
||||
local dist = dist_base + dist_extrabits
|
||||
|
||||
--debug('BACK', len, dist)
|
||||
for i=1,len do
|
||||
local pos = (outstate.window_pos - 1 - dist) % 32768 + 1 -- 32K
|
||||
output(outstate, assert(outstate.window[pos], 'invalid distance'))
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
local function parse_block(bs, outstate)
|
||||
local bfinal = bs:read(1)
|
||||
local btype = bs:read(2)
|
||||
|
||||
local BTYPE_NO_COMPRESSION = 0
|
||||
local BTYPE_FIXED_HUFFMAN = 1
|
||||
local BTYPE_DYNAMIC_HUFFMAN = 2
|
||||
local BTYPE_RESERVED_ = 3
|
||||
|
||||
if DEBUG then
|
||||
debug('bfinal=', bfinal)
|
||||
debug('btype=', btype)
|
||||
end
|
||||
|
||||
if btype == BTYPE_NO_COMPRESSION then
|
||||
bs:read(bs:nbits_left_in_byte())
|
||||
local len = bs:read(16)
|
||||
local nlen_ = noeof(bs:read(16))
|
||||
|
||||
for i=1,len do
|
||||
local by = noeof(bs:read(8))
|
||||
output(outstate, by)
|
||||
end
|
||||
elseif btype == BTYPE_FIXED_HUFFMAN or btype == BTYPE_DYNAMIC_HUFFMAN then
|
||||
local littable, disttable
|
||||
if btype == BTYPE_DYNAMIC_HUFFMAN then
|
||||
littable, disttable = parse_huffmantables(bs)
|
||||
else
|
||||
littable = HuffmanTable {0,8, 144,9, 256,7, 280,8, 288,nil}
|
||||
disttable = HuffmanTable {0,5, 32,nil}
|
||||
end
|
||||
|
||||
repeat
|
||||
local is_done = parse_compressed_item(
|
||||
bs, outstate, littable, disttable)
|
||||
until is_done
|
||||
else
|
||||
runtime_error 'unrecognized compression type'
|
||||
end
|
||||
|
||||
return bfinal ~= 0
|
||||
end
|
||||
|
||||
|
||||
function M.inflate(t)
|
||||
local bs = get_bitstream(t.input)
|
||||
local outbs = get_obytestream(t.output)
|
||||
local outstate = make_outstate(outbs)
|
||||
|
||||
repeat
|
||||
local is_final = parse_block(bs, outstate)
|
||||
until is_final
|
||||
end
|
||||
local inflate = M.inflate
|
||||
|
||||
|
||||
function M.gunzip(t)
|
||||
local bs = get_bitstream(t.input)
|
||||
local outbs = get_obytestream(t.output)
|
||||
local disable_crc = t.disable_crc
|
||||
if disable_crc == nil then disable_crc = false end
|
||||
|
||||
parse_gzip_header(bs)
|
||||
|
||||
local data_crc32 = 0
|
||||
|
||||
inflate{input=bs, output=
|
||||
disable_crc and outbs or
|
||||
function(byte)
|
||||
data_crc32 = crc32(byte, data_crc32)
|
||||
outbs(byte)
|
||||
end
|
||||
}
|
||||
|
||||
bs:read(bs:nbits_left_in_byte())
|
||||
|
||||
local expected_crc32 = bs:read(32)
|
||||
local isize = bs:read(32) -- ignored
|
||||
if DEBUG then
|
||||
debug('crc32=', expected_crc32)
|
||||
debug('isize=', isize)
|
||||
end
|
||||
if not disable_crc and data_crc32 then
|
||||
if data_crc32 ~= expected_crc32 then
|
||||
runtime_error('invalid compressed data--crc error')
|
||||
end
|
||||
end
|
||||
if bs:read() then
|
||||
warn 'trailing garbage ignored'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function M.adler32(byte, crc)
|
||||
local s1 = crc % 65536
|
||||
local s2 = (crc - s1) / 65536
|
||||
s1 = (s1 + byte) % 65521
|
||||
s2 = (s2 + s1) % 65521
|
||||
return s2*65536 + s1
|
||||
end -- 65521 is the largest prime smaller than 2^16
|
||||
|
||||
|
||||
function M.inflate_zlib(t)
|
||||
local bs = get_bitstream(t.input)
|
||||
local outbs = get_obytestream(t.output)
|
||||
local disable_crc = t.disable_crc
|
||||
if disable_crc == nil then disable_crc = false end
|
||||
|
||||
local window_size_ = parse_zlib_header(bs)
|
||||
|
||||
local data_adler32 = 1
|
||||
|
||||
inflate{input=bs, output=
|
||||
disable_crc and outbs or
|
||||
function(byte)
|
||||
data_adler32 = M.adler32(byte, data_adler32)
|
||||
outbs(byte)
|
||||
end
|
||||
}
|
||||
|
||||
bs:read(bs:nbits_left_in_byte())
|
||||
|
||||
local b3 = bs:read(8)
|
||||
local b2 = bs:read(8)
|
||||
local b1 = bs:read(8)
|
||||
local b0 = bs:read(8)
|
||||
local expected_adler32 = ((b3*256 + b2)*256 + b1)*256 + b0
|
||||
if DEBUG then
|
||||
debug('alder32=', expected_adler32)
|
||||
end
|
||||
if not disable_crc then
|
||||
if data_adler32 ~= expected_adler32 then
|
||||
runtime_error('invalid compressed data--crc error')
|
||||
end
|
||||
end
|
||||
if bs:read() then
|
||||
warn 'trailing garbage ignored'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return M
|
@@ -9,8 +9,10 @@ function gitfs.mount(dir, user, repo, branch)
|
||||
|
||||
local list = git.list(user, repo, branch)
|
||||
for path, entry in pairs(list) do
|
||||
local node = fs.mount(fs.combine(dir, path), 'urlfs', entry.url)
|
||||
node.size = entry.size
|
||||
if not fs.exists(fs.combine(dir, path)) then
|
||||
local node = fs.mount(fs.combine(dir, path), 'urlfs', entry.url)
|
||||
node.size = entry.size
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@@ -38,6 +38,11 @@ end
|
||||
|
||||
function urlfs.open(node, fn, fl)
|
||||
|
||||
if fl == 'w' or fl == 'wb' then
|
||||
fs.delete(fn)
|
||||
return fs.open(fn, fl)
|
||||
end
|
||||
|
||||
if fl ~= 'r' and fl ~= 'rb' then
|
||||
error('Unsupported mode')
|
||||
end
|
||||
|
@@ -1,112 +0,0 @@
|
||||
local input = { }
|
||||
|
||||
function input:translate(event, code)
|
||||
|
||||
if event == 'key' then
|
||||
local ch = keys.getName(code)
|
||||
if ch then
|
||||
|
||||
if code == keys.leftCtrl or code == keys.rightCtrl then
|
||||
self.control = true
|
||||
self.combo = false
|
||||
return
|
||||
end
|
||||
|
||||
if code == keys.leftShift or code == keys.rightShift then
|
||||
self.shift = true
|
||||
self.combo = false
|
||||
return
|
||||
end
|
||||
|
||||
if self.shift then
|
||||
if #ch > 1 then
|
||||
ch = 'shift-' .. ch
|
||||
elseif self.control then
|
||||
-- will create control-X
|
||||
-- better than shift-control-x
|
||||
ch = ch:upper()
|
||||
end
|
||||
self.combo = true
|
||||
end
|
||||
|
||||
if self.control then
|
||||
ch = 'control-' .. ch
|
||||
self.combo = true
|
||||
-- even return numbers such as
|
||||
-- control-seven
|
||||
return ch
|
||||
end
|
||||
|
||||
-- filter out characters that will be processed in
|
||||
-- the subsequent char event
|
||||
if ch and #ch > 1 and (code < 2 or code > 11) then
|
||||
return ch
|
||||
end
|
||||
end
|
||||
|
||||
elseif event == 'key_up' then
|
||||
|
||||
if code == keys.leftCtrl or code == keys.rightCtrl then
|
||||
self.control = false
|
||||
elseif code == keys.leftShift or code == keys.rightShift then
|
||||
self.shift = false
|
||||
else
|
||||
return
|
||||
end
|
||||
|
||||
-- only send through the shift / control event if it wasn't
|
||||
-- used in combination with another event
|
||||
if not self.combo then
|
||||
return keys.getName(code)
|
||||
end
|
||||
|
||||
elseif event == 'char' then
|
||||
if not self.control then
|
||||
self.combo = true
|
||||
return event
|
||||
end
|
||||
|
||||
elseif event == 'mouse_click' then
|
||||
|
||||
local buttons = { 'mouse_click', 'mouse_rightclick', 'mouse_doubleclick' }
|
||||
|
||||
self.combo = true
|
||||
if self.shift then
|
||||
return 'shift-' .. buttons[code]
|
||||
end
|
||||
return buttons[code]
|
||||
|
||||
elseif event == "mouse_scroll" then
|
||||
local directions = {
|
||||
[ -1 ] = 'scrollUp',
|
||||
[ 1 ] = 'scrollDown'
|
||||
}
|
||||
return directions[code]
|
||||
|
||||
elseif event == 'paste' then
|
||||
self.combo = true
|
||||
return event
|
||||
|
||||
elseif event == 'mouse_drag' then
|
||||
return event
|
||||
end
|
||||
end
|
||||
|
||||
-- can be useful for testing what keys are generated
|
||||
function input:test()
|
||||
print('press a key...')
|
||||
while true do
|
||||
local e, code = os.pullEvent()
|
||||
|
||||
if e == 'char' and code == 'q' then
|
||||
break
|
||||
end
|
||||
|
||||
local ch = input:translate(e, code)
|
||||
if ch then
|
||||
print(e .. ' ' .. code .. ' ' .. ch)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return input
|
@@ -1,39 +0,0 @@
|
||||
local Util = require('util')
|
||||
local TableDB = require('tableDB')
|
||||
|
||||
local itemDB = TableDB({ fileName = 'usr/etc/items.db' })
|
||||
|
||||
function itemDB:get(key)
|
||||
|
||||
local item = TableDB.get(self, key)
|
||||
|
||||
if item then
|
||||
return item
|
||||
end
|
||||
|
||||
if key[2] ~= 0 then
|
||||
item = TableDB.get(self, { key[1], 0, key[3] })
|
||||
if item and item.maxDamage > 0 then
|
||||
item = Util.shallowCopy(item)
|
||||
item.damage = key[2]
|
||||
item.displayName = string.format('%s (damage: %d)', item.displayName, item.damage)
|
||||
return item
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function itemDB:add(key, item)
|
||||
|
||||
if item.maxDamage > 0 then
|
||||
key = { key[1], 0, key[3] }
|
||||
end
|
||||
TableDB.add(self, key, item)
|
||||
end
|
||||
|
||||
function itemDB:makeKey(item)
|
||||
return { item.name, item.damage, item.nbtHash }
|
||||
end
|
||||
|
||||
itemDB:load()
|
||||
|
||||
return itemDB
|
167
sys/apis/me.lua
167
sys/apis/me.lua
@@ -1,167 +0,0 @@
|
||||
local Util = require('util')
|
||||
|
||||
local ME = {
|
||||
jobList = { }
|
||||
}
|
||||
|
||||
function ME.setDevice(device)
|
||||
ME.p = device
|
||||
--Util.merge(ME, ME.p)
|
||||
|
||||
if not device then
|
||||
error('ME device not attached')
|
||||
end
|
||||
|
||||
for k,v in pairs(ME.p) do
|
||||
if not ME[k] then
|
||||
ME[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ME.isAvailable()
|
||||
return not Util.empty(ME.getAvailableItems())
|
||||
end
|
||||
|
||||
-- Strip off color prefix
|
||||
local function safeString(text)
|
||||
|
||||
local val = text:byte(1)
|
||||
|
||||
if val < 32 or val > 128 then
|
||||
|
||||
local newText = {}
|
||||
for i = 4, #text do
|
||||
local val = text:byte(i)
|
||||
newText[i - 3] = (val > 31 and val < 127) and val or 63
|
||||
end
|
||||
return string.char(unpack(newText))
|
||||
end
|
||||
|
||||
return text
|
||||
end
|
||||
|
||||
function ME.getAvailableItems()
|
||||
local items
|
||||
pcall(function()
|
||||
items = ME.p.getAvailableItems('all')
|
||||
for k,v in pairs(items) do
|
||||
v.id = v.item.id
|
||||
v.name = safeString(v.item.display_name)
|
||||
v.qty = v.item.qty
|
||||
v.dmg = v.item.dmg
|
||||
v.max_dmg = v.item.max_dmg
|
||||
v.nbt_hash = v.item.nbt_hash
|
||||
end
|
||||
end)
|
||||
|
||||
return items or { }
|
||||
end
|
||||
|
||||
function ME.getItemCount(id, dmg, nbt_hash, ignore_dmg)
|
||||
|
||||
local fingerprint = {
|
||||
id = id,
|
||||
nbt_hash = nbt_hash,
|
||||
}
|
||||
|
||||
if not ignore_dmg or ignore_dmg ~= 'yes' then
|
||||
fingerprint.dmg = dmg or 0
|
||||
end
|
||||
|
||||
local item = ME.getItemDetail(fingerprint, false)
|
||||
|
||||
if item then
|
||||
return item.qty
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function ME.extract(id, dmg, nbt_hash, qty, direction, slot)
|
||||
dmg = dmg or 0
|
||||
qty = qty or 1
|
||||
direction = direction or 'up'
|
||||
return pcall(function()
|
||||
local fingerprint = {
|
||||
dmg = dmg,
|
||||
id = id,
|
||||
nbt_hash = nbt_hash
|
||||
}
|
||||
return ME.exportItem(fingerprint, direction, qty, slot)
|
||||
end)
|
||||
end
|
||||
|
||||
function ME.insert(slot, qty, direction)
|
||||
direction = direction or 'up'
|
||||
return ME.pullItem(direction, slot, qty)
|
||||
end
|
||||
|
||||
function ME.isCrafting()
|
||||
local cpus = ME.p.getCraftingCPUs() or { }
|
||||
for k,v in pairs(cpus) do
|
||||
if v.busy then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ME.isCPUAvailable()
|
||||
local cpus = ME.p.getCraftingCPUs() or { }
|
||||
local available = false
|
||||
|
||||
for cpu,v in pairs(cpus) do
|
||||
if not v.busy then
|
||||
available = true
|
||||
elseif not ME.jobList[cpu] then -- something else is crafting something (don't know what)
|
||||
return false -- return false since we are in an unknown state
|
||||
end
|
||||
end
|
||||
return available
|
||||
end
|
||||
|
||||
function ME.getJobList()
|
||||
|
||||
local cpus = ME.p.getCraftingCPUs() or { }
|
||||
for cpu,v in pairs(cpus) do
|
||||
if not v.busy then
|
||||
ME.jobList[cpu] = nil
|
||||
end
|
||||
end
|
||||
|
||||
return ME.jobList
|
||||
end
|
||||
|
||||
function ME.craft(id, dmg, nbt_hash, qty)
|
||||
local cpus = ME.p.getCraftingCPUs() or { }
|
||||
for cpu,v in pairs(cpus) do
|
||||
if not v.busy then
|
||||
ME.p.requestCrafting({
|
||||
id = id,
|
||||
dmg = dmg or 0,
|
||||
nbt_hash = nbt_hash,
|
||||
},
|
||||
qty or 1,
|
||||
cpu
|
||||
)
|
||||
|
||||
os.sleep(0) -- tell it to craft, yet it doesn't show busy - try waiting a cycle...
|
||||
cpus = ME.p.getCraftingCPUs() or { }
|
||||
if not cpus[cpu].busy then
|
||||
-- print('sleeping again')
|
||||
os.sleep(.1) -- sigh
|
||||
cpus = ME.p.getCraftingCPUs() or { }
|
||||
end
|
||||
|
||||
-- not working :(
|
||||
if cpus[cpu].busy then
|
||||
ME.jobList[cpu] = { id = id, dmg = dmg, qty = qty, nbt_hash = nbt_hash }
|
||||
return true
|
||||
end
|
||||
break -- only need to try the first available cpu
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
return ME
|
@@ -1,157 +0,0 @@
|
||||
local class = require('class')
|
||||
local Util = require('util')
|
||||
local Peripheral = require('peripheral')
|
||||
|
||||
local MEProvider = class()
|
||||
|
||||
function MEProvider:init(args)
|
||||
local defaults = {
|
||||
items = { },
|
||||
name = 'ME',
|
||||
}
|
||||
Util.merge(self, defaults)
|
||||
Util.merge(self, args)
|
||||
|
||||
if self.side then
|
||||
local mep = peripheral.wrap('bottom')
|
||||
if mep then
|
||||
Util.merge(self, mep)
|
||||
end
|
||||
else
|
||||
local mep = Peripheral.getByMethod('getAvailableItems')
|
||||
if mep then
|
||||
Util.merge(self, mep)
|
||||
end
|
||||
end
|
||||
|
||||
local sides = {
|
||||
top = 'down',
|
||||
bottom = 'up',
|
||||
east = 'west',
|
||||
west = 'east',
|
||||
north = 'south',
|
||||
south = 'north',
|
||||
}
|
||||
self.oside = sides[self.direction or self.side]
|
||||
end
|
||||
|
||||
function MEProvider:isValid()
|
||||
return self.getAvailableItems and self.getAvailableItems()
|
||||
end
|
||||
|
||||
-- Strip off color prefix
|
||||
local function safeString(text)
|
||||
|
||||
local val = text:byte(1)
|
||||
|
||||
if val < 32 or val > 128 then
|
||||
|
||||
local newText = {}
|
||||
for i = 4, #text do
|
||||
local val = text:byte(i)
|
||||
newText[i - 3] = (val > 31 and val < 127) and val or 63
|
||||
end
|
||||
return string.char(unpack(newText))
|
||||
end
|
||||
|
||||
return text
|
||||
end
|
||||
|
||||
local convertNames = {
|
||||
name = 'id',
|
||||
damage = 'dmg',
|
||||
maxCount = 'max_size',
|
||||
count = 'qty',
|
||||
displayName = 'display_name',
|
||||
maxDamage = 'max_dmg',
|
||||
}
|
||||
|
||||
local function convertItem(item)
|
||||
for k,v in pairs(convertNames) do
|
||||
item[k] = item[v]
|
||||
item[v] = nil
|
||||
end
|
||||
item.displayName = safeString(item.displayName)
|
||||
end
|
||||
|
||||
function MEProvider:refresh()
|
||||
self.items = self.getAvailableItems('all')
|
||||
for _,v in pairs(self.items) do
|
||||
Util.merge(v, v.item)
|
||||
convertItem(v)
|
||||
end
|
||||
return self.items
|
||||
end
|
||||
|
||||
function MEProvider:listItems()
|
||||
self:refresh()
|
||||
return self.items
|
||||
end
|
||||
|
||||
function MEProvider:getItemInfo(name, damage)
|
||||
|
||||
for key,item in pairs(self.items) do
|
||||
if item.name == name and item.damage == damage then
|
||||
return item
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MEProvider:craft(name, damage, count)
|
||||
|
||||
self:refresh()
|
||||
|
||||
local item = self:getItemInfo(name, damage)
|
||||
|
||||
if item and item.is_craftable then
|
||||
|
||||
self.requestCrafting({ id = name, dmg = damage }, count)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function MEProvider:craftItems(items)
|
||||
local cpus = self.getCraftingCPUs() or { }
|
||||
local count = 0
|
||||
|
||||
for _,cpu in pairs(cpus) do
|
||||
if cpu.busy then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
for _,item in pairs(items) do
|
||||
if count >= #cpus then
|
||||
break
|
||||
end
|
||||
if self:craft(item.name, item.damage, item.count) then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function MEProvider:provide(item, count, slot)
|
||||
return pcall(function()
|
||||
self.exportItem({
|
||||
id = item.name,
|
||||
dmg = item.damage
|
||||
}, self.oside, count, slot)
|
||||
end)
|
||||
end
|
||||
|
||||
function MEProvider:insert(slot, count)
|
||||
local s, m = pcall(function() self.pullItem(self.oside, slot, count) end)
|
||||
if not s and m then
|
||||
print('MEProvider:pullItem')
|
||||
print(m)
|
||||
sleep(1)
|
||||
s, m = pcall(function() self.pullItem(self.oside, slot, count) end)
|
||||
if not s and m then
|
||||
print('MEProvider:pullItem')
|
||||
print(m)
|
||||
read()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return MEProvider
|
@@ -1,106 +0,0 @@
|
||||
local Event = require('event')
|
||||
local Logger = require('logger')
|
||||
|
||||
local Message = { }
|
||||
|
||||
local messageHandlers = {}
|
||||
|
||||
function Message.enable()
|
||||
if not device.wireless_modem.isOpen(os.getComputerID()) then
|
||||
device.wireless_modem.open(os.getComputerID())
|
||||
end
|
||||
if not device.wireless_modem.isOpen(60000) then
|
||||
device.wireless_modem.open(60000)
|
||||
end
|
||||
end
|
||||
|
||||
if device and device.wireless_modem then
|
||||
Message.enable()
|
||||
end
|
||||
|
||||
Event.on('device_attach', function(event, deviceName)
|
||||
if deviceName == 'wireless_modem' then
|
||||
Message.enable()
|
||||
end
|
||||
end)
|
||||
|
||||
function Message.addHandler(type, f)
|
||||
table.insert(messageHandlers, {
|
||||
type = type,
|
||||
f = f,
|
||||
enabled = true
|
||||
})
|
||||
end
|
||||
|
||||
function Message.removeHandler(h)
|
||||
for k,v in pairs(messageHandlers) do
|
||||
if v == h then
|
||||
messageHandlers[k] = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Event.on('modem_message',
|
||||
function(event, side, sendChannel, replyChannel, msg, distance)
|
||||
if msg and msg.type then -- filter out messages from other systems
|
||||
local id = replyChannel
|
||||
Logger.log('modem_receive', { id, msg.type })
|
||||
--Logger.log('modem_receive', msg.contents)
|
||||
for k,h in pairs(messageHandlers) do
|
||||
if h.type == msg.type then
|
||||
-- should provide msg.contents instead of message - type is already known
|
||||
h.f(h, id, msg, distance)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
function Message.send(id, msgType, contents)
|
||||
if not device.wireless_modem then
|
||||
error('No modem attached', 2)
|
||||
end
|
||||
|
||||
if id then
|
||||
Logger.log('modem_send', { tostring(id), msgType })
|
||||
device.wireless_modem.transmit(id, os.getComputerID(), {
|
||||
type = msgType, contents = contents
|
||||
})
|
||||
else
|
||||
Logger.log('modem_send', { 'broadcast', msgType })
|
||||
device.wireless_modem.transmit(60000, os.getComputerID(), {
|
||||
type = msgType, contents = contents
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function Message.broadcast(t, contents)
|
||||
if not device.wireless_modem then
|
||||
error('No modem attached', 2)
|
||||
end
|
||||
|
||||
Message.send(nil, t, contents)
|
||||
-- Logger.log('rednet_send', { 'broadcast', t })
|
||||
-- rednet.broadcast({ type = t, contents = contents })
|
||||
end
|
||||
|
||||
function Message.waitForMessage(msgType, timeout, fromId)
|
||||
local timerId = os.startTimer(timeout)
|
||||
repeat
|
||||
local e, side, _id, id, msg, distance = os.pullEvent()
|
||||
if e == 'modem_message' then
|
||||
if msg and msg.type and msg.type == msgType then
|
||||
if not fromId or id == fromId then
|
||||
return e, id, msg, distance
|
||||
end
|
||||
end
|
||||
end
|
||||
until e == 'timer' and side == timerId
|
||||
end
|
||||
|
||||
function Message.enableWirelessLogging()
|
||||
Logger.setWirelessLogging()
|
||||
end
|
||||
|
||||
return Message
|
@@ -172,6 +172,7 @@ function Point.inBox(pt, box)
|
||||
pt.y >= box.y and
|
||||
pt.z >= box.z and
|
||||
pt.x <= box.ex and
|
||||
pt.y <= box.ey and
|
||||
pt.z <= box.ez
|
||||
end
|
||||
|
||||
|
@@ -1,51 +0,0 @@
|
||||
local Util = require('util')
|
||||
local Logger = require('logger')
|
||||
|
||||
local Profile = {
|
||||
start = function() end,
|
||||
stop = function() end,
|
||||
display = function() end,
|
||||
methods = { },
|
||||
}
|
||||
|
||||
local function Profile_display()
|
||||
Logger.log('profile', 'Profiling results')
|
||||
for k,v in pairs(Profile.methods) do
|
||||
Logger.log('profile', '%s: %f %d %f',
|
||||
k, Util.round(v.elapsed, 2), v.count, Util.round(v.elapsed/v.count, 2))
|
||||
end
|
||||
Profile.methods = { }
|
||||
end
|
||||
|
||||
local function Profile_start(name)
|
||||
local p = Profile.methods[name]
|
||||
if not p then
|
||||
p = { }
|
||||
p.elapsed = 0
|
||||
p.count = 0
|
||||
Profile.methods[name] = p
|
||||
end
|
||||
p.clock = os.clock()
|
||||
return p
|
||||
end
|
||||
|
||||
local function Profile_stop(name)
|
||||
local p = Profile.methods[name]
|
||||
p.elapsed = p.elapsed + (os.clock() - p.clock)
|
||||
p.count = p.count + 1
|
||||
end
|
||||
|
||||
function Profile.enable()
|
||||
Logger.log('profile', 'Profiling enabled')
|
||||
Profile.start = Profile_start
|
||||
Profile.stop = Profile_stop
|
||||
Profile.display = Profile_display
|
||||
end
|
||||
|
||||
function Profile.disable()
|
||||
Profile.start = function() end
|
||||
Profile.stop = function() end
|
||||
Profile.display = function() end
|
||||
end
|
||||
|
||||
return Profile
|
@@ -1,143 +0,0 @@
|
||||
local class = require('class')
|
||||
local Util = require('util')
|
||||
local Peripheral = require('peripheral')
|
||||
local itemDB = require('itemDB')
|
||||
|
||||
local RefinedAdapter = class()
|
||||
|
||||
local keys = {
|
||||
'damage',
|
||||
'displayName',
|
||||
'maxCount',
|
||||
'maxDamage',
|
||||
'name',
|
||||
'nbtHash',
|
||||
}
|
||||
|
||||
function RefinedAdapter:init(args)
|
||||
local defaults = {
|
||||
items = { },
|
||||
name = 'refinedStorage',
|
||||
}
|
||||
Util.merge(self, defaults)
|
||||
Util.merge(self, args)
|
||||
|
||||
local controller = Peripheral.getByType('refinedstorage:controller')
|
||||
if controller then
|
||||
Util.merge(self, controller)
|
||||
end
|
||||
end
|
||||
|
||||
function RefinedAdapter:isValid()
|
||||
return not not self.listAvailableItems
|
||||
end
|
||||
|
||||
function RefinedAdapter:isOnline()
|
||||
return self.getNetworkEnergyStored() > 0
|
||||
end
|
||||
|
||||
function RefinedAdapter:getCachedItemDetails(item)
|
||||
local key = { item.name, item.damage, item.nbtHash }
|
||||
|
||||
local detail = itemDB:get(key)
|
||||
if not detail then
|
||||
detail = self.findItem(item)
|
||||
if detail then
|
||||
local meta
|
||||
pcall(function() meta = detail.getMetadata() end)
|
||||
if not meta then
|
||||
return
|
||||
end
|
||||
Util.merge(detail, meta)
|
||||
|
||||
local t = { }
|
||||
for _,k in pairs(keys) do
|
||||
t[k] = detail[k]
|
||||
end
|
||||
|
||||
detail = t
|
||||
itemDB:add(key, detail)
|
||||
end
|
||||
end
|
||||
if detail then
|
||||
return Util.shallowCopy(detail)
|
||||
end
|
||||
end
|
||||
|
||||
function RefinedAdapter:listItems()
|
||||
local items = { }
|
||||
local list
|
||||
|
||||
pcall(function()
|
||||
list = self.listAvailableItems()
|
||||
end)
|
||||
|
||||
if list then
|
||||
|
||||
local throttle = Util.throttle()
|
||||
|
||||
for _,v in pairs(list) do
|
||||
local item = self:getCachedItemDetails(v)
|
||||
if item then
|
||||
item.count = v.count
|
||||
table.insert(items, item)
|
||||
end
|
||||
throttle()
|
||||
end
|
||||
itemDB:flush()
|
||||
end
|
||||
|
||||
return items
|
||||
end
|
||||
|
||||
function RefinedAdapter:getItemInfo(fingerprint)
|
||||
|
||||
local key = { fingerprint.name, fingerprint.damage, fingerprint.nbtHash }
|
||||
|
||||
local item = itemDB:get(key)
|
||||
if not item then
|
||||
return self:getCachedItemDetails(fingerprint)
|
||||
end
|
||||
|
||||
local detail = self.findItem(item)
|
||||
if detail then
|
||||
item.count = detail.count
|
||||
return item
|
||||
end
|
||||
end
|
||||
|
||||
function RefinedAdapter:isCrafting(item)
|
||||
for _,task in pairs(self.getCraftingTasks()) do
|
||||
local output = task.getPattern().outputs[1]
|
||||
if output.name == item.name and
|
||||
output.damage == item.damage and
|
||||
output.nbtHash == item.nbtHash then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function RefinedAdapter:craft(item, qty)
|
||||
local detail = self.findItem(item)
|
||||
if detail then
|
||||
return detail.craft(qty)
|
||||
end
|
||||
end
|
||||
|
||||
function RefinedAdapter:craftItems(items)
|
||||
return false
|
||||
end
|
||||
|
||||
function RefinedAdapter:provide(item, qty, slot)
|
||||
end
|
||||
|
||||
function RefinedAdapter:extract(slot, qty)
|
||||
-- self.pushItems(self.direction, slot, qty)
|
||||
end
|
||||
|
||||
function RefinedAdapter:insert(slot, qty)
|
||||
-- self.pullItems(self.direction, slot, qty)
|
||||
end
|
||||
|
||||
return RefinedAdapter
|
File diff suppressed because it is too large
Load Diff
@@ -1,54 +0,0 @@
|
||||
local class = require('class')
|
||||
local Util = require('util')
|
||||
|
||||
local TableDB = class()
|
||||
function TableDB:init(args)
|
||||
local defaults = {
|
||||
fileName = '',
|
||||
dirty = false,
|
||||
data = { },
|
||||
tabledef = { },
|
||||
}
|
||||
Util.merge(defaults, args)
|
||||
Util.merge(self, defaults)
|
||||
end
|
||||
|
||||
function TableDB:load()
|
||||
local table = Util.readTable(self.fileName)
|
||||
if table then
|
||||
self.data = table.data
|
||||
self.tabledef = table.tabledef
|
||||
end
|
||||
end
|
||||
|
||||
function TableDB:add(key, entry)
|
||||
if type(key) == 'table' then
|
||||
key = table.concat(key, ':')
|
||||
end
|
||||
self.data[key] = entry
|
||||
self.dirty = true
|
||||
end
|
||||
|
||||
function TableDB:get(key)
|
||||
if type(key) == 'table' then
|
||||
key = table.concat(key, ':')
|
||||
end
|
||||
return self.data[key]
|
||||
end
|
||||
|
||||
function TableDB:remove(key)
|
||||
self.data[key] = nil
|
||||
self.dirty = true
|
||||
end
|
||||
|
||||
function TableDB:flush()
|
||||
if self.dirty then
|
||||
Util.writeTable(self.fileName, {
|
||||
-- tabledef = self.tabledef,
|
||||
data = self.data,
|
||||
})
|
||||
self.dirty = false
|
||||
end
|
||||
end
|
||||
|
||||
return TableDB
|
@@ -1,166 +0,0 @@
|
||||
local itemDB = require('itemDB')
|
||||
local Util = require('util')
|
||||
|
||||
local Craft = { }
|
||||
|
||||
local function clearGrid(chestAdapter)
|
||||
for i = 1, 16 do
|
||||
local count = turtle.getItemCount(i)
|
||||
if count > 0 then
|
||||
chestAdapter:insert(i, count)
|
||||
if turtle.getItemCount(i) ~= 0 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function splitKey(key)
|
||||
local t = Util.split(key, '(.-):')
|
||||
local item = { }
|
||||
if #t[#t] > 2 then
|
||||
item.nbtHash = table.remove(t)
|
||||
end
|
||||
item.damage = tonumber(table.remove(t))
|
||||
item.name = table.concat(t, ':')
|
||||
return item
|
||||
end
|
||||
|
||||
local function getItemCount(items, key)
|
||||
local item = splitKey(key)
|
||||
for _,v in pairs(items) do
|
||||
if v.name == item.name and
|
||||
v.damage == item.damage and
|
||||
v.nbtHash == item.nbtHash then
|
||||
return v.count
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function turtleCraft(recipe, qty, chestAdapter)
|
||||
|
||||
clearGrid(chestAdapter)
|
||||
|
||||
for k,v in pairs(recipe.ingredients) do
|
||||
local item = splitKey(v)
|
||||
chestAdapter:provide(item, qty, k)
|
||||
if turtle.getItemCount(k) == 0 then -- ~= qty then
|
||||
-- FIX: ingredients cannot be stacked
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return turtle.craft()
|
||||
end
|
||||
|
||||
function Craft.craftRecipe(recipe, count, chestAdapter)
|
||||
|
||||
local items = chestAdapter:listItems()
|
||||
|
||||
local function sumItems(items)
|
||||
-- produces { ['minecraft:planks:0'] = 8 }
|
||||
local t = {}
|
||||
for _,item in pairs(items) do
|
||||
t[item] = (t[item] or 0) + 1
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
count = math.ceil(count / recipe.count)
|
||||
|
||||
local maxCount = recipe.maxCount or math.floor(64 / recipe.count)
|
||||
local summedItems = sumItems(recipe.ingredients)
|
||||
|
||||
for key,icount in pairs(summedItems) do
|
||||
local itemCount = getItemCount(items, key)
|
||||
if itemCount < icount * count then
|
||||
local irecipe = Craft.recipes[key]
|
||||
if irecipe then
|
||||
Util.print('Crafting %d %s', icount * count - itemCount, key)
|
||||
if not Craft.craftRecipe(irecipe,
|
||||
icount * count - itemCount,
|
||||
chestAdapter) then
|
||||
turtle.select(1)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
repeat
|
||||
if not turtleCraft(recipe, math.min(count, maxCount), chestAdapter) then
|
||||
turtle.select(1)
|
||||
return false
|
||||
end
|
||||
count = count - maxCount
|
||||
until count <= 0
|
||||
|
||||
turtle.select(1)
|
||||
return true
|
||||
end
|
||||
|
||||
-- given a certain quantity, return how many of those can be crafted
|
||||
function Craft.getCraftableAmount(recipe, count, items)
|
||||
|
||||
local function sumItems(recipe, items, summedItems, count)
|
||||
|
||||
local canCraft = 0
|
||||
|
||||
for i = 1, count do
|
||||
for _,item in pairs(recipe.ingredients) do
|
||||
local summedItem = summedItems[item] or getItemCount(items, item)
|
||||
|
||||
local irecipe = Craft.recipes[item]
|
||||
if irecipe and summedItem <= 0 then
|
||||
summedItem = summedItem + sumItems(irecipe, items, summedItems, 1)
|
||||
end
|
||||
if summedItem <= 0 then
|
||||
return canCraft
|
||||
end
|
||||
summedItems[item] = summedItem - 1
|
||||
end
|
||||
canCraft = canCraft + recipe.count
|
||||
end
|
||||
|
||||
return canCraft
|
||||
end
|
||||
|
||||
return sumItems(recipe, items, { }, math.ceil(count / recipe.count))
|
||||
end
|
||||
|
||||
function Craft.canCraft(item, count, items)
|
||||
return Craft.getCraftableAmount(Craft.recipes[item], count, items) == count
|
||||
end
|
||||
|
||||
function Craft.setRecipes(recipes)
|
||||
Craft.recipes = recipes
|
||||
end
|
||||
|
||||
function Craft.getCraftableAmountTest()
|
||||
local results = { }
|
||||
Craft.setRecipes(Util.readTable('sys/etc/recipes.db'))
|
||||
|
||||
local items = {
|
||||
{ name = 'minecraft:planks', damage = 0, count = 5 },
|
||||
{ name = 'minecraft:log', damage = 0, count = 2 },
|
||||
}
|
||||
results[1] = { item = 'chest', expected = 1, got = Craft.getCraftableAmount(Craft.recipes['minecraft:chest:0'], 2, items) }
|
||||
|
||||
items = {
|
||||
{ name = 'minecraft:log', damage = 0, count = 1 },
|
||||
{ name = 'minecraft:coal', damage = 1, count = 1 },
|
||||
}
|
||||
results[2] = { item = 'torch', expected = 4, got = Craft.getCraftableAmount(Craft.recipes['minecraft:torch:0'], 4, items) }
|
||||
|
||||
return results
|
||||
end
|
||||
|
||||
function Craft.craftRecipeTest(name, count)
|
||||
local ChestAdapter = require('chestAdapter18')
|
||||
local chestAdapter = ChestAdapter({ wrapSide = 'top', direction = 'down' })
|
||||
Craft.setRecipes(Util.readTable('usr/etc/recipes.db'))
|
||||
return { Craft.craftRecipe(Craft.recipes[name], count, chestAdapter) }
|
||||
end
|
||||
|
||||
return Craft
|
@@ -1,165 +0,0 @@
|
||||
local Point = require('point')
|
||||
local Util = require('util')
|
||||
|
||||
local checkedNodes = { }
|
||||
local nodes = { }
|
||||
local box = { }
|
||||
local oldCallback
|
||||
|
||||
local function toKey(pt)
|
||||
return table.concat({ pt.x, pt.y, pt.z }, ':')
|
||||
end
|
||||
|
||||
local function addNode(node)
|
||||
|
||||
for i = 0, 5 do
|
||||
local hi = turtle.getHeadingInfo(i)
|
||||
local testNode = { x = node.x + hi.xd, y = node.y + hi.yd, z = node.z + hi.zd }
|
||||
|
||||
if Point.inBox(testNode, box) then
|
||||
local key = toKey(testNode)
|
||||
if not checkedNodes[key] then
|
||||
nodes[key] = testNode
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function dig(action)
|
||||
|
||||
local directions = {
|
||||
top = 'up',
|
||||
bottom = 'down',
|
||||
}
|
||||
|
||||
-- convert to up, down, north, south, east, west
|
||||
local direction = directions[action.side] or
|
||||
turtle.getHeadingInfo(turtle.point.heading).direction
|
||||
|
||||
local hi = turtle.getHeadingInfo(direction)
|
||||
local node = { x = turtle.point.x + hi.xd, y = turtle.point.y + hi.yd, z = turtle.point.z + hi.zd }
|
||||
|
||||
if Point.inBox(node, box) then
|
||||
|
||||
local key = toKey(node)
|
||||
checkedNodes[key] = true
|
||||
nodes[key] = nil
|
||||
|
||||
if action.dig() then
|
||||
addNode(node)
|
||||
repeat until not action.dig() -- sand, etc
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function move(action)
|
||||
if action == 'turn' then
|
||||
dig(turtle.getAction('forward'))
|
||||
elseif action == 'up' then
|
||||
dig(turtle.getAction('up'))
|
||||
dig(turtle.getAction('forward'))
|
||||
elseif action == 'down' then
|
||||
dig(turtle.getAction('down'))
|
||||
dig(turtle.getAction('forward'))
|
||||
elseif action == 'back' then
|
||||
dig(turtle.getAction('up'))
|
||||
dig(turtle.getAction('down'))
|
||||
end
|
||||
|
||||
if oldCallback then
|
||||
oldCallback(action)
|
||||
end
|
||||
end
|
||||
|
||||
-- find the closest block
|
||||
-- * favor same plane
|
||||
-- * going backwards only if the dest is above or below
|
||||
function closestPoint(reference, pts)
|
||||
local lpt, lm -- lowest
|
||||
for _,pt in pairs(pts) do
|
||||
local m = Point.turtleDistance(reference, pt)
|
||||
local h = Point.calculateHeading(reference, pt)
|
||||
local t = Point.calculateTurns(reference.heading, h)
|
||||
if pt.y ~= reference.y then -- try and stay on same plane
|
||||
m = m + .01
|
||||
end
|
||||
if t ~= 2 or pt.y == reference.y then
|
||||
m = m + t
|
||||
if t > 0 then
|
||||
m = m + .01
|
||||
end
|
||||
end
|
||||
if not lm or m < lm then
|
||||
lpt = pt
|
||||
lm = m
|
||||
end
|
||||
end
|
||||
return lpt
|
||||
end
|
||||
|
||||
local function getAdjacentPoint(pt)
|
||||
local t = { }
|
||||
table.insert(t, pt)
|
||||
for i = 0, 5 do
|
||||
local hi = turtle.getHeadingInfo(i)
|
||||
local heading
|
||||
if i < 4 then
|
||||
heading = (hi.heading + 2) % 4
|
||||
end
|
||||
table.insert(t, { x = pt.x + hi.xd, z = pt.z + hi.zd, y = pt.y + hi.yd, heading = heading })
|
||||
end
|
||||
|
||||
return closestPoint(turtle.getPoint(), t)
|
||||
end
|
||||
|
||||
return function(startPt, endPt, firstPt, verbose)
|
||||
|
||||
checkedNodes = { }
|
||||
nodes = { }
|
||||
box = { }
|
||||
|
||||
box.x = math.min(startPt.x, endPt.x)
|
||||
box.y = math.min(startPt.y, endPt.y)
|
||||
box.z = math.min(startPt.z, endPt.z)
|
||||
box.ex = math.max(startPt.x, endPt.x)
|
||||
box.ey = math.max(startPt.y, endPt.y)
|
||||
box.ez = math.max(startPt.z, endPt.z)
|
||||
|
||||
if not turtle.pathfind(firstPt) then
|
||||
error('failed to reach starting point')
|
||||
end
|
||||
|
||||
turtle.setPolicy("attack", { dig = dig }, "assuredMove")
|
||||
|
||||
oldCallback = turtle.getMoveCallback()
|
||||
turtle.setMoveCallback(move)
|
||||
|
||||
repeat
|
||||
local key = toKey(turtle.point)
|
||||
|
||||
checkedNodes[key] = true
|
||||
nodes[key] = nil
|
||||
|
||||
dig(turtle.getAction('down'))
|
||||
dig(turtle.getAction('up'))
|
||||
dig(turtle.getAction('forward'))
|
||||
|
||||
if verbose then
|
||||
print(string.format('%d nodes remaining', Util.size(nodes)))
|
||||
end
|
||||
|
||||
if Util.size(nodes) == 0 then
|
||||
break
|
||||
end
|
||||
|
||||
local node = closestPoint(turtle.point, nodes)
|
||||
node = getAdjacentPoint(node)
|
||||
if not turtle.gotoPoint(node) then
|
||||
break
|
||||
end
|
||||
until turtle.abort
|
||||
|
||||
turtle.resetState()
|
||||
turtle.setMoveCallback(oldCallback)
|
||||
end
|
@@ -88,10 +88,6 @@ local function mapDimensions(dest, blocks, boundingBox)
|
||||
}
|
||||
end
|
||||
|
||||
local function nodeToString(n)
|
||||
return string.format('%d:%d:%d:%d', n._x, n._y, n._z, n.__heading or 9)
|
||||
end
|
||||
|
||||
-- shifting and coordinate flipping
|
||||
local function pointToMap(dim, pt)
|
||||
return { x = pt.x + dim.ox, z = pt.y + dim.oy, y = pt.z + dim.oz }
|
||||
@@ -144,15 +140,34 @@ local function addSensorBlocks(blocks, sblocks)
|
||||
end
|
||||
end
|
||||
|
||||
local function selectDestination(pts, box, map, dim)
|
||||
|
||||
while #pts > 0 do
|
||||
local pt = Point.closest(turtle.point, pts)
|
||||
|
||||
if (box and not Point.inBox(pt, box)) or
|
||||
map[pt.z + dim.oz][pt.x + dim.ox][pt.y + dim.oy] == 1 then
|
||||
Util.removeByValue(pts, pt)
|
||||
else
|
||||
return pt
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function pathTo(dest, options)
|
||||
|
||||
local blocks = options.blocks or { }
|
||||
local allDests = options.dest or { } -- support alternative destinations
|
||||
local blocks = options.blocks or turtle.getState().blocks or { }
|
||||
local dests = options.dest or { dest } -- support alternative destinations
|
||||
local box = options.box or turtle.getState().box
|
||||
|
||||
local lastDim = nil
|
||||
local map = nil
|
||||
local grid = nil
|
||||
|
||||
if box then
|
||||
box = Point.normalizeBox(box)
|
||||
end
|
||||
|
||||
-- Creates a pathfinder object
|
||||
local myFinder = Pathfinder(grid, 'ASTAR', walkable)
|
||||
|
||||
@@ -162,7 +177,7 @@ local function pathTo(dest, options)
|
||||
while turtle.point.x ~= dest.x or turtle.point.z ~= dest.z or turtle.point.y ~= dest.y do
|
||||
|
||||
-- map expands as we encounter obstacles
|
||||
local dim = mapDimensions(dest, blocks, options.box)
|
||||
local dim = mapDimensions(dest, blocks, box)
|
||||
|
||||
-- reuse map if possible
|
||||
if not lastDim or not dimsAreEqual(dim, lastDim) then
|
||||
@@ -179,6 +194,15 @@ local function pathTo(dest, options)
|
||||
addBlock(map, dim, b)
|
||||
end
|
||||
|
||||
dest = selectDestination(dests, box, map, dim)
|
||||
if not dest then
|
||||
error('failed to reach destination')
|
||||
-- return false, 'failed to reach destination'
|
||||
end
|
||||
if turtle.point.x == dest.x and turtle.point.z == dest.z and turtle.point.y == dest.y then
|
||||
break
|
||||
end
|
||||
|
||||
-- Define start and goal locations coordinates
|
||||
local startPt = pointToMap(dim, turtle.point)
|
||||
local endPt = pointToMap(dim, dest)
|
||||
@@ -187,14 +211,8 @@ local function pathTo(dest, options)
|
||||
local path = myFinder:getPath(startPt.x, startPt.y, startPt.z, turtle.point.heading, endPt.x, endPt.y, endPt.z, dest.heading)
|
||||
|
||||
if not path then
|
||||
Util.removeByValue(allDests, dest)
|
||||
dest = Point.closest(turtle.point, allDests)
|
||||
|
||||
if not dest then
|
||||
return false, 'failed to recalculate'
|
||||
end
|
||||
Util.removeByValue(dests, dest)
|
||||
else
|
||||
|
||||
for node, count in path:nodes() do
|
||||
local pt = nodeToPoint(dim, node)
|
||||
|
||||
@@ -206,9 +224,6 @@ local function pathTo(dest, options)
|
||||
-- when encountering obstacles -- IS THIS RIGHT ??
|
||||
if not turtle.gotoSingleTurn(pt.x, pt.z, pt.y, node.heading) then
|
||||
table.insert(blocks, pt)
|
||||
if #allDests > 0 then
|
||||
dest = Point.closest(turtle.point, allDests)
|
||||
end
|
||||
--if device.turtlesensorenvironment then
|
||||
-- addSensorBlocks(blocks, device.turtlesensorenvironment.sonicScan())
|
||||
--end
|
||||
@@ -224,10 +239,27 @@ local function pathTo(dest, options)
|
||||
return dest
|
||||
end
|
||||
|
||||
return function(dest, options)
|
||||
options = options or { }
|
||||
if not options.blocks and turtle.gotoPoint(dest) then
|
||||
return dest
|
||||
end
|
||||
return pathTo(dest, options)
|
||||
end
|
||||
return {
|
||||
pathfind = function(dest, options)
|
||||
options = options or { }
|
||||
--if not options.blocks and turtle.gotoPoint(dest) then
|
||||
-- return dest
|
||||
--end
|
||||
return pathTo(dest, options)
|
||||
end,
|
||||
|
||||
-- set a global bounding box
|
||||
-- box can be overridden by passing box in pathfind options
|
||||
setBox = function(box)
|
||||
turtle.getState().box = box
|
||||
end,
|
||||
|
||||
setBlocks = function(blocks)
|
||||
turtle.getState().blocks = blocks
|
||||
end,
|
||||
|
||||
reset = function()
|
||||
turtle.getState().box = nil
|
||||
turtle.getState().blocks = nil
|
||||
end,
|
||||
}
|
||||
|
@@ -1,8 +1,8 @@
|
||||
local Util = require('util')
|
||||
local class = require('class')
|
||||
local Event = require('event')
|
||||
local Tween = require('tween')
|
||||
local Region = require('region')
|
||||
local Tween = require('ui.tween')
|
||||
local Region = require('ui.region')
|
||||
|
||||
local mapColorToGray = {
|
||||
[ colors.white ] = colors.white,
|
||||
|
@@ -426,6 +426,10 @@ function Util.matches(str, pattern)
|
||||
return t
|
||||
end
|
||||
|
||||
function Util.startsWidth(s, match)
|
||||
return string.sub(s, 1, #match) == match
|
||||
end
|
||||
|
||||
function Util.widthify(s, len)
|
||||
s = s or ''
|
||||
local slen = #s
|
||||
|
Reference in New Issue
Block a user