AutoMap/src/init.lua

161 lines
5.1 KiB
Lua

-- AutoMap: Copyright 2021 Vincent Robinson under the MIT license. See `license.txt` for more info.
print("---> AutoMap Version 1.0.0 (c) 2021 Vincent Robinson\n")
-- The global namespace for all AutoMap functions.
auto = {}
dofile("src/helpers.lua")
dofile("src/wad.lua")
-- No need to include any other files for this control script.
-- Get the directory delimiter for the system. This is a standard way to do it, not a hack.
local DIR_DELIM = package.config:sub(1, 1)
-- Place a string in double quotes
local function quote(str)
return "\"" .. str .. "\""
end
-- Replace an identifier in a string without formatting. The identifier must be present.
local function replace_ident(s, ident, repl)
local first, last = s:find(ident, 1, true)
return s:sub(1, first - 1) .. repl .. s:sub(last + 1)
end
--[[ Parse the input WAD path and return other necessary paths based on the configuration.
Specifically, it returns a table with the following:
* input_path: The path that was provided to this function
* input_name: The name of the input file
* output_path: The path that the output file should be at
* output_name: The name that the output file should have
* dir: The directory containing both the input and output files
]]
local function parse_input_path(config, input_path)
local paths = {}
paths.input_path = input_path
-- Get the directory of the WAD file by finding the last slash (of either type since
-- Windows can use `/` as well)
paths.dir = ""
paths.input_name = input_path
for i = #input_path, 1, -1 do
local c = input_path:sub(i, i)
if c == "/" or c == "\\" then
paths.dir = input_path:sub(1, i)
paths.input_name = input_path:sub(i + 1)
break
end
end
-- Find the first and last character of the shared part of the input and output names
local input_format = config.input_format
local first, format_last = input_format:find("%name%", 1, true)
local actual_last = format_last - #input_format + #paths.input_name
-- Ensure that the input WAD follows the naming conventions, or else there may be problems
assert(paths.input_name:sub(1, first - 1) == input_format:sub(1, first - 1) and
paths.input_name:sub(actual_last + 1) == input_format:sub(format_last + 1),
"Input WAD does not follow naming conventions")
paths.output_name = replace_ident(config.output_format, "%name%",
paths.input_name:sub(first, actual_last))
paths.output_path = paths.dir .. paths.output_name
return paths
end
-- Run the nodebuilder on the output file
local function run_nodebuilder(config, to_nodebuild, output_path)
if to_nodebuild == "no_nodebuild" then
return
end
local params
if to_nodebuild == "normal" then
params = config.nodebuilder_params_normal
else
params = config.nodebuilder_params_fast
end
local quoted_output = quote(output_path)
params = replace_ident(params, "%input%", quoted_output)
params = replace_ident(params, "%output%", quoted_output)
local command = quote(config.nodebuilder_path) .. " " .. params
if DIR_DELIM == "\\" then
-- This is to circumvent very stupid problems with `cmd /C`, which `os.execute` uses.
-- See <https://stackoverflow.com/questions/9964865/c-system-not-working-when-there-
-- are-spaces-in-two-different-parameters>
command = quote(command)
end
io.write("\n---> ")
local success, status, code = os.execute(command)
assert(success, "Nodebuilding failed: " .. status .. " code " .. code)
end
-- Runs all AutoMap Lua lumps in the specified WAD. A separate instance of the Lua interpreter
-- is opened to more completely sandbox it and prevent vulnerabilities.
local function run_autolua_lumps(paths, wad)
local lumps = wad:get_lumps_with_prefix("AML_")
local args = {paths.input_path, paths.output_path}
local to_remove = {}
for _, lump in ipairs(lumps) do
local name = os.tmpname()
local file = assert(io.open(name, "w"))
file:write(lump.content)
args[#args + 1] = quote(name) .. " " .. quote(lump.name)
to_remove[#to_remove + 1] = name
file:close()
end
-- In this one case of running a command, Windows requires `\` instead of `/`
local command = "lua" .. DIR_DELIM .. "lua54 -W src/run.lua " .. table.concat(args, " ")
if DIR_DELIM == "\\" then
command = quote(command)
end
local success, status, code = os.execute(command)
for _, name in ipairs(to_remove) do
os.remove(name)
end
assert(success, "Running AutoMap Lua lumps failed: " .. status .. " code " .. code)
end
local function read_wad(path)
local file = io.open(path, "rb")
assert(file, "Unable to open " .. path)
local wad = Wad(file:read("a"))
file:close()
return wad
end
-- Parse command line arguments
assert(#arg == 2, "Incorrect number of command line arguments")
-- How the wad file should be built with a nodebuilder.
local to_nodebuild = arg[1]
-- Path to the WAD file being processed.
local wad_path = arg[2]
-- Configuration options from `config.lua`
local config = dofile("config.lua")
local paths = parse_input_path(config, wad_path)
print("Preprocessing: " .. paths.input_name .. "\n")
run_autolua_lumps(paths, read_wad(paths.input_path))
print("\nPreprocessing saved to " .. paths.output_path)
run_nodebuilder(config, to_nodebuild, paths.output_path)