raze/polymer/eduke32/source/lunatic/build.lua
helixhorned 3697842bc5 Add ART loader for the LuaJIT BUILD struct loader module, 2 more examples.
The map iterator now has init/finish capability, making it possible to
write scripts that aggregate data over multiple map files.  One such example
calculates some statistics, the other loads art metadata and looks for
red walls with non-pow2 ysize tiles.

git-svn-id: https://svn.eduke32.com/eduke32@2814 1a8010ca-5511-0410-912e-c29ae57300e0
2012-07-08 21:47:11 +00:00

296 lines
7.2 KiB
Lua

-- Loaders for various BUILD structures for LuaJIT
local ffi = require "ffi"
local io = require "io"
local error = error
local assert = assert
local print = print
local setmetatable = setmetatable
module(...)
ffi.cdef[[
#pragma pack(push,1)
typedef struct
{
int16_t wallptr, wallnum;
int32_t ceilingz, floorz;
int16_t ceilingstat, floorstat;
int16_t ceilingpicnum, ceilingheinum;
int8_t ceilingshade;
uint8_t ceilingpal, ceilingxpanning, ceilingypanning;
int16_t floorpicnum, floorheinum;
int8_t floorshade;
uint8_t floorpal, floorxpanning, floorypanning;
uint8_t visibility, filler;
int16_t lotag, hitag, extra;
} sectortype;
typedef struct
{
int32_t x, y;
int16_t point2, nextwall, nextsector;
int16_t cstat;
int16_t picnum, overpicnum;
int8_t shade;
uint8_t pal, xrepeat, yrepeat, xpanning, ypanning;
int16_t lotag, hitag, extra;
} walltype;
typedef struct
{
int32_t x, y, z;
int16_t cstat, picnum;
int8_t shade;
uint8_t pal, clipdist, filler;
uint8_t xrepeat, yrepeat;
int8_t xoffset, yoffset;
int16_t sectnum, statnum;
int16_t ang, owner, xvel, yvel, zvel;
int16_t lotag, hitag, extra;
} spritetype;
]]
local C = ffi.C
local MAX =
{
SECTORS = { [7]=1024, [8]=4096, [9]=4096 },
WALLS = { [7]=8192, [8]=16384, [9]=16384 },
SPRITES = { [7]=4096, [8]=16384, [9]=16384 },
TILES = 30720,
}
local function doread(fh, basectype, numelts)
local cd = ffi.new(basectype.."[?]", numelts)
local size = ffi.sizeof(cd)
if (numelts==0) then
return nil
end
assert(size % numelts == 0)
local datstr = fh:read(size)
if (datstr == nil or #datstr < size) then
fh:close()
return nil
end
ffi.copy(cd, datstr, size)
return cd
end
local function set_secwalspr_mt(structar, maxidx)
local mt = {
__index = function(tab, idx)
if (idx < 0 or idx >= maxidx) then
error("Invalid structure array read access", 2)
end
return structar[idx]
end,
__newindex = function(tab, idx, newval)
error('cannot write directly to structure array', 2)
end,
}
return setmetatable({}, mt)
end
--== LOADBOARD ==--
-- returns:
-- on failure, nil, errmsg
-- on success, a table
-- {
-- version = <num>,
-- numsectors=<num>, numwalls=<num>, numsprites=<num>,
-- sector=<cdata (array of sectortype)>,
-- wall=<cdata (array of walltype)>,
-- sprite=nil or <cdata> (array of spritetype),
-- start =
-- { x=<num>, y=<num>, z=<num>, ang=<num>, sectnum=<num> }
-- }
function loadboard(filename)
local fh, errmsg = io.open(filename)
if (fh==nil) then
return nil, errmsg
end
-- The table we'll return on success
local map = { start={} }
local cd = doread(fh, "int32_t", 4)
if (cd==nil) then
return nil, "Couldn't read header"
end
map.version = cd[0]
map.start.x = cd[1]
map.start.y = cd[2]
map.start.z = cd[3]
if (map.version < 7 or map.version > 9) then
fh:close()
return nil, "Invalid map version"
end
cd = doread(fh, "int16_t", 3)
if (cd==nil) then
return nil, "Couldn't read header (2)"
end
map.start.ang = cd[0]
map.start.sectnum = cd[1]
-- sectors
map.numsectors = cd[2]
if (map.numsectors == nil) then
return nil, "Couldn't read number of sectors"
end
if (map.numsectors <= 0 or map.numsectors > MAX.SECTORS[map.version]) then
fh:close()
return nil, "Invalid number of sectors"
end
map.sector = doread(fh, "sectortype", map.numsectors)
if (map.sector == nil) then
return nil, "Couldn't read sectors"
end
-- walls
cd = doread(fh, "int16_t", 1)
if (cd == nil) then
return nil, "Couldn't read number of walls"
end
map.numwalls = cd[0]
if (map.numwalls <= 0 or map.numwalls > MAX.WALLS[map.version]) then
fh:close()
return nil, "Invalid number of walls"
end
map.wall = doread(fh, "walltype", map.numwalls)
if (map.wall == nil) then
return nil, "Couldn't read walls"
end
-- sprites
cd = doread(fh, "int16_t", 1)
if (cd == nil) then
return nil, "Couldn't read number of sprites"
end
map.numsprites = cd[0]
if (map.numsprites < 0 or map.numsprites > MAX.SPRITES[map.version]) then
fh:close()
return nil, "Invalid number of sprites"
end
map.sprite = doread(fh, "spritetype", map.numsprites)
if (map.numsprites~=0 and map.sprite == nil) then
return nil, "Couldn't read sprites"
end
map.sector = set_secwalspr_mt(map.sector, map.numsectors)
map.wall = set_secwalspr_mt(map.wall, map.numwalls)
map.sprite = set_secwalspr_mt(map.sprite, map.numsprites)
-- done
fh:close()
return map
end
local function set_sizarray_mt(sizar)
local mt = {
__index = function(tab, idx)
if (idx < 0 or idx >= MAX.TILES) then
error("Invalid tile size array read access", 2)
end
return sizar[idx]
end,
__newindex = function(tab, idx, newval)
if (idx < 0 or idx >= MAX.TILES) then
error("Invalid tile size array write access", 2)
end
sizar[idx] = newval
end,
}
return setmetatable({}, mt)
end
--== LOADARTS (currently tilesizx and tilesizy only) ==--
-- filenames: a table with ART file names
-- returns:
-- on failure, nil, errmsg
-- on success, a table
-- {
-- sizx = <cdata (array of length MAXTILES)>
-- sizy = <cdata (array of length MAXTILES)>
-- }
function loadarts(filenames)
local tile = {
sizx = ffi.new("int16_t [?]", MAX.TILES),
sizy = ffi.new("int16_t [?]", MAX.TILES),
}
for fni=1,#filenames do
local fn = filenames[fni]
local fh, errmsg = io.open(fn)
if (fh==nil) then
return nil, errmsg
end
local cd = doread(fh, "int32_t", 4)
-- artversion, numtiles, localtilestart, localtileend
if (cd==nil) then
fh:close()
return nil, fn..": Couldn't read header"
end
local localtilestart = cd[2]
local numtileshere = cd[3]-localtilestart
if (numtileshere < 0 or localtilestart+numtileshere >= MAX.TILES) then
fh:close()
return nil, fn..": Invalid tile start/end"
end
if (numtileshere==0) then
fh:close()
else
-- X sizes
cd = doread(fh, "int16_t", numtileshere)
if (cd == nil) then
fh:close()
return nil, fn..": Couldn't read tilesizx array"
end
ffi.copy(tile.sizx+localtilestart, cd, numtileshere*2)
-- Y sizes
cd = doread(fh, "int16_t", numtileshere)
if (cd == nil) then
fh:close()
return nil, fn..": Couldn't read tilesizy array"
end
ffi.copy(tile.sizy+localtilestart, cd, numtileshere*2)
end
end
tile.sizx = set_sizarray_mt(tile.sizx)
tile.sizy = set_sizarray_mt(tile.sizy)
return tile
end