defripper-cd2025/exportall.lua
Aaron Suen f0b79ad9fc Fix custom filter i18n failure
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.
2023-12-29 12:00:26 -05:00

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