f0b79ad9fc
When a language establishes a convention of using plain strings as exception types instead of something custom that can hold e.g. a "code" property, it effectively waives its right to i18n its error messages, or else programs cannot differentiate between types of errors and catch/pcall constructs are of very limited use. Unfortunately it seems that Lua did not realize this, and loadfile returns a plain string that is not usable due to i18n. Rather than trying to catch the "file not found" error, do a scan to look for the file first, and if it's found in the directory listing, assume any errors that happen on loading it are fatal.
262 lines
6.4 KiB
Lua
262 lines
6.4 KiB
Lua
-- LUALOCALS < ---------------------------------------------------------
|
|
local error, io, ipairs, loadfile, minetest, next, os, pairs, rawget,
|
|
string, type
|
|
= error, io, ipairs, loadfile, minetest, next, os, pairs, rawget,
|
|
string, type
|
|
local io_open, os_remove, string_gsub, string_sub
|
|
= io.open, os.remove, string.gsub, string.sub
|
|
-- LUALOCALS > ---------------------------------------------------------
|
|
|
|
local include = ...
|
|
local dumptable, sortedpairs = include("dumptable")
|
|
local getdir, ripmedia = include("fileops")
|
|
local configdb = include("configdb")
|
|
local _, mediadirs = include("mediacache")
|
|
|
|
local modname = minetest.get_current_modname()
|
|
|
|
local nodekeys = {
|
|
alpha = true,
|
|
climbable = true,
|
|
color = true,
|
|
collision_box = true,
|
|
description = true,
|
|
drawtype = true,
|
|
inventory_image = true,
|
|
legacy_facedir_simple = true,
|
|
light_source = true,
|
|
liquid_move_physics = function(def)
|
|
return def.liquid_move_physics
|
|
or def.liquidtype ~= nil and def.liquidtype ~= "none"
|
|
or nil
|
|
end,
|
|
mesh = true,
|
|
node_box = true,
|
|
palette = true,
|
|
paramtype = true,
|
|
paramtype2 = true,
|
|
place_param2 = true,
|
|
post_effect_color = true,
|
|
selection_box = true,
|
|
sounds = true,
|
|
special_tiles = true,
|
|
sunlight_propagates = true,
|
|
tiles = true,
|
|
type = true,
|
|
use_texture_alpha = true,
|
|
visual_scale = true,
|
|
walkable = true,
|
|
wield_image = true,
|
|
}
|
|
for k, v in pairs(nodekeys) do
|
|
if v == true then
|
|
nodekeys[k] = function(def) return rawget(def, k) end
|
|
end
|
|
end
|
|
|
|
local blocked = {
|
|
air = true,
|
|
ignore = true
|
|
}
|
|
|
|
local function deepcopy(obj)
|
|
if type(obj) == "table" then
|
|
local t = {}
|
|
for k, v in pairs(obj) do t[k] = deepcopy(v) end
|
|
return t
|
|
end
|
|
return obj
|
|
end
|
|
|
|
local function exportall()
|
|
local count = 0
|
|
|
|
local outdir = getdir(minetest.get_worldpath() .. "/" .. modname)
|
|
|
|
local outf = io_open(outdir .. "/exported.lua", "w")
|
|
outf:write("-- AUTOMATICALLY GENERATED by <https://gitlab.com/szest/defripper>"
|
|
.. "\n\nlocal reg = ...\n\n")
|
|
|
|
local hascustomlua
|
|
for _, fn in pairs(minetest.get_dir_list(outdir, false)) do
|
|
if fn == "customfilter.lua" then
|
|
hascustomlua = true
|
|
break
|
|
end
|
|
end
|
|
local custom
|
|
if hascustomlua then
|
|
local loaded, err = loadfile(outdir .. "/customfilter.lua")
|
|
if not loaded then return error(err) end
|
|
custom = loaded()
|
|
if type(custom) ~= "function" then
|
|
return error("customfilter.lua must return filter function")
|
|
end
|
|
end
|
|
|
|
local filtered = {}
|
|
local aliases = {}
|
|
local skipped = {}
|
|
do
|
|
local props = configdb.props or nodekeys
|
|
local function process(into, k, v)
|
|
if not blocked[k] and configdb.items[k] then
|
|
local t = {}
|
|
for k2, v2 in pairs(nodekeys) do
|
|
if props[k2] then
|
|
t[k2] = deepcopy(v2(v))
|
|
end
|
|
end
|
|
t._raw_name = k
|
|
t._alias_to = v._alias_to
|
|
if custom then
|
|
local r = custom(t, k, v)
|
|
if r == false then
|
|
t = nil
|
|
skipped[k] = true
|
|
elseif r ~= nil then
|
|
if type(r) ~= "table" then
|
|
return error("customfilter must"
|
|
.. " return table, nil, or false")
|
|
end
|
|
t = r
|
|
end
|
|
end
|
|
into[k] = t
|
|
end
|
|
end
|
|
for k, v in pairs(minetest.registered_items) do
|
|
process(filtered, k, v)
|
|
end
|
|
for k, v in pairs(minetest.registered_aliases) do
|
|
local def = minetest.registered_items[v]
|
|
if def then
|
|
process(aliases, k, {_alias_to = v})
|
|
if not filtered[v] then
|
|
process(filtered, v, def)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local missing_defs = {}
|
|
for k in pairs(configdb.items) do
|
|
if not (blocked[k] or skipped[k] or filtered[k] or aliases[k]) then
|
|
local mod = string_gsub(k, ":.*", "")
|
|
missing_defs[mod] = (missing_defs[mod] or 0) + 1
|
|
end
|
|
end
|
|
|
|
local localized = {}
|
|
do
|
|
local candidates = {}
|
|
for k, v in pairs(filtered) do
|
|
dumptable(v, {
|
|
path = string_gsub(k, "%W", "_"),
|
|
locals = function(item, ctx, obj)
|
|
local cand = candidates[item]
|
|
if not cand then
|
|
cand = {
|
|
qty = 0,
|
|
name = "L." .. ctx.path,
|
|
obj = obj
|
|
}
|
|
candidates[item] = cand
|
|
end
|
|
cand.qty = cand.qty + 1
|
|
if #ctx.path < #cand.name
|
|
or (#ctx.path == #cand.name
|
|
and ctx.path < cand.name) then
|
|
cand.name = "L." .. ctx.path
|
|
end
|
|
end
|
|
})
|
|
end
|
|
for k, v in pairs(candidates) do
|
|
dumptable(v.obj, {
|
|
path = v.name,
|
|
locals = function(item)
|
|
if item ~= k then
|
|
local cand = candidates[item]
|
|
cand.rawqty = cand.rawqty or cand.qty
|
|
cand.useby = cand.useby or {}
|
|
cand.useby[v.name] = true
|
|
cand.qty = cand.qty - v.qty + 1
|
|
end
|
|
end
|
|
})
|
|
end
|
|
local anylocals = nil
|
|
for k, v in sortedpairs(candidates) do
|
|
if #k + #v.name * (v.qty + 1) < #k * v.qty then
|
|
if not anylocals then
|
|
outf:write("local L = {}\n\n")
|
|
anylocals = true
|
|
end
|
|
outf:write(v.name .. " = " .. k .. "\n\n")
|
|
localized[k] = v.name
|
|
end
|
|
end
|
|
end
|
|
|
|
local mymedia = {}
|
|
for _, t in ipairs({filtered, aliases}) do
|
|
for _, v in sortedpairs(t) do
|
|
ripmedia(mymedia, v.sounds, outdir, "sounds", function(spec, fn)
|
|
return string_sub(fn, 1, #spec + 1) == spec .. "."
|
|
and string_sub(fn, -4) == ".ogg"
|
|
end)
|
|
ripmedia(mymedia, v.palette, outdir, "textures")
|
|
ripmedia(mymedia, v.tiles, outdir, "textures")
|
|
ripmedia(mymedia, v.special_tiles, outdir, "textures")
|
|
ripmedia(mymedia, v.inventory_image, outdir, "textures")
|
|
ripmedia(mymedia, v.wield_image, outdir, "textures")
|
|
ripmedia(mymedia, v.mesh, outdir, "models")
|
|
outf:write("reg(" .. dumptable(v, {
|
|
locals = function(item)
|
|
return localized[item]
|
|
end
|
|
}) .. ")\n\n")
|
|
count = count + 1
|
|
end
|
|
end
|
|
outf:close()
|
|
|
|
local missing_media = {}
|
|
for k, v in pairs(configdb.media) do
|
|
if not ripmedia(mymedia, k, outdir, v) then
|
|
missing_media[v] = (missing_media[v] or 0) + 1
|
|
end
|
|
end
|
|
|
|
for mdir in pairs(mediadirs) do
|
|
for _, fn in pairs(minetest.get_dir_list(outdir .. "/" .. mdir, false) or {}) do
|
|
local fulldest = outdir .. "/" .. mdir .. "/" .. fn
|
|
if not mymedia[fulldest] then
|
|
os_remove(fulldest)
|
|
end
|
|
end
|
|
end
|
|
|
|
local mediaqty = 0
|
|
if next(mymedia) then
|
|
local attrdata = {}
|
|
for k, v in sortedpairs(mymedia) do
|
|
mediaqty = mediaqty + 1
|
|
local fn = string_sub(k, #outdir + 1)
|
|
attrdata[fn] = v.modname
|
|
end
|
|
local attrf = io_open(outdir .. "/mediasource.json", "w")
|
|
attrf:write(minetest.write_json(attrdata, true))
|
|
attrf:close()
|
|
end
|
|
|
|
return {
|
|
exported_defs = count,
|
|
exported_media = mediaqty,
|
|
missing_defs = missing_defs,
|
|
missing_media = missing_media
|
|
}
|
|
end
|
|
|
|
return exportall
|