-- definitions of BUILD and game types for the Lunatic Interpreter -- INTERNAL! local ffi = require("ffi") ---- sector, wall, sprite, ... ---- ffi.cdef[[ #pragma pack(push,1) typedef struct { const 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; const 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; const int16_t sectnum, statnum; int16_t ang, owner, xvel, yvel, zvel; int16_t lotag, hitag, extra; } spritetype; typedef struct { const uint32_t mdanimtims; const int16_t mdanimcur; int16_t angoff, pitch, roll; int32_t xoff, yoff, zoff; uint8_t flags; uint8_t xpanning, ypanning; const uint8_t filler; float alpha; const int32_t _do_not_use1; const int32_t _do_not_use2; } spriteext_t; #pragma pack(pop) ]] -- game structs ffi.cdef[[ #pragma pack(push,1) // ACTOR_T, might still need to make some fields read-only typedef struct { const int32_t t_data[10]; // 40b sometimes used to hold offsets to con code int16_t picnum,ang,extra,owner; //8b int16_t movflag,tempang,timetosleep; //6b int32_t flags, bposx,bposy,bposz; //16b int32_t floorz,ceilingz,lastvx,lastvy; //16b int32_t lasttransport; //4b const int16_t lightId, lightcount, lightmaxrange, cgg; //8b int16_t actorstayput, dispicnum, shootzvel; // 6b const int8_t _do_not_use[24]; } actor_t; #pragma pack(pop) int32_t engine_main_arrays_are_static; ]] if (ffi.C.engine_main_arrays_are_static) then ffi.cdef[[ sectortype sector[]; walltype wall[]; spritetype sprite[]; spriteext_t spriteext[]; ]] else ffi.cdef[[ sectortype *sector; walltype *wall; spritetype *sprite; spriteext_t *spriteext; ]] end ffi.cdef[[ const int16_t numsectors, numwalls; const int16_t headspritesect[16384+1], headspritestat[1024+1]; const int16_t prevspritesect[16384], prevspritestat[16384]; const int16_t nextspritesect[16384], nextspritestat[16384]; ]] ffi.cdef[[ actor_t actor[16384]; ]] ---- _G tweaks -- pull in only 'safe' stuff ---- local G_ = {} -- our soon-to-be global environment local oG = _G G_.coroutine = coroutine G_.assert = assert G_.tostring = tostring G_.tonumber = tonumber --rawget G_.xpcall = xpcall G_.ipairs = ipairs G_.print = print G_.pcall = pcall --gcinfo --DEPRECATED --module --setfenv --require --rawset --jit G_.bit = bit --package G_.error = error --debug --loadfile --rawequal --load G_.unpack = unpack G_.pairs = pairs G_.table = table G_._VERSION = _VERSION --newproxy --NOT STD? --collectgarbage --dofile G_.next = next G_.math = math --loadstring --_G G_.select = select G_.string = string G_.type = type --getmetatable --getfenv --setmetatable G_._G = G_ -- REMOVE this for release DBG_ = {} DBG_.loadstring = oG.loadstring ---- Set up restricted access to ffi.C from lunatic. ---- local ffiC = ffi.C -- TODO: error(...) --> error(..., 2) to blame the caller? local det = {} -- dummy empty table local tmpmt = { __index = function() error('dummy variable: read access forbidden') end, __newindex = function() error('dummy variable: write access forbidden') end, __metatable = true -- forbid setting the metatable } oG.setmetatable(det, tmpmt) -- GLOBAL gv: provides access to C global *scalars* gv = { -- all non-scalars need to be explicitly listed -- and access to them is redirected to the dummy -- empty table... this is somewhat ugly sector = det, wall = det, sprite = det, spriteext = det, headspritesect = det, headspritestat = det, prevspritesect = det, prevspritestat = det, nextspritesect = det, nextspritestat = det, actor = det, } local tmpmt = { __index = ffiC, __newindex = function() error("cannot create new or write into existing fields of 'gv'") end, __metatable = true, } oG.setmetatable(gv, tmpmt) ---- indirect C array access ---- sector = {} local tmpmt = { __index = function(tab, key) if (key >= 0 and key < ffiC.numsectors) then return ffiC.sector[key] end error('out-of-bounds sector[] read access') end, __newindex = function(tab, key, val) error('cannot write directly to sector[] struct') end, __metatable = true, } oG.setmetatable(sector, tmpmt) wall = {} local tmpmt = { __index = function(tab, key) if (key >= 0 and key < ffiC.numwalls) then return ffiC.wall[key] end error('out-of-bounds wall[] read access') end, __newindex = function(tab, key, val) error('cannot write directly to wall[] struct') end, __metatable = true, } oG.setmetatable(wall, tmpmt) -- create a safe indirection for a ffi.C array local function creategtab(ctab, maxidx, name) local tab = {} local tmpmt = { __index = function(tab, key) if (key>=0 and key < maxidx) then return ctab[key] end error('out-of-bounds '..name..' read access') end, __newindex = function(tab, key, val) error('cannot write directly to '..name) end, __metatable = true, } oG.setmetatable(tab, tmpmt) return tab end sprite = creategtab(ffiC.sprite, 16384, 'sprite[] struct') spriteext = creategtab(ffiC.spriteext, 16384, 'spriteext[] struct') headspritesect = creategtab(ffiC.headspritesect, 16384, 'headspritesect[]') headspritestat = creategtab(ffiC.headspritestat, 1024, 'headspritestat[]') nextspritesect = creategtab(ffiC.nextspritesect, 16384, 'nextspritesect[]') nextspritestat = creategtab(ffiC.nextspritestat, 16384, 'nextspritestat[]') prevspritesect = creategtab(ffiC.prevspritesect, 16384, 'prevspritesect[]') prevspritestat = creategtab(ffiC.prevspritestat, 16384, 'prevspritestat[]') actor = creategtab(ffiC.actor, 16384, 'actor[]') ---- per-sector/per-statnum sprite iterators ---- local function iter_spritesofsect(sect, i) if (i < 0) then i = ffiC.headspritesect[sect] else i = ffiC.nextspritesect[i] end if (i >= 0) then return i, i end end function spritesofsect(sect) assert(sect >= 0 and sect < ffiC.numsectors, "passed invalid sectnum to spritesofsect iterator") return iter_spritesofsect, sect, -1 end local function iter_spritesofstat(stat, i) if (i < 0) then i = ffiC.headspritestat[stat] else i = ffiC.nextspritestat[i] end if (i >= 0) then return i, i end end function spritesofstat(stat) assert(stat >= 0 and stat < 1024, "passed invalid statnum to spritesofstat iterator") return iter_spritesofstat , stat, -1 end -- add new variables/functions living in the global environment G_.DBG_ = DBG_ -- REMOVE this for release G_.gv = gv G_.sector = sector G_.wall = wall G_.sprite = sprite G_.spriteext = spriteext G_.headspritesect = headspritesect G_.headspritestat = headspritestat G_.nextspritesect = nextspritesect G_.nextspritestat = nextspritestat G_.prevspritesect = prevspritesect G_.prevspritestat = prevspritestat G_.actor = actor G_.spritesofsect = spritesofsect G_.spritesofstat = spritesofstat -- 'simple' code for prohibiting initial assignments to create new variables, -- from 14.2 of PiL function gamevar(name, initval) -- aka 'declare' rawset(G_, name, initval or false) end setmetatable( G_, { __newindex = function (_, n) error("attempt to write to undeclared variable "..n, 2) end, __index = function (_, n) error("attempt to read undeclared variable "..n, 2) end, }) -- change the environment of the running Lua thread to the table G_ setfenv(0, G_)