raze-gles/polymer/eduke32/source/lunatic/build.lua

297 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