Add more lua scripts.

develop
Alexander Gavrilov 2012-05-08 12:55:06 +04:00
parent fca618ff1b
commit 191071beb6
5 changed files with 623 additions and 5 deletions

@ -0,0 +1,234 @@
--[[ DataDumper.lua
Copyright (c) 2007 Olivetti-Engineering SA
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.
]]
local _ENV = mkmodule('dumper')
local dumplua_closure = [[
local closures = {}
local function closure(t)
closures[#closures+1] = t
t[1] = assert(loadstring(t[1]))
return t[1]
end
for _,t in pairs(closures) do
for i = 2,#t do
debug.setupvalue(t[1], i-1, t[i])
end
end
]]
local lua_reserved_keywords = {
'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for',
'function', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat',
'return', 'then', 'true', 'until', 'while' }
local function keys(t)
local res = {}
local oktypes = { stringstring = true, numbernumber = true }
local function cmpfct(a,b)
if oktypes[type(a)..type(b)] then
return a < b
else
return type(a) < type(b)
end
end
for k in pairs(t) do
res[#res+1] = k
end
table.sort(res, cmpfct)
return res
end
local c_functions = {}
for _,lib in pairs{'_G', 'string', 'table', 'math',
'io', 'os', 'coroutine', 'package', 'debug'} do
local t = _G[lib] or {}
lib = lib .. "."
if lib == "_G." then lib = "" end
for k,v in pairs(t) do
if type(v) == 'function' and not pcall(string.dump, v) then
c_functions[v] = lib..k
end
end
end
function DataDumper(value, varname, fastmode, ident, indent_step)
indent_step = indent_step or 2
local defined, dumplua = {}
-- Local variables for speed optimization
local string_format, type, string_dump, string_rep =
string.format, type, string.dump, string.rep
local tostring, pairs, table_concat =
tostring, pairs, table.concat
local keycache, strvalcache, out, closure_cnt = {}, {}, {}, 0
setmetatable(strvalcache, {__index = function(t,value)
local res = string_format('%q', value)
t[value] = res
return res
end})
local fcts = {
string = function(value) return strvalcache[value] end,
number = function(value) return value end,
boolean = function(value) return tostring(value) end,
['nil'] = function(value) return 'nil' end,
['function'] = function(value)
return string_format("loadstring(%q)", string_dump(value))
end,
userdata = function() error("Cannot dump userdata") end,
thread = function() error("Cannot dump threads") end,
}
local function test_defined(value, path)
if defined[value] then
if path:match("^getmetatable.*%)$") then
out[#out+1] = string_format("s%s, %s)\n", path:sub(2,-2), defined[value])
else
out[#out+1] = path .. " = " .. defined[value] .. "\n"
end
return true
end
defined[value] = path
end
local function make_key(t, key)
local s
if type(key) == 'string' and key:match('^[_%a][_%w]*$') then
s = key .. "="
else
s = "[" .. dumplua(key, 0) .. "]="
end
t[key] = s
return s
end
for _,k in ipairs(lua_reserved_keywords) do
keycache[k] = '["'..k..'"] = '
end
if fastmode then
fcts.table = function (value)
-- Table value
local numidx = 1
out[#out+1] = "{"
for key,val in pairs(value) do
if key == numidx then
numidx = numidx + 1
else
out[#out+1] = keycache[key]
end
local str = dumplua(val)
out[#out+1] = str..","
end
if string.sub(out[#out], -1) == "," then
out[#out] = string.sub(out[#out], 1, -2);
end
out[#out+1] = "}"
return ""
end
else
fcts.table = function (value, ident, path)
if test_defined(value, path) then return "nil" end
-- Table value
local sep, str, numidx, totallen = " ", {}, 1, 0
local meta, metastr = (debug or getfenv()).getmetatable(value)
if meta then
ident = ident + 1
metastr = dumplua(meta, ident, "getmetatable("..path..")")
totallen = totallen + #metastr + 16
end
for _,key in pairs(keys(value)) do
local val = value[key]
local s = ""
local subpath = path
if key == numidx then
subpath = subpath .. "[" .. numidx .. "]"
numidx = numidx + 1
else
s = keycache[key]
if not s:match "^%[" then subpath = subpath .. "." end
subpath = subpath .. s:gsub("%s*=%s*$","")
end
s = s .. dumplua(val, ident+1, subpath)
str[#str+1] = s
totallen = totallen + #s + 2
end
if totallen > 80 then
sep = "\n" .. string_rep(' ', indent_step*(ident+1))
end
str = "{"..sep..table_concat(str, ","..sep).." "..sep:sub(1,-1-indent_step).."}"
if meta then
sep = sep:sub(1,-3)
return "setmetatable("..sep..str..","..sep..metastr..sep:sub(1,-3)..")"
end
return str
end
fcts['function'] = function (value, ident, path)
if test_defined(value, path) then return "nil" end
if c_functions[value] then
return c_functions[value]
elseif debug == nil or debug.getupvalue(value, 1) == nil then
return string_format("loadstring(%q)", string_dump(value))
end
closure_cnt = closure_cnt + 1
local res = {string.dump(value)}
for i = 1,math.huge do
local name, v = debug.getupvalue(value,i)
if name == nil then break end
res[i+1] = v
end
return "closure " .. dumplua(res, ident, "closures["..closure_cnt.."]")
end
end
function dumplua(value, ident, path)
return fcts[type(value)](value, ident, path)
end
if varname == nil then
varname = "return "
elseif varname:match("^[%a_][%w_]*$") then
varname = varname .. " = "
end
if fastmode then
setmetatable(keycache, {__index = make_key })
out[1] = varname
table.insert(out,dumplua(value, 0))
return table.concat(out)
else
setmetatable(keycache, {__index = make_key })
local items = {}
for i=1,10 do items[i] = '' end
items[3] = dumplua(value, ident or 0, "t")
if closure_cnt > 0 then
items[1], items[6] = dumplua_closure:match("(.*\n)\n(.*)")
out[#out+1] = ""
end
if #out > 0 then
items[2], items[4] = "local t = ", "\n"
items[5] = table.concat(out)
items[7] = varname .. "t"
else
items[2] = varname
end
return table.concat(items)
end
end
return _ENV

@ -1,5 +1,7 @@
local _ENV = mkmodule('utils') local _ENV = mkmodule('utils')
local df = df
-- Comparator function -- Comparator function
function compare(a,b) function compare(a,b)
if a < b then if a < b then
@ -26,6 +28,43 @@ function compare_name(a,b)
end end
end end
-- Make a field comparator
function compare_field(field,cmp)
cmp = cmp or compare
if field then
return function (a,b)
return cmp(a[field],b[field])
end
else
return cmp
end
end
-- Make a comparator of field vs key
function compare_field_key(field,cmp)
cmp = cmp or compare
if field then
return function (a,b)
return cmp(a[field],b)
end
else
return cmp
end
end
function is_container(obj)
return df.isvalid(obj) == 'ref' and obj._kind == 'container'
end
-- Make a sequence of numbers in 1..size
function make_index_sequence(size)
local index = {}
for i=1,size do
index[i] = i
end
return index
end
--[[ --[[
Sort items in data according to ordering. Sort items in data according to ordering.
@ -75,10 +114,7 @@ function make_sort_order(data,ordering)
end end
-- Make an order table -- Make an order table
local index = {} local index = make_index_sequence(size)
for i=1,size do
index[i] = i
end
-- Sort the ordering table -- Sort the ordering table
table.sort(index, function(ia,ib) table.sort(index, function(ia,ib)
@ -119,7 +155,7 @@ function assign(tgt,src)
df.assign(tgt, src) df.assign(tgt, src)
elseif type(tgt) == 'table' then elseif type(tgt) == 'table' then
for k,v in pairs(src) do for k,v in pairs(src) do
if type(v) == 'table' or df.isvalid(v) == 'ref' then if type(v) == 'table' then
local cv = tgt[k] local cv = tgt[k]
if cv == nil then if cv == nil then
cv = {} cv = {}
@ -136,4 +172,131 @@ function assign(tgt,src)
return tgt return tgt
end end
local function copy_field(obj,k,v,deep)
if v == nil then
return NULL
end
if deep then
local field = obj:_field(k)
if field == v then
return clone(v,deep)
end
end
return v
end
-- Copy the object as lua data structures.
function clone(obj,deep)
if type(obj) == 'table' then
if deep then
return assign({},obj)
else
return copyall(obj)
end
elseif df.isvalid(obj) == 'ref' then
local kind = obj._kind
if kind == 'primitive' then
return obj.value
elseif kind == 'bitfield' then
local rv = {}
for k,v in pairs(obj) do
rv[k] = v
end
return rv
elseif kind == 'container' then
local rv = {}
for k,v in ipairs(obj) do
rv[k+1] = copy_field(obj,k,v,deep)
end
return rv
else -- struct
local rv = {}
for k,v in pairs(obj) do
rv[k] = copy_field(obj,k,v,deep)
end
return rv
end
else
return obj
end
end
-- Sort a vector or lua table
function sort_vector(vector,field,cmp)
local fcmp = compare_field(field,cmp)
local scmp = function(a,b)
return fcmp(a,b) < 0
end
if df.isvalid(vector) then
if vector._kind ~= 'container' then
error('Container expected: '..tostring(vector))
end
local items = clone(vector, true)
table.sort(items, scmp)
vector:assign(items)
else
table.sort(vector, scmp)
end
return vector
end
-- Binary search in a vector or lua table
function binsearch(vector,key,field,cmp,min,max)
if not(min and max) then
if df.isvalid(vector) then
min = -1
max = #vector
else
min = 0
max = #vector+1
end
end
local mf = math.floor
local fcmp = compare_field_key(field,cmp)
while true do
local mid = mf((min+max)/2)
if mid <= min then
return nil, false, max
end
local item = vector[mid]
local cv = fcmp(item, key)
if cv == 0 then
return item, true, mid
elseif cv < 0 then
min = mid
else
max = mid
end
end
end
-- Binary search and insert
function insert_sorted(vector,item,field,cmp)
local key = item
if field and item then
key = item[field]
end
local cur,found,pos = binsearch(vector,key,field,cmp)
if found then
return false,cur,pos
else
if df.isvalid(vector) then
vector:insert(pos, item)
else
table.insert(vector, pos, item)
end
return true,vector[pos],pos
end
end
-- Binary search, then insert or overwrite
function insert_or_replace(vector,item,field,cmp)
local added,cur,pos = insert_sorted(vector,item,field,cmp)
if not added then
vector[pos] = item
cur = vector[pos]
end
return added,cur,pos
end
return _ENV return _ENV

@ -0,0 +1,96 @@
-- List input items for the building currently being built.
local dumper = require 'dumper'
local function copy_flags(tgt,src,name)
local val = src[name]
if val.whole == 0 then
return
end
local out = {}
tgt[name] = out
for k,v in pairs(val) do
if v then
out[k] = v
end
end
end
local function copy_value(tgt,src,name,defval)
if src[name] ~= defval then
tgt[name] = src[name]
end
end
local function copy_enum(tgt,src,name,defval,ename,enum)
if src[name] ~= defval then
tgt[name] = ename..'.'..enum[src[name]]
end
end
local lookup = {}
for i=df.job_item_vector_id._first_item,df.job_item_vector_id._last_item do
local id = df.job_item_vector_id.attrs[i].other
local ptr
if id == df.items_other_id.ANY then
ptr = df.global.world.items.all
elseif id == df.items_other_id.BAD then
ptr = df.global.world.items.bad
else
ptr = df.global.world.items.other[id]
end
if ptr then
local _,addr = df.sizeof(ptr)
lookup[addr] = 'df.job_item_vector_id.'..df.job_item_vector_id[i]
end
end
local function clone_filter(src,quantity)
local tgt = {}
src.flags2.allow_artifact = false
if quantity ~= 1 then
tgt.quantity = quantity
end
copy_enum(tgt, src, 'item_type', -1, 'df.item_type', df.item_type)
copy_value(tgt, src, 'item_subtype', -1)
copy_value(tgt, src, 'mat_type', -1)
copy_value(tgt, src, 'mat_index', -1)
copy_flags(tgt, src, 'flags1')
copy_flags(tgt, src, 'flags2')
copy_flags(tgt, src, 'flags3')
copy_value(tgt, src, 'reaction_class', '')
copy_value(tgt, src, 'has_material_reaction_product', '')
copy_value(tgt, src, 'metal_ore', -1)
copy_value(tgt, src, 'min_dimension', -1)
copy_enum(tgt, src, 'has_tool_use', -1, 'df.tool_uses', df.tool_uses)
local ptr = src.item_vector
if ptr and ptr ~= df.global.world.items.other[0] then
local _,addr = df.sizeof(ptr)
tgt.vector_id = lookup[addr]
end
return tgt
end
local function dump(name)
local out = {}
for i,v in ipairs(df.global.ui_build_selector.requirements) do
out[#out+1] = clone_filter(v.filter, v.count_required)
end
local fmt = dumper.DataDumper(out,name,false,1,4)
local out = string.gsub(fmt, '"', '')
print(out)
end
local itype = df.global.ui_build_selector.building_type
local stype = df.global.ui_build_selector.building_subtype
if itype == df.building_type.Workshop then
dump(' [df.workshop_type.'..df.workshop_type[stype]..'] = ')
elseif itype == df.building_type.Furnace then
dump(' [df.furnace_type.'..df.furnace_type[stype]..'] = ')
elseif itype == df.building_type.Trap then
dump(' [df.trap_type.'..df.trap_type[stype]..'] = ')
else
dump(' [df.building_type.'..df.building_type[itype]..'] = ')
end

@ -0,0 +1,125 @@
-- Verify item occupancy and block item list integrity.
--
-- Checks:
-- 1) Item has flags.on_ground <=> it is in the correct block item list
-- 2) A tile has items in block item list <=> it has occupancy.item
-- 3) The block item lists are sorted.
local utils = require 'utils'
function check_block_items(fix)
local cnt = 0
local icnt = 0
local found = {}
local found_somewhere = {}
local should_fix = false
local can_fix = true
for _,block in ipairs(df.global.world.map.map_blocks) do
local itable = {}
local bx,by,bz = pos2xyz(block.map_pos)
-- Scan the block item vector
local last_id = nil
local resort = false
for _,id in ipairs(block.items) do
local item = df.item.find(id)
local ix,iy,iz = pos2xyz(item.pos)
local dx,dy,dz = ix-bx,iy-by,iz-bz
-- Check sorted order
if last_id and last_id >= id then
print(bx,by,bz,last_id,id,'block items not sorted')
resort = true
else
last_id = id
end
-- Check valid coordinates and flags
if not item.flags.on_ground then
print(bx,by,bz,id,dx,dy,'in block & not on ground')
elseif dx < 0 or dx >= 16 or dy < 0 or dy >= 16 or dz ~= 0 then
found_somewhere[id] = true
print(bx,by,bz,id,dx,dy,dz,'invalid pos')
can_fix = false
else
found[id] = true
itable[dx + dy*16] = true;
-- Check missing occupancy
if not block.occupancy[dx][dy].item then
print(bx,by,bz,dx,dy,'item & not occupied')
if fix then
block.occupancy[dx][dy].item = true
else
should_fix = true
end
end
end
end
-- Sort the vector if needed
if resort then
if fix then
utils.sort_vector(block.items)
else
should_fix = true
end
end
icnt = icnt + #block.items
-- Scan occupancy for spurious marks
for x=0,15 do
local ocx = block.occupancy[x]
for y=0,15 do
if ocx[y].item and not itable[x + y*16] then
print(bx,by,bz,x,y,'occupied & no item')
if fix then
ocx[y].item = false
else
should_fix = true
end
end
end
end
cnt = cnt + 256
end
-- Check if any items are missing from blocks
for _,item in ipairs(df.global.world.items.all) do
if item.flags.on_ground and not found[item.id] then
can_fix = false
if not found_somewhere[item.id] then
print(id,item.pos.x,item.pos.y,item.pos.z,'on ground & not in block')
end
end
end
-- Report
print(cnt.." tiles and "..icnt.." items checked.")
if should_fix and can_fix then
print("Use 'fix/item-occupancy --fix' to fix the listed problems.")
elseif should_fix then
print("The problems are too severe to be fixed by this script.")
end
end
local opt = ...
local fix = false
if opt then
if opt == '--fix' then
fix = true
else
dfhack.printerr('Invalid option: '..opt)
return
end
end
print("Checking item occupancy - this will take a few seconds.")
check_block_items(fix)