- removed Lunatic remains.

This looks as dead as it could be, so away it goes.
This commit is contained in:
Christoph Oelckers 2019-09-21 20:27:29 +02:00
parent 639d1fb4d3
commit 7721ed31c0
182 changed files with 0 additions and 54282 deletions

View file

@ -150,26 +150,14 @@ EDUKE32_STATIC_ASSERT(7 <= MAXTILES-MAXUSERTILES);
#include "sounds.h"
#include "soundsdyn.h"
#ifdef LUNATIC
# include "lunatic_game.h"
#endif
static inline int32_t G_HaveActor(int spriteNum)
{
#ifdef LUNATIC
return El_HaveActor(spriteNum);
#else
return g_tile[spriteNum].execPtr!=NULL;
#endif
}
static inline int32_t G_DefaultActorHealth(int spriteNum)
{
#ifdef LUNATIC
return g_elActors[spriteNum].strength;
#else
return G_HaveActor(spriteNum) ? g_tile[spriteNum].execPtr[0] : 0;
#endif
}
#endif

View file

@ -32,10 +32,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "savegame.h"
#include "scriplib.h"
#ifdef LUNATIC
# include "lunatic_game.h"
#endif
#include "vfs.h"
#if KRANDDEBUG

View file

@ -28,10 +28,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "gamedef.h" // vmstate_t
#include "sector.h" // mapstate_t
#ifdef LUNATIC
# include "lunatic_game.h"
#endif
int32_t VM_ExecuteEvent(int const nEventID, int const spriteNum, int const playerNum, int const nDist, int32_t const nReturn);
int32_t VM_ExecuteEvent(int const nEventID, int const spriteNum, int const playerNum, int const nDist);
int32_t VM_ExecuteEvent(int const nEventID, int const spriteNum, int const playerNum);
@ -39,11 +35,7 @@ int32_t VM_ExecuteEventWithValue(int const nEventID, int const spriteNum, int co
static FORCE_INLINE bool VM_HaveEvent(int const nEventID)
{
#ifdef LUNATIC
return L_IsInitialized(&g_ElState) && El_HaveEvent(nEventID);
#else
return !!apScriptEvents[nEventID];
#endif
}
static FORCE_INLINE int32_t VM_OnEvent(int nEventID, int spriteNum, int playerNum, int nDist, int32_t nReturn)

View file

@ -1,55 +0,0 @@
-- INTERNAL
-- definitions of BUILD and game types for the Lunatic Interpreter
local ffi = require("ffi")
local ffiC = ffi.C
ffi.cdef[[
enum {
LUNATIC_CLIENT_MAPSTER32 = 0,
LUNATIC_CLIENT_EDUKE32 = 1,
LUNATIC_CLIENT = LUNATIC_CLIENT_MAPSTER32
}
]]
--== First, load the definitions common to the game's and editor's Lua interface.
decl = ffi.cdef
local defs_c = require("defs_common")
defs_c.finish_spritetype({})
defs_c.create_globals(_G)
-- TODO: provide access to only a subset, restict access to ffiC?
gv = ffiC
--== Mapster32-specific initialization
ffi.cdef "char *listsearchpath(int32_t initp);"
-- Add the search path directories to the Lua load path.
local initp = 1
while (true) do
local sp_c = ffiC.listsearchpath(initp)
if (sp_c == nil) then
break
end
local sp = ffi.string(sp_c)
assert(sp:sub(-1)=='/')
package.path = sp..'?.lua;'..package.path
initp = 0
end
-- Helper functions
local package = package
local require = require
function reload(modname)
package.loaded[modname] = nil
return require(modname)
end
--print('Lua load path: '..package.path)

File diff suppressed because it is too large Load diff

View file

@ -1,124 +0,0 @@
-- Implementation of a bound-checked array type factory for LuaJIT 2.0 or later.
--
-- Usage example:
--
-- > bcarray.new("int8_t", 3, "test", "three_pigs")
-- > a = ffi.new("struct { int32_t a; three_pigs p; int16_t b; }")
-- > =ffi.sizeof(a) --> 12
-- > b = ffi.new("__attribute__((packed)) struct { int32_t a; three_pigs p; int16_t b; }")
-- > =ffi.sizeof(b) --> 9
local ffi = require("ffi")
local string = require("string")
local table = require("table")
local assert = assert
local error = error
local pairs = pairs
local type = type
local bcarray = {}
-- Generate C decl for a sequence of <nelts> const struct members.
-- For example, for 4 elements,
-- "const $ _r1, _f2, _u3, _n4;"
local function flatten_array(nelts, rng)
local strtab = { "$ " }
if (rng and rng.getu32==nil) then
assert(type(rng)=="table")
for i=1,#rng do
strtab[i+1] = rng[i]..((i<#rng) and "," or ";")
end
else
for i=1,nelts do
local ch = 97 + (rng and (rng:getu32() % 25) or 0) -- 'a'..'z'
strtab[i+1] = string.format("_%c%x%s", ch, i, (i<nelts) and "," or ";")
end
end
return table.concat(strtab)
end
-- ctype = bcarray.new(basetype, numelts, showname [, typename] [, rng] [, mtadd])
-- (optional fields may be nil)
--
-- <numelts>: Number of elements in array (small number)
-- <showname>: The name to be shown for the derived type in error messages
-- <typename>: If non-nil, the name under which the derived type is typedef'd
-- <rng>: Random generator state + method :getu32(). If nil, then members are
-- named _a1, _a2, ...
-- It also may be a table containing member names at numeric indices 1..#rng.
-- <mtadd>: A table containing functions __index and/or __newindex. They are
-- called first and the bound-checking ones are tail-called then. If the
-- custom __index one returns something, it is returned by the composite one.
function bcarray.new(basetype, numelts, showname, typename, rng, mtadd)
local eltptr_t = ffi.typeof("$ *", ffi.typeof(basetype))
local mt = {
__index = function(ar, idx)
if (not (idx >= 0 and idx < numelts)) then
error("out-of-bounds "..showname.." read access", 2)
end
return ffi.cast(eltptr_t, ar)[idx]
end,
-- NOTE: this function will be dead code if the prefixed __newindex
-- errors out unconditionally or the bcarray is declared 'const'.
__newindex = function(ar, idx, val)
if (not (idx >= 0 and idx < numelts)) then
error("out-of-bounds "..showname.." write access", 2)
end
ffi.cast(eltptr_t, ar)[idx] = val
end,
}
if (mtadd ~= nil) then
local curindexf, curnewindexf = mt.__index, mt.__newindex
local addindexf, addnewindexf = mtadd.__index, mtadd.__newindex
if (addindexf) then
-- Additional __index metamethod given.
mt.__index = function(ar, idx)
local sth = addindexf(ar, idx)
if (sth ~= nil) then
return sth
end
return curindexf(ar, idx)
end
end
if (addnewindexf) then
-- Additional __newindex metamethod given.
mt.__newindex = function(ar, idx, val)
addnewindexf(ar, idx, val)
return curnewindexf(ar, idx, val)
end
end
end
local cdeclstr = "struct {"..flatten_array(numelts, rng).."}"
local bcarray_t = ffi.typeof(cdeclstr, ffi.typeof(basetype));
bcarray_t = ffi.metatype(bcarray_t, mt)
if (not (rng and rng.getu32==nil)) then
-- When passing a member name table, it is allowed to have a different
-- number of named members than array elements.
assert(ffi.sizeof(bcarray_t) == ffi.sizeof(basetype)*numelts)
end
if (typename ~= nil) then
-- Register the type name in the global namespace.
assert(type(typename)=="string")
ffi.cdef("typedef $ $;", bcarray_t, typename)
end
return bcarray_t
end
return bcarray

View file

@ -1,126 +0,0 @@
-- Bound-checking functions for engine and game "things".
local ffiC = require("ffi").C
local type = type
local error = error
local bcheck = {}
--== ENGINE ==--
function bcheck.sector_idx(sectnum)
if (not (sectnum >= 0 and sectnum < ffiC.numsectors)) then
error("invalid sector number "..sectnum, 3)
end
end
function bcheck.wall_idx(wallnum)
if (not (wallnum >= 0 and wallnum < ffiC.numwalls)) then
error("invalid wall number "..wallnum, 3)
end
end
-- TODO: Provide another function that also checks whether the sprite exists in
-- the game world (statnum != MAXSTATUS).
function bcheck.sprite_idx(spritenum)
-- if (not (spritenum >= 0 and spritenum < ffiC.MAXSPRITES)) then
if (not (spritenum >= 0 and spritenum < ffiC.MAXSPRITES)) then
error("invalid sprite number "..spritenum, 3)
end
end
function bcheck.tile_idx(tilenum)
if (not (tilenum >= 0 and tilenum < ffiC.MAXTILES)) then
error("invalid tile number "..tilenum, 3)
end
end
--== HELPERS ==--
function bcheck.number(val, errlev)
if (type(val)~="number" or val~=val) then
error("invalid argument: must be a non-NaN number", errlev or 3)
end
end
function bcheck.type(val, typestr, errlev)
if (type(val)~=typestr) then
error("invalid argument: must be a "..typestr, errlev or 3)
end
end
--== GAME ==--
if (ffiC.LUNATIC_CLIENT == ffiC.LUNATIC_CLIENT_MAPSTER32) then
return bcheck
end
local con_lang = require("con_lang")
function bcheck.player_idx(snum)
if (not (snum >= 0 and snum < ffiC.g_mostConcurrentPlayers)) then
error("invalid player number "..snum, 3)
end
end
function bcheck.sound_idx(sndidx)
if (not (sndidx >= 0 and sndidx < con_lang.MAXSOUNDS)) then
error("invalid sound number "..sndidx, 3)
end
end
function bcheck.weapon_idx(weap)
if (not (weap >= 0 and weap < ffiC.MAX_WEAPONS)) then
error("Invalid weapon ID "..weap, 3)
end
end
function bcheck.inventory_idx(inv)
if (not (inv >= 0 and inv < ffiC.GET_MAX)) then
error("Invalid inventory ID "..inv, 3)
end
end
function bcheck.volume_idx(volume)
if (not (volume >= 0 and volume < con_lang.MAXVOLUMES)) then
error("invalid volume number "..volume, 3)
end
end
function bcheck.level_idx(level)
if (not (level >= 0 and level < con_lang.MAXLEVELS)) then
error("invalid level number "..level, 3)
end
end
function bcheck.linear_map_idx(idx)
if (not (idx >= 0 and idx <= con_lang.MAXLEVELS * con_lang.MAXVOLUMES)) then
error("invalid linear map index "..idx, 3)
end
end
function bcheck.quote_idx(qnum, onlyidx)
if (not (qnum >= 0 and qnum < con_lang.MAXQUOTES)) then
error("invalid quote number "..qnum, 3)
end
local cstr = ffiC.apStrings[qnum]
if (onlyidx) then
return cstr
end
if (cstr == nil) then
error("null quote "..qnum, 3)
end
return cstr
end
function bcheck.top_level(funcname, errlev)
if (ffiC.g_elCallDepth > 0) then
error("Invalid use of "..funcname..": must be called from top level", errlev or 3)
end
end
return bcheck

View file

@ -1,293 +0,0 @@
-- "Bit array" module based on LuaJIT's BitOp.
local bit = require "bit"
local math = require "math"
local ffi = require "ffi"
local assert = assert
local error = error
local type = type
local tostring = tostring
module(...)
local bitar_ct = ffi.typeof[[
struct {
const double maxbidx; // last permissible bit index
const double maxidx; // last permissible int32_t index
const intptr_t arptr; // address of the int32_t array
}]]
local ptr_to_int = ffi.typeof("int32_t *")
local anchor = {}
-- population count of a nibble
local nibpop = ffi.new("double [?]", 16,
{ 0, 1, 1, 2, 1, 2, 2, 3,
1, 2, 2, 3, 2, 3, 3, 4 })
-- ...and of a byte
local bytepop = ffi.new("double [?]", 256)
for i=0,255 do
bytepop[i] = nibpop[bit.band(i, 15)] + nibpop[bit.rshift(i, 4)]
end
nibpop = nil
local function bitar_from_intar(maxbidx, maxidx, ar)
-- We need to have the int32_t[?] array be reachable so that it will not be
-- garbage collected
local ar_intptr = ffi.cast("intptr_t", ar)
anchor[tostring(ar_intptr)] = ar
-- Leaving the (potential) high trailing bits at 0 lets us not worry
-- about them in the population count calculation (__len metamethod).
-- Also, this is correct for maxbidx%32 == 0, since BitOp's shifts
-- mask the 5 lower bits of the counts.
local numremain = bit.band(maxbidx+1, 31)
ar[maxidx] = bit.band(ar[maxidx], bit.rshift(-1, 32-numremain))
return bitar_ct(maxbidx, maxidx, ar_intptr)
end
local function setop_common_rel(s1, s2)
if (s1.maxbidx ~= s2.maxbidx) then
error("bad arguments to bit array set op: must be of same length", 4)
end
local ar1 = ffi.cast(ptr_to_int, s1.arptr)
local ar2 = ffi.cast(ptr_to_int, s2.arptr)
return ar1, ar2
end
local function setop_common(s1, s2)
if (not ffi.istype(bitar_ct, s1) or not ffi.istype(bitar_ct, s2)) then
error("bad arguments to bit array set op: both must be 'bitar' types", 3)
end
local ar1, ar2 = setop_common_rel(s1, s2)
local ar = ffi.new("int32_t [?]", s1.maxidx+1)
return ar, ar1, ar2
end
local mt = {
--- Operational methods
__add = function(s1, s2) -- set union
local ar, ar1, ar2 = setop_common(s1, s2)
for i=0,s1.maxidx do
ar[i] = bit.bor(ar1[i], ar2[i])
end
return bitar_from_intar(s1.maxbidx, s1.maxidx, ar)
end,
__mul = function(s1, s2) -- set intersection
local ar, ar1, ar2 = setop_common(s1, s2)
for i=0,s1.maxidx do
ar[i] = bit.band(ar1[i], ar2[i])
end
return bitar_from_intar(s1.maxbidx, s1.maxidx, ar)
end,
__sub = function(s1, s2) -- set difference
local ar, ar1, ar2 = setop_common(s1, s2)
for i=0,s1.maxidx do
ar[i] = bit.band(ar1[i], bit.bnot(ar2[i]))
end
return bitar_from_intar(s1.maxbidx, s1.maxidx, ar)
end,
__unm = function(s) -- bitwise NOT
local newar = ffi.new("int32_t [?]", s.maxidx+1)
local oldar = ffi.cast(ptr_to_int, s.arptr)
for i=0,s.maxidx do
newar[i] = bit.bnot(oldar[i])
end
return bitar_from_intar(s.maxbidx, s.maxidx, newar)
end,
--- Additional operations
__index = {
-- TODO: Rename to 'testi', 'seti', 'cleari'; add 'flipi'?
-- Is bit i set?
isset = function(s, i)
if (not (i >= 0 and i<=s.maxbidx)) then
error("bad bit index for isset: must be in [0.."..s.maxbidx.."]", 2)
end
s = ffi.cast(ptr_to_int, s.arptr)
return (bit.band(s[bit.rshift(i, 5)], bit.lshift(1, i)) ~= 0)
end,
-- Clear bit i.
set0 = function(s, i)
if (not (i >= 0 and i<=s.maxbidx)) then
error("bad bit index for set0: must be in [0.."..s.maxbidx.."]", 2)
end
s = ffi.cast(ptr_to_int, s.arptr)
local jx = bit.rshift(i, 5)
s[jx] = bit.band(s[jx], bit.rol(0xfffffffe, i))
end,
-- Set bit i.
set1 = function(s, i)
if (not (i >= 0 and i<=s.maxbidx)) then
error("bad bit index for set1: must be in [0.."..s.maxbidx.."]", 2)
end
s = ffi.cast(ptr_to_int, s.arptr)
local jx = bit.rshift(i, 5)
s[jx] = bit.bor(s[jx], bit.rol(0x00000001, i))
end
},
--- Relational methods
__eq = function(s1, s2) -- set identity
local ar1, ar2 = setop_common_rel(s1, s2)
for i=0,s1.maxidx do
if (bit.bxor(ar1[i], ar2[i]) ~= 0) then
return false
end
end
return true
end,
__le = function(s1, s2)
local ar1, ar2 = setop_common_rel(s1, s2)
for i=0,s1.maxidx do
if (bit.band(ar1[i], bit.bnot(ar2[i])) ~= 0) then
return false
end
end
return true
end,
__lt = function(s1, s2)
return s1 <= s2 and not (s2 == s1)
end,
-- The length operator gets the population count of the bit array, i.e. the
-- number of set bits.
__len = function(s)
local ar = ffi.cast(ptr_to_int, s.arptr)
local popcnt = 0
for i=0,s.maxidx do
popcnt = popcnt + bytepop[bit.band(ar[i], 255)] +
bytepop[bit.band(bit.rshift(ar[i], 8), 255)] +
bytepop[bit.band(bit.rshift(ar[i], 16), 255)] +
bytepop[bit.rshift(ar[i], 24)]
end
return popcnt
end,
-- serialization
__tostring = function(s)
local size=s.maxidx+1
local ar = ffi.cast(ptr_to_int, s.arptr)
local hdr = "bitar.new("..s.maxbidx..", '"
local ofs = #hdr
local totalstrlen = ofs+8*size+2
local str = ffi.new("char [?]", totalstrlen)
ffi.copy(str, hdr, ofs)
for i=0,s.maxidx do
-- 'a' is ASCII 97
for nib=0,7 do
str[ofs + 8*i + nib] = 97 + bit.band(bit.rshift(ar[i], 4*nib), 0x0000000f)
end
end
ffi.copy(str+totalstrlen-2, "')", 2)
return ffi.string(str, totalstrlen)
end,
-- On garbage collection of the bitar, clear the array's anchor so that it
-- can be collected too.
__gc = function(s)
anchor[tostring(s.arptr)] = nil
end,
__metatable = true,
}
ffi.metatype(bitar_ct, mt)
-- Create new bit array.
function new(maxbidx, initval)
if (type(maxbidx) ~= "number" or not (maxbidx >= 0 and maxbidx <= (2^31)-2)) then
-- NOTE: Uh-oh, we can't write '2^31' because that would be interpreted
-- as color by OSD_Printf.
error("bad argument #1 to bitar.new (must be a number in [0..(2**31)-2])", 2)
end
if (math.floor(maxbidx) ~= maxbidx) then
error("bad argument #1 to bitar.new (must be an integral number)")
end
if (ffi.istype(ptr_to_int, initval)) then
-- Initialization from an array on the C side. INTERNAL.
-- Cannot be reached by user code since there's no access to the FFI
-- (and thus no way to create pointers).
return bitar_from_intar(maxbidx, (maxbidx+1)/32-1, initval)
elseif (type(initval)=="string") then
-- String containing hex digits (a..p) given, for INTERNAL use only.
-- XXX: Can be reached by user code.
local lstr = initval
local numnibs = #lstr
assert(numnibs%8 == 0)
local size = numnibs/8
local maxidx = size-1
local ar = ffi.new("int32_t [?]", size)
local str = ffi.new("char [?]", numnibs)
ffi.copy(str, lstr, numnibs)
for i=0,maxidx do
ar[i] = 0
for nib=0,7 do
local hexdig = str[8*i + nib]
assert(hexdig >= 97 and hexdig < 97+16)
ar[i] = bit.bor(ar[i], bit.lshift(hexdig-97, 4*nib))
end
end
-- NOTE: <maxbidx> cannot be extracted from the string, use the passed one.
return bitar_from_intar(maxbidx, maxidx, ar)
else
-- User-requested bitar creation.
if (initval ~= 0 and initval ~= 1) then
error("bad argument #2 to bitar.new (must be either 0 or 1)", 2)
end
local maxidx = math.floor(maxbidx/32)
local size = maxidx+1
local ar = ffi.new("int32_t [?]", size)
if (initval==1) then
ffi.fill(ar, size*4, -1)
end
return bitar_from_intar(maxbidx, maxidx, ar)
end
end

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,20 +0,0 @@
----------------------------------------------------------------------------
-- LuaJIT x64 disassembler wrapper module.
--
-- Copyright (C) 2005-2015 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This module just exports the 64 bit functions from the combined
-- x86/x64 disassembler module. All the interesting stuff is there.
------------------------------------------------------------------------------
local require = require
module(...)
local dis_x86 = require(_PACKAGE.."dis_x86")
create = dis_x86.create64
disass = dis_x86.disass64
regname = dis_x86.regname64

View file

@ -1,836 +0,0 @@
----------------------------------------------------------------------------
-- LuaJIT x86/x64 disassembler module.
--
-- Copyright (C) 2005-2015 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This is a helper module used by the LuaJIT machine code dumper module.
--
-- Sending small code snippets to an external disassembler and mixing the
-- output with our own stuff was too fragile. So I had to bite the bullet
-- and write yet another x86 disassembler. Oh well ...
--
-- The output format is very similar to what ndisasm generates. But it has
-- been developed independently by looking at the opcode tables from the
-- Intel and AMD manuals. The supported instruction set is quite extensive
-- and reflects what a current generation Intel or AMD CPU implements in
-- 32 bit and 64 bit mode. Yes, this includes MMX, SSE, SSE2, SSE3, SSSE3,
-- SSE4.1, SSE4.2, SSE4a and even privileged and hypervisor (VMX/SVM)
-- instructions.
--
-- Notes:
-- * The (useless) a16 prefix, 3DNow and pre-586 opcodes are unsupported.
-- * No attempt at optimization has been made -- it's fast enough for my needs.
-- * The public API may change when more architectures are added.
------------------------------------------------------------------------------
local type = type
local sub, byte, format = string.sub, string.byte, string.format
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
local lower, rep = string.lower, string.rep
-- Map for 1st opcode byte in 32 bit mode. Ugly? Well ... read on.
local map_opc1_32 = {
--0x
[0]="addBmr","addVmr","addBrm","addVrm","addBai","addVai","push es","pop es",
"orBmr","orVmr","orBrm","orVrm","orBai","orVai","push cs","opc2*",
--1x
"adcBmr","adcVmr","adcBrm","adcVrm","adcBai","adcVai","push ss","pop ss",
"sbbBmr","sbbVmr","sbbBrm","sbbVrm","sbbBai","sbbVai","push ds","pop ds",
--2x
"andBmr","andVmr","andBrm","andVrm","andBai","andVai","es:seg","daa",
"subBmr","subVmr","subBrm","subVrm","subBai","subVai","cs:seg","das",
--3x
"xorBmr","xorVmr","xorBrm","xorVrm","xorBai","xorVai","ss:seg","aaa",
"cmpBmr","cmpVmr","cmpBrm","cmpVrm","cmpBai","cmpVai","ds:seg","aas",
--4x
"incVR","incVR","incVR","incVR","incVR","incVR","incVR","incVR",
"decVR","decVR","decVR","decVR","decVR","decVR","decVR","decVR",
--5x
"pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR",
"popUR","popUR","popUR","popUR","popUR","popUR","popUR","popUR",
--6x
"sz*pushaw,pusha","sz*popaw,popa","boundVrm","arplWmr",
"fs:seg","gs:seg","o16:","a16",
"pushUi","imulVrmi","pushBs","imulVrms",
"insb","insVS","outsb","outsVS",
--7x
"joBj","jnoBj","jbBj","jnbBj","jzBj","jnzBj","jbeBj","jaBj",
"jsBj","jnsBj","jpeBj","jpoBj","jlBj","jgeBj","jleBj","jgBj",
--8x
"arith!Bmi","arith!Vmi","arith!Bmi","arith!Vms",
"testBmr","testVmr","xchgBrm","xchgVrm",
"movBmr","movVmr","movBrm","movVrm",
"movVmg","leaVrm","movWgm","popUm",
--9x
"nop*xchgVaR|pause|xchgWaR|repne nop","xchgVaR","xchgVaR","xchgVaR",
"xchgVaR","xchgVaR","xchgVaR","xchgVaR",
"sz*cbw,cwde,cdqe","sz*cwd,cdq,cqo","call farViw","wait",
"sz*pushfw,pushf","sz*popfw,popf","sahf","lahf",
--Ax
"movBao","movVao","movBoa","movVoa",
"movsb","movsVS","cmpsb","cmpsVS",
"testBai","testVai","stosb","stosVS",
"lodsb","lodsVS","scasb","scasVS",
--Bx
"movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi",
"movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI",
--Cx
"shift!Bmu","shift!Vmu","retBw","ret","$lesVrm","$ldsVrm","movBmi","movVmi",
"enterBwu","leave","retfBw","retf","int3","intBu","into","iretVS",
--Dx
"shift!Bm1","shift!Vm1","shift!Bmc","shift!Vmc","aamBu","aadBu","salc","xlatb",
"fp*0","fp*1","fp*2","fp*3","fp*4","fp*5","fp*6","fp*7",
--Ex
"loopneBj","loopeBj","loopBj","sz*jcxzBj,jecxzBj,jrcxzBj",
"inBau","inVau","outBua","outVua",
"callVj","jmpVj","jmp farViw","jmpBj","inBad","inVad","outBda","outVda",
--Fx
"lock:","int1","repne:rep","rep:","hlt","cmc","testb!Bm","testv!Vm",
"clc","stc","cli","sti","cld","std","incb!Bm","incd!Vm",
}
assert(#map_opc1_32 == 255)
-- Map for 1st opcode byte in 64 bit mode (overrides only).
local map_opc1_64 = setmetatable({
[0x06]=false, [0x07]=false, [0x0e]=false,
[0x16]=false, [0x17]=false, [0x1e]=false, [0x1f]=false,
[0x27]=false, [0x2f]=false, [0x37]=false, [0x3f]=false,
[0x60]=false, [0x61]=false, [0x62]=false, [0x63]="movsxdVrDmt", [0x67]="a32:",
[0x40]="rex*", [0x41]="rex*b", [0x42]="rex*x", [0x43]="rex*xb",
[0x44]="rex*r", [0x45]="rex*rb", [0x46]="rex*rx", [0x47]="rex*rxb",
[0x48]="rex*w", [0x49]="rex*wb", [0x4a]="rex*wx", [0x4b]="rex*wxb",
[0x4c]="rex*wr", [0x4d]="rex*wrb", [0x4e]="rex*wrx", [0x4f]="rex*wrxb",
[0x82]=false, [0x9a]=false, [0xc4]=false, [0xc5]=false, [0xce]=false,
[0xd4]=false, [0xd5]=false, [0xd6]=false, [0xea]=false,
}, { __index = map_opc1_32 })
-- Map for 2nd opcode byte (0F xx). True CISC hell. Hey, I told you.
-- Prefix dependent MMX/SSE opcodes: (none)|rep|o16|repne, -|F3|66|F2
local map_opc2 = {
--0x
[0]="sldt!Dmp","sgdt!Ump","larVrm","lslVrm",nil,"syscall","clts","sysret",
"invd","wbinvd",nil,"ud1",nil,"$prefetch!Bm","femms","3dnowMrmu",
--1x
"movupsXrm|movssXrm|movupdXrm|movsdXrm",
"movupsXmr|movssXmr|movupdXmr|movsdXmr",
"movhlpsXrm$movlpsXrm|movsldupXrm|movlpdXrm|movddupXrm",
"movlpsXmr||movlpdXmr",
"unpcklpsXrm||unpcklpdXrm",
"unpckhpsXrm||unpckhpdXrm",
"movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm",
"movhpsXmr||movhpdXmr",
"$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm",
"hintnopVm","hintnopVm","hintnopVm","hintnopVm",
--2x
"movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil,
"movapsXrm||movapdXrm",
"movapsXmr||movapdXmr",
"cvtpi2psXrMm|cvtsi2ssXrVmt|cvtpi2pdXrMm|cvtsi2sdXrVmt",
"movntpsXmr|movntssXmr|movntpdXmr|movntsdXmr",
"cvttps2piMrXm|cvttss2siVrXm|cvttpd2piMrXm|cvttsd2siVrXm",
"cvtps2piMrXm|cvtss2siVrXm|cvtpd2piMrXm|cvtsd2siVrXm",
"ucomissXrm||ucomisdXrm",
"comissXrm||comisdXrm",
--3x
"wrmsr","rdtsc","rdmsr","rdpmc","sysenter","sysexit",nil,"getsec",
"opc3*38",nil,"opc3*3a",nil,nil,nil,nil,nil,
--4x
"cmovoVrm","cmovnoVrm","cmovbVrm","cmovnbVrm",
"cmovzVrm","cmovnzVrm","cmovbeVrm","cmovaVrm",
"cmovsVrm","cmovnsVrm","cmovpeVrm","cmovpoVrm",
"cmovlVrm","cmovgeVrm","cmovleVrm","cmovgVrm",
--5x
"movmskpsVrXm$||movmskpdVrXm$","sqrtpsXrm|sqrtssXrm|sqrtpdXrm|sqrtsdXrm",
"rsqrtpsXrm|rsqrtssXrm","rcppsXrm|rcpssXrm",
"andpsXrm||andpdXrm","andnpsXrm||andnpdXrm",
"orpsXrm||orpdXrm","xorpsXrm||xorpdXrm",
"addpsXrm|addssXrm|addpdXrm|addsdXrm","mulpsXrm|mulssXrm|mulpdXrm|mulsdXrm",
"cvtps2pdXrm|cvtss2sdXrm|cvtpd2psXrm|cvtsd2ssXrm",
"cvtdq2psXrm|cvttps2dqXrm|cvtps2dqXrm",
"subpsXrm|subssXrm|subpdXrm|subsdXrm","minpsXrm|minssXrm|minpdXrm|minsdXrm",
"divpsXrm|divssXrm|divpdXrm|divsdXrm","maxpsXrm|maxssXrm|maxpdXrm|maxsdXrm",
--6x
"punpcklbwPrm","punpcklwdPrm","punpckldqPrm","packsswbPrm",
"pcmpgtbPrm","pcmpgtwPrm","pcmpgtdPrm","packuswbPrm",
"punpckhbwPrm","punpckhwdPrm","punpckhdqPrm","packssdwPrm",
"||punpcklqdqXrm","||punpckhqdqXrm",
"movPrVSm","movqMrm|movdquXrm|movdqaXrm",
--7x
"pshufwMrmu|pshufhwXrmu|pshufdXrmu|pshuflwXrmu","pshiftw!Pmu",
"pshiftd!Pmu","pshiftq!Mmu||pshiftdq!Xmu",
"pcmpeqbPrm","pcmpeqwPrm","pcmpeqdPrm","emms|",
"vmreadUmr||extrqXmuu$|insertqXrmuu$","vmwriteUrm||extrqXrm$|insertqXrm$",
nil,nil,
"||haddpdXrm|haddpsXrm","||hsubpdXrm|hsubpsXrm",
"movVSmMr|movqXrm|movVSmXr","movqMmr|movdquXmr|movdqaXmr",
--8x
"joVj","jnoVj","jbVj","jnbVj","jzVj","jnzVj","jbeVj","jaVj",
"jsVj","jnsVj","jpeVj","jpoVj","jlVj","jgeVj","jleVj","jgVj",
--9x
"setoBm","setnoBm","setbBm","setnbBm","setzBm","setnzBm","setbeBm","setaBm",
"setsBm","setnsBm","setpeBm","setpoBm","setlBm","setgeBm","setleBm","setgBm",
--Ax
"push fs","pop fs","cpuid","btVmr","shldVmru","shldVmrc",nil,nil,
"push gs","pop gs","rsm","btsVmr","shrdVmru","shrdVmrc","fxsave!Dmp","imulVrm",
--Bx
"cmpxchgBmr","cmpxchgVmr","$lssVrm","btrVmr",
"$lfsVrm","$lgsVrm","movzxVrBmt","movzxVrWmt",
"|popcntVrm","ud2Dp","bt!Vmu","btcVmr",
"bsfVrm","bsrVrm|lzcntVrm|bsrWrm","movsxVrBmt","movsxVrWmt",
--Cx
"xaddBmr","xaddVmr",
"cmppsXrmu|cmpssXrmu|cmppdXrmu|cmpsdXrmu","$movntiVmr|",
"pinsrwPrWmu","pextrwDrPmu",
"shufpsXrmu||shufpdXrmu","$cmpxchg!Qmp",
"bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR",
--Dx
"||addsubpdXrm|addsubpsXrm","psrlwPrm","psrldPrm","psrlqPrm",
"paddqPrm","pmullwPrm",
"|movq2dqXrMm|movqXmr|movdq2qMrXm$","pmovmskbVrMm||pmovmskbVrXm",
"psubusbPrm","psubuswPrm","pminubPrm","pandPrm",
"paddusbPrm","padduswPrm","pmaxubPrm","pandnPrm",
--Ex
"pavgbPrm","psrawPrm","psradPrm","pavgwPrm",
"pmulhuwPrm","pmulhwPrm",
"|cvtdq2pdXrm|cvttpd2dqXrm|cvtpd2dqXrm","$movntqMmr||$movntdqXmr",
"psubsbPrm","psubswPrm","pminswPrm","porPrm",
"paddsbPrm","paddswPrm","pmaxswPrm","pxorPrm",
--Fx
"|||lddquXrm","psllwPrm","pslldPrm","psllqPrm",
"pmuludqPrm","pmaddwdPrm","psadbwPrm","maskmovqMrm||maskmovdquXrm$",
"psubbPrm","psubwPrm","psubdPrm","psubqPrm",
"paddbPrm","paddwPrm","padddPrm","ud",
}
assert(map_opc2[255] == "ud")
-- Map for three-byte opcodes. Can't wait for their next invention.
local map_opc3 = {
["38"] = { -- [66] 0f 38 xx
--0x
[0]="pshufbPrm","phaddwPrm","phadddPrm","phaddswPrm",
"pmaddubswPrm","phsubwPrm","phsubdPrm","phsubswPrm",
"psignbPrm","psignwPrm","psigndPrm","pmulhrswPrm",
nil,nil,nil,nil,
--1x
"||pblendvbXrma",nil,nil,nil,
"||blendvpsXrma","||blendvpdXrma",nil,"||ptestXrm",
nil,nil,nil,nil,
"pabsbPrm","pabswPrm","pabsdPrm",nil,
--2x
"||pmovsxbwXrm","||pmovsxbdXrm","||pmovsxbqXrm","||pmovsxwdXrm",
"||pmovsxwqXrm","||pmovsxdqXrm",nil,nil,
"||pmuldqXrm","||pcmpeqqXrm","||$movntdqaXrm","||packusdwXrm",
nil,nil,nil,nil,
--3x
"||pmovzxbwXrm","||pmovzxbdXrm","||pmovzxbqXrm","||pmovzxwdXrm",
"||pmovzxwqXrm","||pmovzxdqXrm",nil,"||pcmpgtqXrm",
"||pminsbXrm","||pminsdXrm","||pminuwXrm","||pminudXrm",
"||pmaxsbXrm","||pmaxsdXrm","||pmaxuwXrm","||pmaxudXrm",
--4x
"||pmulddXrm","||phminposuwXrm",
--Fx
[0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt",
},
["3a"] = { -- [66] 0f 3a xx
--0x
[0x00]=nil,nil,nil,nil,nil,nil,nil,nil,
"||roundpsXrmu","||roundpdXrmu","||roundssXrmu","||roundsdXrmu",
"||blendpsXrmu","||blendpdXrmu","||pblendwXrmu","palignrPrmu",
--1x
nil,nil,nil,nil,
"||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru",
nil,nil,nil,nil,nil,nil,nil,nil,
--2x
"||pinsrbXrVmu","||insertpsXrmu","||pinsrXrVmuS",nil,
--4x
[0x40] = "||dppsXrmu",
[0x41] = "||dppdXrmu",
[0x42] = "||mpsadbwXrmu",
--6x
[0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu",
[0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu",
},
}
-- Map for VMX/SVM opcodes 0F 01 C0-FF (sgdt group with register operands).
local map_opcvm = {
[0xc1]="vmcall",[0xc2]="vmlaunch",[0xc3]="vmresume",[0xc4]="vmxoff",
[0xc8]="monitor",[0xc9]="mwait",
[0xd8]="vmrun",[0xd9]="vmmcall",[0xda]="vmload",[0xdb]="vmsave",
[0xdc]="stgi",[0xdd]="clgi",[0xde]="skinit",[0xdf]="invlpga",
[0xf8]="swapgs",[0xf9]="rdtscp",
}
-- Map for FP opcodes. And you thought stack machines are simple?
local map_opcfp = {
-- D8-DF 00-BF: opcodes with a memory operand.
-- D8
[0]="faddFm","fmulFm","fcomFm","fcompFm","fsubFm","fsubrFm","fdivFm","fdivrFm",
"fldFm",nil,"fstFm","fstpFm","fldenvVm","fldcwWm","fnstenvVm","fnstcwWm",
-- DA
"fiaddDm","fimulDm","ficomDm","ficompDm",
"fisubDm","fisubrDm","fidivDm","fidivrDm",
-- DB
"fildDm","fisttpDm","fistDm","fistpDm",nil,"fld twordFmp",nil,"fstp twordFmp",
-- DC
"faddGm","fmulGm","fcomGm","fcompGm","fsubGm","fsubrGm","fdivGm","fdivrGm",
-- DD
"fldGm","fisttpQm","fstGm","fstpGm","frstorDmp",nil,"fnsaveDmp","fnstswWm",
-- DE
"fiaddWm","fimulWm","ficomWm","ficompWm",
"fisubWm","fisubrWm","fidivWm","fidivrWm",
-- DF
"fildWm","fisttpWm","fistWm","fistpWm",
"fbld twordFmp","fildQm","fbstp twordFmp","fistpQm",
-- xx C0-FF: opcodes with a pseudo-register operand.
-- D8
"faddFf","fmulFf","fcomFf","fcompFf","fsubFf","fsubrFf","fdivFf","fdivrFf",
-- D9
"fldFf","fxchFf",{"fnop"},nil,
{"fchs","fabs",nil,nil,"ftst","fxam"},
{"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz"},
{"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp"},
{"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"},
-- DA
"fcmovbFf","fcmoveFf","fcmovbeFf","fcmovuFf",nil,{nil,"fucompp"},nil,nil,
-- DB
"fcmovnbFf","fcmovneFf","fcmovnbeFf","fcmovnuFf",
{nil,nil,"fnclex","fninit"},"fucomiFf","fcomiFf",nil,
-- DC
"fadd toFf","fmul toFf",nil,nil,
"fsub toFf","fsubr toFf","fdivr toFf","fdiv toFf",
-- DD
"ffreeFf",nil,"fstFf","fstpFf","fucomFf","fucompFf",nil,nil,
-- DE
"faddpFf","fmulpFf",nil,{nil,"fcompp"},
"fsubrpFf","fsubpFf","fdivrpFf","fdivpFf",
-- DF
nil,nil,nil,nil,{"fnstsw ax"},"fucomipFf","fcomipFf",nil,
}
assert(map_opcfp[126] == "fcomipFf")
-- Map for opcode groups. The subkey is sp from the ModRM byte.
local map_opcgroup = {
arith = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" },
shift = { "rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar" },
testb = { "testBmi", "testBmi", "not", "neg", "mul", "imul", "div", "idiv" },
testv = { "testVmi", "testVmi", "not", "neg", "mul", "imul", "div", "idiv" },
incb = { "inc", "dec" },
incd = { "inc", "dec", "callUmp", "$call farDmp",
"jmpUmp", "$jmp farDmp", "pushUm" },
sldt = { "sldt", "str", "lldt", "ltr", "verr", "verw" },
sgdt = { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt",
"smsw", nil, "lmsw", "vm*$invlpg" },
bt = { nil, nil, nil, nil, "bt", "bts", "btr", "btc" },
cmpxchg = { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil,
nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" },
pshiftw = { nil, nil, "psrlw", nil, "psraw", nil, "psllw" },
pshiftd = { nil, nil, "psrld", nil, "psrad", nil, "pslld" },
pshiftq = { nil, nil, "psrlq", nil, nil, nil, "psllq" },
pshiftdq = { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" },
fxsave = { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr",
nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" },
prefetch = { "prefetch", "prefetchw" },
prefetcht = { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" },
}
------------------------------------------------------------------------------
-- Maps for register names.
local map_regs = {
B = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
B64 = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
W = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
"r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" },
D = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
"r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" },
Q = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" },
M = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext!
X = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" },
}
local map_segregs = { "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7" }
-- Maps for size names.
local map_sz2n = {
B = 1, W = 2, D = 4, Q = 8, M = 8, X = 16,
}
local map_sz2prefix = {
B = "byte", W = "word", D = "dword",
Q = "qword",
M = "qword", X = "xword",
F = "dword", G = "qword", -- No need for sizes/register names for these two.
}
------------------------------------------------------------------------------
-- Output a nicely formatted line with an opcode and operands.
local function putop(ctx, text, operands)
local code, pos, hex = ctx.code, ctx.pos, ""
local hmax = ctx.hexdump
if hmax > 0 then
for i=ctx.start,pos-1 do
hex = hex..format("%02X", byte(code, i, i))
end
if #hex > hmax then hex = sub(hex, 1, hmax)..". "
else hex = hex..rep(" ", hmax-#hex+2) end
end
if operands then text = text.." "..operands end
if ctx.o16 then text = "o16 "..text; ctx.o16 = false end
if ctx.a32 then text = "a32 "..text; ctx.a32 = false end
if ctx.rep then text = ctx.rep.." "..text; ctx.rep = false end
if ctx.rex then
local t = (ctx.rexw and "w" or "")..(ctx.rexr and "r" or "")..
(ctx.rexx and "x" or "")..(ctx.rexb and "b" or "")
if t ~= "" then text = "rex."..t.." "..text end
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
ctx.rex = false
end
if ctx.seg then
local text2, n = gsub(text, "%[", "["..ctx.seg..":")
if n == 0 then text = ctx.seg.." "..text else text = text2 end
ctx.seg = false
end
if ctx.lock then text = "lock "..text; ctx.lock = false end
local imm = ctx.imm
if imm then
local sym = ctx.symtab[imm]
if sym then text = text.."\t->"..sym end
end
ctx.out(format("%08x %s%s\n", ctx.addr+ctx.start, hex, text))
ctx.mrm = false
ctx.start = pos
ctx.imm = nil
end
-- Clear all prefix flags.
local function clearprefixes(ctx)
ctx.o16 = false; ctx.seg = false; ctx.lock = false; ctx.rep = false
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
ctx.rex = false; ctx.a32 = false
end
-- Fallback for incomplete opcodes at the end.
local function incomplete(ctx)
ctx.pos = ctx.stop+1
clearprefixes(ctx)
return putop(ctx, "(incomplete)")
end
-- Fallback for unknown opcodes.
local function unknown(ctx)
clearprefixes(ctx)
return putop(ctx, "(unknown)")
end
-- Return an immediate of the specified size.
local function getimm(ctx, pos, n)
if pos+n-1 > ctx.stop then return incomplete(ctx) end
local code = ctx.code
if n == 1 then
local b1 = byte(code, pos, pos)
return b1
elseif n == 2 then
local b1, b2 = byte(code, pos, pos+1)
return b1+b2*256
else
local b1, b2, b3, b4 = byte(code, pos, pos+3)
local imm = b1+b2*256+b3*65536+b4*16777216
ctx.imm = imm
return imm
end
end
-- Process pattern string and generate the operands.
local function putpat(ctx, name, pat)
local operands, regs, sz, mode, sp, rm, sc, rx, sdisp
local code, pos, stop = ctx.code, ctx.pos, ctx.stop
-- Chars used: 1DFGIMPQRSTUVWXacdfgijmoprstuwxyz
for p in gmatch(pat, ".") do
local x = nil
if p == "V" or p == "U" then
if ctx.rexw then sz = "Q"; ctx.rexw = false
elseif ctx.o16 then sz = "W"; ctx.o16 = false
elseif p == "U" and ctx.x64 then sz = "Q"
else sz = "D" end
regs = map_regs[sz]
elseif p == "T" then
if ctx.rexw then sz = "Q"; ctx.rexw = false else sz = "D" end
regs = map_regs[sz]
elseif p == "B" then
sz = "B"
regs = ctx.rex and map_regs.B64 or map_regs.B
elseif match(p, "[WDQMXFG]") then
sz = p
regs = map_regs[sz]
elseif p == "P" then
sz = ctx.o16 and "X" or "M"; ctx.o16 = false
regs = map_regs[sz]
elseif p == "S" then
name = name..lower(sz)
elseif p == "s" then
local imm = getimm(ctx, pos, 1); if not imm then return end
x = imm <= 127 and format("+0x%02x", imm)
or format("-0x%02x", 256-imm)
pos = pos+1
elseif p == "u" then
local imm = getimm(ctx, pos, 1); if not imm then return end
x = format("0x%02x", imm)
pos = pos+1
elseif p == "w" then
local imm = getimm(ctx, pos, 2); if not imm then return end
x = format("0x%x", imm)
pos = pos+2
elseif p == "o" then -- [offset]
if ctx.x64 then
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
x = format("[0x%08x%08x]", imm2, imm1)
pos = pos+8
else
local imm = getimm(ctx, pos, 4); if not imm then return end
x = format("[0x%08x]", imm)
pos = pos+4
end
elseif p == "i" or p == "I" then
local n = map_sz2n[sz]
if n == 8 and ctx.x64 and p == "I" then
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
x = format("0x%08x%08x", imm2, imm1)
else
if n == 8 then n = 4 end
local imm = getimm(ctx, pos, n); if not imm then return end
if sz == "Q" and (imm < 0 or imm > 0x7fffffff) then
imm = (0xffffffff+1)-imm
x = format(imm > 65535 and "-0x%08x" or "-0x%x", imm)
else
x = format(imm > 65535 and "0x%08x" or "0x%x", imm)
end
end
pos = pos+n
elseif p == "j" then
local n = map_sz2n[sz]
if n == 8 then n = 4 end
local imm = getimm(ctx, pos, n); if not imm then return end
if sz == "B" and imm > 127 then imm = imm-256
elseif imm > 2147483647 then imm = imm-4294967296 end
pos = pos+n
imm = imm + pos + ctx.addr
if imm > 4294967295 and not ctx.x64 then imm = imm-4294967296 end
ctx.imm = imm
if sz == "W" then
x = format("word 0x%04x", imm%65536)
elseif ctx.x64 then
local lo = imm % 0x1000000
x = format("0x%02x%06x", (imm-lo) / 0x1000000, lo)
else
x = format("0x%08x", imm)
end
elseif p == "R" then
local r = byte(code, pos-1, pos-1)%8
if ctx.rexb then r = r + 8; ctx.rexb = false end
x = regs[r+1]
elseif p == "a" then x = regs[1]
elseif p == "c" then x = "cl"
elseif p == "d" then x = "dx"
elseif p == "1" then x = "1"
else
if not mode then
mode = ctx.mrm
if not mode then
if pos > stop then return incomplete(ctx) end
mode = byte(code, pos, pos)
pos = pos+1
end
rm = mode%8; mode = (mode-rm)/8
sp = mode%8; mode = (mode-sp)/8
sdisp = ""
if mode < 3 then
if rm == 4 then
if pos > stop then return incomplete(ctx) end
sc = byte(code, pos, pos)
pos = pos+1
rm = sc%8; sc = (sc-rm)/8
rx = sc%8; sc = (sc-rx)/8
if ctx.rexx then rx = rx + 8; ctx.rexx = false end
if rx == 4 then rx = nil end
end
if mode > 0 or rm == 5 then
local dsz = mode
if dsz ~= 1 then dsz = 4 end
local disp = getimm(ctx, pos, dsz); if not disp then return end
if mode == 0 then rm = nil end
if rm or rx or (not sc and ctx.x64 and not ctx.a32) then
if dsz == 1 and disp > 127 then
sdisp = format("-0x%x", 256-disp)
elseif disp >= 0 and disp <= 0x7fffffff then
sdisp = format("+0x%x", disp)
else
sdisp = format("-0x%x", (0xffffffff+1)-disp)
end
else
sdisp = format(ctx.x64 and not ctx.a32 and
not (disp >= 0 and disp <= 0x7fffffff)
and "0xffffffff%08x" or "0x%08x", disp)
end
pos = pos+dsz
end
end
if rm and ctx.rexb then rm = rm + 8; ctx.rexb = false end
if ctx.rexr then sp = sp + 8; ctx.rexr = false end
end
if p == "m" then
if mode == 3 then x = regs[rm+1]
else
local aregs = ctx.a32 and map_regs.D or ctx.aregs
local srm, srx = "", ""
if rm then srm = aregs[rm+1]
elseif not sc and ctx.x64 and not ctx.a32 then srm = "rip" end
ctx.a32 = false
if rx then
if rm then srm = srm.."+" end
srx = aregs[rx+1]
if sc > 0 then srx = srx.."*"..(2^sc) end
end
x = format("[%s%s%s]", srm, srx, sdisp)
end
if mode < 3 and
(not match(pat, "[aRrgp]") or match(pat, "t")) then -- Yuck.
x = map_sz2prefix[sz].." "..x
end
elseif p == "r" then x = regs[sp+1]
elseif p == "g" then x = map_segregs[sp+1]
elseif p == "p" then -- Suppress prefix.
elseif p == "f" then x = "st"..rm
elseif p == "x" then
if sp == 0 and ctx.lock and not ctx.x64 then
x = "CR8"; ctx.lock = false
else
x = "CR"..sp
end
elseif p == "y" then x = "DR"..sp
elseif p == "z" then x = "TR"..sp
elseif p == "t" then
else
error("bad pattern `"..pat.."'")
end
end
if x then operands = operands and operands..", "..x or x end
end
ctx.pos = pos
return putop(ctx, name, operands)
end
-- Forward declaration.
local map_act
-- Fetch and cache MRM byte.
local function getmrm(ctx)
local mrm = ctx.mrm
if not mrm then
local pos = ctx.pos
if pos > ctx.stop then return nil end
mrm = byte(ctx.code, pos, pos)
ctx.pos = pos+1
ctx.mrm = mrm
end
return mrm
end
-- Dispatch to handler depending on pattern.
local function dispatch(ctx, opat, patgrp)
if not opat then return unknown(ctx) end
if match(opat, "%|") then -- MMX/SSE variants depending on prefix.
local p
if ctx.rep then
p = ctx.rep=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)"
ctx.rep = false
elseif ctx.o16 then p = "%|[^%|]*%|([^%|]*)"; ctx.o16 = false
else p = "^[^%|]*" end
opat = match(opat, p)
if not opat then return unknown(ctx) end
-- ctx.rep = false; ctx.o16 = false
--XXX fails for 66 f2 0f 38 f1 06 crc32 eax,WORD PTR [esi]
--XXX remove in branches?
end
if match(opat, "%$") then -- reg$mem variants.
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
opat = match(opat, mrm >= 192 and "^[^%$]*" or "%$(.*)")
if opat == "" then return unknown(ctx) end
end
if opat == "" then return unknown(ctx) end
local name, pat = match(opat, "^([a-z0-9 ]*)(.*)")
if pat == "" and patgrp then pat = patgrp end
return map_act[sub(pat, 1, 1)](ctx, name, pat)
end
-- Get a pattern from an opcode map and dispatch to handler.
local function dispatchmap(ctx, opcmap)
local pos = ctx.pos
local opat = opcmap[byte(ctx.code, pos, pos)]
pos = pos + 1
ctx.pos = pos
return dispatch(ctx, opat)
end
-- Map for action codes. The key is the first char after the name.
map_act = {
-- Simple opcodes without operands.
[""] = function(ctx, name, pat)
return putop(ctx, name)
end,
-- Operand size chars fall right through.
B = putpat, W = putpat, D = putpat, Q = putpat,
V = putpat, U = putpat, T = putpat,
M = putpat, X = putpat, P = putpat,
F = putpat, G = putpat,
-- Collect prefixes.
[":"] = function(ctx, name, pat)
ctx[pat == ":" and name or sub(pat, 2)] = name
if ctx.pos - ctx.start > 5 then return unknown(ctx) end -- Limit #prefixes.
end,
-- Chain to special handler specified by name.
["*"] = function(ctx, name, pat)
return map_act[name](ctx, name, sub(pat, 2))
end,
-- Use named subtable for opcode group.
["!"] = function(ctx, name, pat)
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
return dispatch(ctx, map_opcgroup[name][((mrm-(mrm%8))/8)%8+1], sub(pat, 2))
end,
-- o16,o32[,o64] variants.
sz = function(ctx, name, pat)
if ctx.o16 then ctx.o16 = false
else
pat = match(pat, ",(.*)")
if ctx.rexw then
local p = match(pat, ",(.*)")
if p then pat = p; ctx.rexw = false end
end
end
pat = match(pat, "^[^,]*")
return dispatch(ctx, pat)
end,
-- Two-byte opcode dispatch.
opc2 = function(ctx, name, pat)
return dispatchmap(ctx, map_opc2)
end,
-- Three-byte opcode dispatch.
opc3 = function(ctx, name, pat)
return dispatchmap(ctx, map_opc3[pat])
end,
-- VMX/SVM dispatch.
vm = function(ctx, name, pat)
return dispatch(ctx, map_opcvm[ctx.mrm])
end,
-- Floating point opcode dispatch.
fp = function(ctx, name, pat)
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
local rm = mrm%8
local idx = pat*8 + ((mrm-rm)/8)%8
if mrm >= 192 then idx = idx + 64 end
local opat = map_opcfp[idx]
if type(opat) == "table" then opat = opat[rm+1] end
return dispatch(ctx, opat)
end,
-- REX prefix.
rex = function(ctx, name, pat)
if ctx.rex then return unknown(ctx) end -- Only 1 REX prefix allowed.
for p in gmatch(pat, ".") do ctx["rex"..p] = true end
ctx.rex = true
end,
-- Special case for nop with REX prefix.
nop = function(ctx, name, pat)
return dispatch(ctx, ctx.rex and pat or "nop")
end,
}
------------------------------------------------------------------------------
-- Disassemble a block of code.
local function disass_block(ctx, ofs, len)
if not ofs then ofs = 0 end
local stop = len and ofs+len or #ctx.code
ofs = ofs + 1
ctx.start = ofs
ctx.pos = ofs
ctx.stop = stop
ctx.imm = nil
ctx.mrm = false
clearprefixes(ctx)
while ctx.pos <= stop do dispatchmap(ctx, ctx.map1) end
if ctx.pos ~= ctx.start then incomplete(ctx) end
end
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
local function create_(code, addr, out)
local ctx = {}
ctx.code = code
ctx.addr = (addr or 0) - 1
ctx.out = out or io.write
ctx.symtab = {}
ctx.disass = disass_block
ctx.hexdump = 16
ctx.x64 = false
ctx.map1 = map_opc1_32
ctx.aregs = map_regs.D
return ctx
end
local function create64_(code, addr, out)
local ctx = create_(code, addr, out)
ctx.x64 = true
ctx.map1 = map_opc1_64
ctx.aregs = map_regs.Q
return ctx
end
-- Simple API: disassemble code (a string) at address and output via out.
local function disass_(code, addr, out)
create_(code, addr, out):disass()
end
local function disass64_(code, addr, out)
create64_(code, addr, out):disass()
end
-- Return register name for RID.
local function regname_(r)
if r < 8 then return map_regs.D[r+1] end
return map_regs.X[r-7]
end
local function regname64_(r)
if r < 16 then return map_regs.Q[r+1] end
return map_regs.X[r-15]
end
-- Public module functions.
module(...)
create = create_
create64 = create64_
disass = disass_
disass64 = disass64_
regname = regname_
regname64 = regname64_

View file

@ -1,16 +0,0 @@
FILES=lunatic.html lunacon.html
# NOTE: the 'source-highlight-args' attribute is not present in the default
# AsciiDoc distribution. It has to be hacked into its
# filters/source/source-highlight-filter.conf like this:
#
# filter="source-highlight -f xhtml -s {language} (...)"
# -->
# filter="source-highlight {source-highlight-args} -f xhtml -s {language} (...)"
%.html: %.txt Makefile lunatic_sh.style
asciidoc -v -a latexmath -a source-highlight-args="--style-file=$(shell pwd)/lunatic_sh.style" $<
all: $(FILES)
clean:
rm -f $(FILES)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

View file

@ -1,14 +0,0 @@
Lunatic depends on the following:
* LuaJIT 2.0.3 or later [http://luajit.org]. It is recommended to get the
latest git HEAD of the 2.0 branch. As of time of writing, LuaJIT 2.1 is
in alpha, and has not been successfully tested with Lunatic.
* LPeg 0.12 [http://www.inf.puc-rio.br/~roberto/lpeg].
For Windows, static libraries and headers have been provided.
See platform/Windows/lib/instructions.txt for information on building.
Build EDuke32 with the GNU Make invocation "make LUNATIC=1".

View file

@ -1,73 +0,0 @@
diff --git a/lpcap.h b/lpcap.h
index c0a0e38..1bfc109 100644
--- a/lpcap.h
+++ b/lpcap.h
@@ -18,7 +18,7 @@ typedef enum CapKind {
typedef struct Capture {
const char *s; /* subject position */
- short idx; /* extra info about capture (group name, arg index, etc.) */
+ int idx; /* extra info about capture (group name, arg index, etc.) */
byte kind; /* kind of capture */
byte siz; /* size of full capture + 1 (0 = not a full capture) */
} Capture;
diff --git a/makefile b/makefile
index 57a18fb..88e8a60 100644
--- a/makefile
+++ b/makefile
@@ -1,5 +1,8 @@
LIBNAME = lpeg
-LUADIR = ../lua/
+# Linux example:
+LUADIR = /usr/local/include/luajit-2.0
+# Windows example (from MSYS bash):
+#LUADIR = /f/g/mod/luajit-2.0/src
COPT = -O2
# COPT = -DLPEG_DEBUG -g
@@ -19,10 +22,21 @@ CWARNS = -Wall -Wextra -pedantic \
-Wmissing-prototypes \
-Wnested-externs \
-Wstrict-prototypes \
+ -Werror-implicit-function-declaration \
# -Wunreachable-code \
+# Example:
+# --------
+# 1. Build shared lib (for LunaCON standalone)
+# $ make linux
+# 2. Remove shared-lib *.o
+# $ rm *.o
+# 3. Build static lib (for EDuke32/Lunatic)
+# $ PIC='' make liblpeg.a
-CFLAGS = $(CWARNS) $(COPT) -std=c99 -I$(LUADIR) -fPIC
+PIC ?= -fPIC
+
+CFLAGS = $(CWARNS) $(COPT) -std=c99 -I$(LUADIR) $(PIC)
CC = gcc
FILES = lpvm.o lpcap.o lptree.o lpcode.o lpprint.o
@@ -38,13 +52,16 @@ macosx:
lpeg.so: $(FILES)
env $(CC) $(DLLFLAGS) $(FILES) -o lpeg.so
+liblpeg.a: $(FILES)
+ $(AR) rc $@ $^
+
$(FILES): makefile
test: test.lua re.lua lpeg.so
./test.lua
clean:
- rm -f $(FILES) lpeg.so
+ rm -f $(FILES) lpeg.so liblpeg.a
lpcap.o: lpcap.c lpcap.h lptypes.h
@@ -52,4 +69,3 @@ lpcode.o: lpcode.c lptypes.h lpcode.h lptree.h lpvm.h lpcap.h
lpprint.o: lpprint.c lptypes.h lpprint.h lptree.h lpvm.h lpcap.h
lptree.o: lptree.c lptypes.h lpcap.h lpcode.h lptree.h lpvm.h lpprint.h
lpvm.o: lpvm.c lpcap.h lptypes.h lpvm.h lpprint.h lptree.h
-

View file

@ -1,366 +0,0 @@
LunaCON User Manual -- The ``Lunatic Translator''
=================================================
Helixhorned <contact: Duke4.net forums>
:max-width: 56em
:numbered:
:icons:
:toc:
:conf-files: lunatic.conf
Introduction
------------
The Lunatic build of EDuke32 completely reimplements the CON scripting language
using the framework provided by its Lua interface. It does so by generating at
start-up time one Lunatic module from all specified CON files and
mutators.footnote:[In CON, the *`include`* directive amounts to merely textual
inclusion, as is mutator appending with the `-mx` switch. All CON code is
translated in one global context sharing the same namespace.]
LunaCON implements nearly all of modern EDuke32 CON. One of its main aims is
correctness in a broad sense, which encompasses multiple issues.
* LunaCON more strictly validates the CON source at translation time. For
example, using labels of mismatched type, such as a ++define++d number where
a `move` is expected, issues an translation error.
* Lexically and syntactically, LunaCON accepts only a subset of what the C-CON
translator would pass through. This includes rejecting clearly erroneous code
such as an unfinished directive at the end of a translation unit, but also
code that implies ambiguities in the language.
* On the run-time side, most checking is done by Lunatic. For example, indexing
actor gamevars with out-of-bounds values produces an error that gets
permanently displayed on the screen. Like with Lua code, the
link:lunatic.html#error[error] entry in the log then contains a traceback
annotated with line numbers, making it possible for the CON coder to pinpoint
its location and context.
For these reasons, many existing CON mods and TCs are expected to need
amendments in order to translate and/or run properly. This is generally a
desired thing, since otherwise (most of the time, unintentional) misuse of the
CON system may produce behavior that is either erratic, or appears to work on
the surface but is hiding potential issues.
.A warning
The code generated by LunaCON is unsuitable for human consumption --
specifically, it should not be used as a base for new Lunatic code. Even though
that code runs in Lunatic's protected user environment, it uses private
interfaces that are subject to change in addition to the officially exposed and
documented ones.
Nevertheless, LunaCON is also available as a stand-alone Lua script. Primarily,
this allows it to be used as a checking tool during CON development without the
need to start EDuke32. Also, multiple CON codebases can be checked in a batch
fashion for non-runtime problems.
Usage
-----
:LuaJIT: http://luajit.org
:LPeg: http://www.inf.puc-rio.br/~roberto/lpeg/
The stand-alone LunaCON script, `lunacon.lua`, needs {LuaJIT}[LuaJIT] for
execution and {LPeg}[LPeg] as additional dependency.footnote:[In order to
translate some very large CON files, minor modifications have to be made to
LuaJIT and LPeg. Refer to `lunatic/doc/how_to_build_lunatic.txt` in the EDuke32
source distribution for details.] The script also requires `con_lang.lua` to be
present. It expects one or more names of root CON files together with any
number of options, in any order (arguments starting with a dash are always
interpreted as the latter).
.Example usage
----------
luajit ./lunacon.lua -Wall mymod.con
----------
All arguments after a single `@` argument are taken to name _file lists_.
These are files containing lines either
* a file name of a root CON file which gets processed, or
* a completely blank line or a line starting with `#`, both of which are ignored.
Options
-------
Most options documented in the following can also be passed to the Lunatic
build of EDuke32. Options on the command line are processed in their given
order, from left to right. Many of them can be negated by appeding `no-` after
the ``option category letter'', for example `-Wno-not-redefined`. This index
only lists the positive forms of each non-compound option and labels whether it
is enabled or disabled by default.
General options
~~~~~~~~~~~~~~~
++-I__directory__++ (stand-alone only)::
Specifies a _default directory_ to search for CON files as a last resort. This
can be useful if mods expect part of their included files to reside inside GRP
or ZIP containers, which the stand-alone translator cannot examine. This option
can only be passed once.
Warning options
~~~~~~~~~~~~~~~
These options affect on which occasions warnings, or in some cases, errors
instead of warnings, are produced at translation time.
`-Wall`::
Enables all warning and errors described in this subsection.
`-Wbad-identifier` (default: off)::
Warns whenever an identifier does not match `[A-Za-z_][A-Za-z0-9_]*`. In words,
a ``good'' identifier is expected to start with a letter or an underscore,
followed by zero or more letters, underscores, or digits.
`-Wchained-loadactor` (default: on)::
Warns whenever an `eventloadactor` block appears multiple times for the same
tile number. In LunaCON, these are translated to an `EVENT_LOADACTOR` block
that checks the tile number of its current actor. This event gets chained to
the end of all preceding `EVENT_LOADACTOR` definitions, whereas with C-CON, a
new `eventloadactor` block for the same tile number would override the existing
one.
`-Werror-bad-getactorvar` (default: off)::
When enabled, produces an error whenever a global or per-player gamevar is
attempted to be read using ++getactorvar++. Otherwise, a warning is emitted. In
this case, the generated code is either a read of the (global) gamevar, or an
access of the per-player gamevar with the given index or the current player
index (depending on `-fbad-getactorvar-use-pli`), which is probably not what
the coder intended.
`-Wnot-redefined` (default: on)::
Warns whenever a `define` directive was ignored because it attempted to
redefine an already existing label to a different number. The label can exist
either due to a previous `define`, or because it is a predefined label such as
`NO`.
`-Wnumber-conversion` (default: on)::
Warns whenever a literal decimal number is encountered that is out of the range
for a 32-bit integer, but inside that of an unsigned 32-bit integer. In this
case, 2^32^ is subtracted from the number, producing a negative value without
changing the bit representation.
`-Wnever-used-gamevar` (default: off)::
After translation, issues a warning for every CON-side user gamevar definition
that was never referenced, that is, neither read nor written in the CON code.
`-Wnever-read-gamevar` (default: off)::
After translation, issues a warning for every CON-side user gamevar definition
that was assigned to but never read in the CON code.
`-Wsystem-gamevar` (default: on)::
Warns whenever the initial value of a system gamevar was overridden (by issuing
`gamevar` at file scope), but the provided gamevar flags did not match those of
the kept predefined ones.
Code generation options
~~~~~~~~~~~~~~~~~~~~~~~
These options change the way certain CON code constructs are translated to Lua,
set the output behavior of the stand-alone translator, or toggle various error
conditions.
`-fno` (stand-alone only)::
Disable printing out the generated code and validating its syntax.
`-fno=onlycheck` (stand-alone only)::
Disable printing out the generated code, but validate whether it is
syntactically legal Lua code. A failure of this check represents a bug with
LunaCON itself.
`-fnames` (stand-alone only)::
Instead of generating Lua code, output all ++define++d names that were ever
passed as tile number to `actor`, `useractor` or `eventloadactor` in a form
suitable for the `names.h` file read by Mapster32.
`-ferror-nostate` (default: on)::
If enabled, an attempt to call a `state` that was not previously defined
results in an error. Otherwise, a warning is issued and no code is generated
for the `state` invocation.
`-ferror-nonlocal-userdef` (default: on)::
If enabled, an attept to issue `getuserdef` or `setuserdef` when the current
player doesn't equal the local player generates an error. Otherwise, the
userdef structure can be accessed irrespective of the current player.
`-ferror-negative-tag-write` (default: off)::
If enabled, an attempt to assign a negative value to the `lotag` or `hitag`
member of the sector, wall or (t)sprite structures produces an error.
Normally, it is legal to assign negative values to these members, but since
they are unsigned 16-bit integers on the C side, such values will be converted
to positive ones and may entail undesired behavior.
+
NOTE: From CON as well as Lunatic, `hitag` and `lotag` are seen as *signed*
16-bit integers.
`-fbad-getactorvar-use-pli` (default: on)::
If enabled and `-Werror-bad-getactorvar` is off, a `getactorvar` of a
per-player variable will result the gamevar being indexed with the current
player instead of the provided index. This is the (probably unintended)
behavior of C-CON.
`-fplayervar` (default: on)::
If enabled, per-player `gamevar` definitions really generate `con.playervar`
initializations in the translated Lua code. Otherwise, per-player gamevars are
treated as global gamevars, which can be useful for code that attempts to
access them in contexts with no current player, yielding errors in Lunatic.
`-ftrapv` (default: off)::
Enable _trapping_ behavior for arithmetic operations whose result overflows the
range of a signed 32-bit integer, i.e. generate an error on overflow. By
default, overflow results in undefined behavior. Currently, only multiplication
is handled.
`-fwrapv` (default: off)::
Enable _wrapping_ behavior for arithmetic operations whose result overflows the
range of a signed 32-bit integer, i.e. after each operation, only the 32 lower
bits of the result are kept. Currently, only multiplication is handled. Only
one of `-ftrapv` or `-fwrapv` may be enabled at the same time.
Differences from C-CON
----------------------
Despite the aim to provide as much compatibility to CON as possible where
reasonable, a couple of its ``features'' -- some of which are coincidental --
cannot be implemented without unnaturally bending the implementation into
shape. On the other hand, LunaCON sports some features that C-CON lacks, and
does not exhibit some of its strange quirks or outright bugs.
Syntactic and lexical changes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The two command classes
^^^^^^^^^^^^^^^^^^^^^^^
LunaCON makes a clear distinction between ``outer'' commands that have an
effect when translating a CON file (_directives_ such as `gamevar`,
`definesound` or `spritenvg`) and ``inner'' commands meant to be effective at
execution time of `actor`/`useractor`, `state`, `event` and `eventloadactor`
blocks. Thus, issuing directives inside of these will make LunaCON reject the
input file due to a syntax error, as will an attempt to use run-time commands
such as `sizeat` at file scope.
This strict behavior is one hand a consequence of how the LunaCON parser is
structured, but on the other hand it may expose code for which the author
misunderstood its meaning. An example for the first case would be a `gamevar`
inside a block, which one mistakenly could take to have local scope. Gamevars
in CON always have both global scope and lifetime though, so such a mental
model on the coder's part may lead to unexpected bugs. In the second case,
run-time commands at file scope are really translated to bytecode by C-CON, but
as they reside outside of any block, they are never reached -- in other words,
they are dead code.
Currently, the only exception to this rule is that a `definequote` is allowed
inside delimited blocks, which however does not change its semantics in any
way: it still only defines the initial contents of a quote, and does not
magically act like `redefinequote`.
Ambiguous lexical elements
^^^^^^^^^^^^^^^^^^^^^^^^^^
LunaCON is fairly relaxed as to which character sequences constitute valid
identifier names used for e.g. ++define++d labels or variables. It does so out
of the necessity of supporting CON code found ``in the wild''. An identifier
* must not be a token denoting a number,
* must start with an _allowed first character_,
* may contain any number of _allowed following characters_,
where
* allowed first characters are: letters, digits, and those in ```_*?`''
* allowed following characters are the same as allowed first characters, plus
``+++++'', ``++-++'' and ``++.++''.
// ^ `+` and `-`
Numbers can be written in either decimal or hexadecimal form, optionally
prepended by a ``++-++'' sign. In the decimal case, the modulus of the number
can be (lexically) any sequence of decimal digits, though there are
restrictions on the permitted values.footnote:[Specifically, it is forbidden to
write a number whose value falls outside the range [--2^31^-1 .. 2^32^--1\].]
Hexadecimal number literals must be prefixed with `0x` or `0X`, and may
optionally be suffixed with an `h`.
The following constructions are not allowed, as they would create ambiguities
with the definitions above:
* A sequence of digits followed by letters to mean the the digits interpreted
as a number, ignoring the trailing letters, e.g. `1267AT`.
* A hexadecimal constant denoted using only a trailing `h`, for example
`00000000h`.
Miscellaneous
^^^^^^^^^^^^^
* Read array expressions as well as `switch` statements can be arbitrarily
nested.
// XXX: 'Read' is confusing. Need better wording.
Run-time changes
~~~~~~~~~~~~~~~~
Behavior on error
^^^^^^^^^^^^^^^^^
As LunaCON is implemented by translating all given CON code to a Lunatic
module, it is the Lunatic runtime that checks for proper use of its services at
execution time and takes care of error handling and reporting. In Lua, an error
link:lunatic.html#nlcf[transfers control] to the end of the innermost
``protected'' call of a Lua chunk.
This is in contrast to C-CON, which for some errors would print a message to
the log, but otherwise would continue execution as if nothing had happened. In
LunaCON, the code following an error is *not* executed. This way, the author of
the CON code is notified of the presence of the bug, and by fixing it
eventually obtains cleaner code.
Quote behavior
^^^^^^^^^^^^^^
* The `redefinequote` command can be issued even if the given quote number has
not been previously allocated using the `definequote` directive. This makes
it possible to use a range of quote numbers on the fly without the need to
declare their future use.
Miscellaneous
^^^^^^^^^^^^^
:r5097: http://svn.eduke32.com/listing.php?repname=eduke32&rev=5097
* Issuing `break` inside a part of an event chain (defined using multiple
`onevent` blocks for one event kind) does not abort the whole chain. (Since
EDuke32 {r5097}[r5097], this is the behavior of C-CON as well.)
Unavailable functionality
~~~~~~~~~~~~~~~~~~~~~~~~~
C-CON's `jump` and `getcurraddress` are not available and will not be
implemented.
The following commands are not yet implemented. Those highlighted in bold give
errors, while the others merely warn either at translation or execution
time.
Directives
^^^^^^^^^^
*`includedefault`*, `definecheat`, `setcfgname`, `setgamename`
Run-time commands
^^^^^^^^^^^^^^^^^
*`activatecheat`*, *`clearmapstate`*, *`startcutscene`*, *`ifcutscene`*, `save`,
`savenn`, *`lineintersect`*, *`rayintersect`*, *`sectorofwall`*.
Additionally, various multiplayer-related commands either unconditionally
return results as if no multiplayer game is in progress, or are non-functional
for more than the first player. Also, some MP-related variables are merely
predefined constants.

View file

@ -1,3 +0,0 @@
[replacements]
# Make -- an en dash for good looking minus signs.
--=&#8211;

File diff suppressed because it is too large Load diff

View file

@ -1,10 +0,0 @@
// Custom style file for GNU Source-highlight, derived from its default.style
bgcolor "white"; // the background color for documents
context gray; // the color for context lines (when specified with line ranges)
keyword darkblue b; // for language keywords
string "#116611" f; // for strings and chars
specialchar pink f; // for special chars, e.g., \n, \t, \\
comment "#444444" i, noref; // for comments
cbracket red; // for block brackets (e.g. {, })

View file

@ -1,699 +0,0 @@
----------------------------------------------------------------------------
-- LuaJIT compiler dump module.
--
-- Copyright (C) 2005-2015 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
--
-- This module can be used to debug the JIT compiler itself. It dumps the
-- code representations and structures used in various compiler stages.
--
-- Example usage:
--
-- luajit -jdump -e "local x=0; for i=1,1e6 do x=x+i end; print(x)"
-- luajit -jdump=im -e "for i=1,1000 do for j=1,1000 do end end" | less -R
-- luajit -jdump=is myapp.lua | less -R
-- luajit -jdump=-b myapp.lua
-- luajit -jdump=+aH,myapp.html myapp.lua
-- luajit -jdump=ixT,myapp.dump myapp.lua
--
-- The first argument specifies the dump mode. The second argument gives
-- the output file name. Default output is to stdout, unless the environment
-- variable LUAJIT_DUMPFILE is set. The file is overwritten every time the
-- module is started.
--
-- Different features can be turned on or off with the dump mode. If the
-- mode starts with a '+', the following features are added to the default
-- set of features; a '-' removes them. Otherwise the features are replaced.
--
-- The following dump features are available (* marks the default):
--
-- * t Print a line for each started, ended or aborted trace (see also -jv).
-- * b Dump the traced bytecode.
-- * i Dump the IR (intermediate representation).
-- r Augment the IR with register/stack slots.
-- s Dump the snapshot map.
-- * m Dump the generated machine code.
-- x Print each taken trace exit.
-- X Print each taken trace exit and the contents of all registers.
-- a Print the IR of aborted traces, too.
--
-- The output format can be set with the following characters:
--
-- T Plain text output.
-- A ANSI-colored text output
-- H Colorized HTML + CSS output.
--
-- The default output format is plain text. It's set to ANSI-colored text
-- if the COLORTERM variable is set. Note: this is independent of any output
-- redirection, which is actually considered a feature.
--
-- You probably want to use less -R to enjoy viewing ANSI-colored text from
-- a pipe or a file. Add this to your ~/.bashrc: export LESS="-R"
--
------------------------------------------------------------------------------
-- Cache some library functions and objects.
local jit = require("jit")
assert(jit.version_num == 20003, "LuaJIT core/library version mismatch")
local jutil = require("jit.util")
local vmdef = require("jit.vmdef")
local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc
local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek
local tracemc, tracesnap = jutil.tracemc, jutil.tracesnap
local traceexitstub, ircalladdr = jutil.traceexitstub, jutil.ircalladdr
local bit = require("bit")
local band, shl, shr = bit.band, bit.lshift, bit.rshift
local sub, gsub, format = string.sub, string.gsub, string.format
local byte, char, rep = string.byte, string.char, string.rep
local type, tostring = type, tostring
local stdout, stderr = io.stdout, io.stderr
-- Load other modules on-demand.
local bcline, disass
-- Active flag, output file handle and dump mode.
local active, out, dumpmode
------------------------------------------------------------------------------
local symtabmt = { __index = false }
local symtab = {}
local nexitsym = 0
-- Fill nested symbol table with per-trace exit stub addresses.
local function fillsymtab_tr(tr, nexit)
local t = {}
symtabmt.__index = t
if jit.arch == "mips" or jit.arch == "mipsel" then
t[traceexitstub(tr, 0)] = "exit"
return
end
for i=0,nexit-1 do
local addr = traceexitstub(tr, i)
t[addr] = tostring(i)
end
local addr = traceexitstub(tr, nexit)
if addr then t[addr] = "stack_check" end
end
-- Fill symbol table with trace exit stub addresses.
local function fillsymtab(tr, nexit)
local t = symtab
if nexitsym == 0 then
local ircall = vmdef.ircall
for i=0,#ircall do
local addr = ircalladdr(i)
if addr ~= 0 then t[addr] = ircall[i] end
end
end
if nexitsym == 1000000 then -- Per-trace exit stubs.
fillsymtab_tr(tr, nexit)
elseif nexit > nexitsym then -- Shared exit stubs.
for i=nexitsym,nexit-1 do
local addr = traceexitstub(i)
if addr == nil then -- Fall back to per-trace exit stubs.
fillsymtab_tr(tr, nexit)
setmetatable(symtab, symtabmt)
nexit = 1000000
break
end
t[addr] = tostring(i)
end
nexitsym = nexit
end
return t
end
local function dumpwrite(s)
out:write(s)
end
-- Disassemble machine code.
local function dump_mcode(tr)
local info = traceinfo(tr)
if not info then return end
local mcode, addr, loop = tracemc(tr)
if not mcode then return end
if not disass then disass = require("jit.dis_"..jit.arch) end
out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
local ctx = disass.create(mcode, addr, dumpwrite)
ctx.hexdump = 0
ctx.symtab = fillsymtab(tr, info.nexit)
if loop ~= 0 then
symtab[addr+loop] = "LOOP"
ctx:disass(0, loop)
out:write("->LOOP:\n")
ctx:disass(loop, #mcode-loop)
symtab[addr+loop] = nil
else
ctx:disass(0, #mcode)
end
end
------------------------------------------------------------------------------
local irtype_text = {
[0] = "nil",
"fal",
"tru",
"lud",
"str",
"p32",
"thr",
"pro",
"fun",
"p64",
"cdt",
"tab",
"udt",
"flt",
"num",
"i8 ",
"u8 ",
"i16",
"u16",
"int",
"u32",
"i64",
"u64",
"sfp",
}
local colortype_ansi = {
[0] = "%s",
"%s",
"%s",
"\027[36m%s\027[m",
"\027[32m%s\027[m",
"%s",
"\027[1m%s\027[m",
"%s",
"\027[1m%s\027[m",
"%s",
"\027[33m%s\027[m",
"\027[31m%s\027[m",
"\027[36m%s\027[m",
"\027[34m%s\027[m",
"\027[34m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
}
local function colorize_text(s, t)
return s
end
local function colorize_ansi(s, t)
return format(colortype_ansi[t], s)
end
local irtype_ansi = setmetatable({},
{ __index = function(tab, t)
local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
local html_escape = { ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;", }
local function colorize_html(s, t)
s = gsub(s, "[<>&]", html_escape)
return format('<span class="irt_%s">%s</span>', irtype_text[t], s)
end
local irtype_html = setmetatable({},
{ __index = function(tab, t)
local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
local header_html = [[
<style type="text/css">
background { background: #ffffff; color: #000000; }
pre.ljdump {
font-size: 10pt;
background: #f0f4ff;
color: #000000;
border: 1px solid #bfcfff;
padding: 0.5em;
margin-left: 2em;
margin-right: 2em;
}
span.irt_str { color: #00a000; }
span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; }
span.irt_tab { color: #c00000; }
span.irt_udt, span.irt_lud { color: #00c0c0; }
span.irt_num { color: #4040c0; }
span.irt_int, span.irt_i8, span.irt_u8, span.irt_i16, span.irt_u16 { color: #b040b0; }
</style>
]]
local colorize, irtype
-- Lookup tables to convert some literals into names.
local litname = {
["SLOAD "] = setmetatable({}, { __index = function(t, mode)
local s = ""
if band(mode, 1) ~= 0 then s = s.."P" end
if band(mode, 2) ~= 0 then s = s.."F" end
if band(mode, 4) ~= 0 then s = s.."T" end
if band(mode, 8) ~= 0 then s = s.."C" end
if band(mode, 16) ~= 0 then s = s.."R" end
if band(mode, 32) ~= 0 then s = s.."I" end
t[mode] = s
return s
end}),
["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", },
["CONV "] = setmetatable({}, { __index = function(t, mode)
local s = irtype[band(mode, 31)]
s = irtype[band(shr(mode, 5), 31)].."."..s
if band(mode, 0x400) ~= 0 then s = s.." trunc"
elseif band(mode, 0x800) ~= 0 then s = s.." sext" end
local c = shr(mode, 14)
if c == 2 then s = s.." index" elseif c == 3 then s = s.." check" end
t[mode] = s
return s
end}),
["FLOAD "] = vmdef.irfield,
["FREF "] = vmdef.irfield,
["FPMATH"] = vmdef.irfpm,
}
local function ctlsub(c)
if c == "\n" then return "\\n"
elseif c == "\r" then return "\\r"
elseif c == "\t" then return "\\t"
else return format("\\%03d", byte(c))
end
end
local function fmtfunc(func, pc)
local fi = funcinfo(func, pc)
if fi.loc then
return fi.loc
elseif fi.ffid then
return vmdef.ffnames[fi.ffid]
elseif fi.addr then
return format("C:%x", fi.addr)
else
return "(?)"
end
end
local function formatk(tr, idx)
local k, t, slot = tracek(tr, idx)
local tn = type(k)
local s
if tn == "number" then
if k == 2^52+2^51 then
s = "bias"
else
s = format("%+.14g", k)
end
elseif tn == "string" then
s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub))
elseif tn == "function" then
s = fmtfunc(k)
elseif tn == "table" then
s = format("{%p}", k)
elseif tn == "userdata" then
if t == 12 then
s = format("userdata:%p", k)
else
s = format("[%p]", k)
if s == "[0x00000000]" then s = "NULL" end
end
elseif t == 21 then -- int64_t
s = sub(tostring(k), 1, -3)
if sub(s, 1, 1) ~= "-" then s = "+"..s end
else
s = tostring(k) -- For primitives.
end
s = colorize(format("%-4s", s), t)
if slot then
s = format("%s @%d", s, slot)
end
return s
end
local function printsnap(tr, snap)
local n = 2
for s=0,snap[1]-1 do
local sn = snap[n]
if shr(sn, 24) == s then
n = n + 1
local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS
if ref < 0 then
out:write(formatk(tr, ref))
elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM
out:write(colorize(format("%04d/%04d", ref, ref+1), 14))
else
local m, ot, op1, op2 = traceir(tr, ref)
out:write(colorize(format("%04d", ref), band(ot, 31)))
end
out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME
else
out:write("---- ")
end
end
out:write("]\n")
end
-- Dump snapshots (not interleaved with IR).
local function dump_snap(tr)
out:write("---- TRACE ", tr, " snapshots\n")
for i=0,1000000000 do
local snap = tracesnap(tr, i)
if not snap then break end
out:write(format("#%-3d %04d [ ", i, snap[0]))
printsnap(tr, snap)
end
end
-- Return a register name or stack slot for a rid/sp location.
local function ridsp_name(ridsp, ins)
if not disass then disass = require("jit.dis_"..jit.arch) end
local rid, slot = band(ridsp, 0xff), shr(ridsp, 8)
if rid == 253 or rid == 254 then
return (slot == 0 or slot == 255) and " {sink" or format(" {%04d", ins-slot)
end
if ridsp > 255 then return format("[%x]", slot*4) end
if rid < 128 then return disass.regname(rid) end
return ""
end
-- Dump CALL* function ref and return optional ctype.
local function dumpcallfunc(tr, ins)
local ctype
if ins > 0 then
local m, ot, op1, op2 = traceir(tr, ins)
if band(ot, 31) == 0 then -- nil type means CARG(func, ctype).
ins = op1
ctype = formatk(tr, op2)
end
end
if ins < 0 then
out:write(format("[0x%x](", tonumber((tracek(tr, ins)))))
else
out:write(format("%04d (", ins))
end
return ctype
end
-- Recursively gather CALL* args and dump them.
local function dumpcallargs(tr, ins)
if ins < 0 then
out:write(formatk(tr, ins))
else
local m, ot, op1, op2 = traceir(tr, ins)
local oidx = 6*shr(ot, 8)
local op = sub(vmdef.irnames, oidx+1, oidx+6)
if op == "CARG " then
dumpcallargs(tr, op1)
if op2 < 0 then
out:write(" ", formatk(tr, op2))
else
out:write(" ", format("%04d", op2))
end
else
out:write(format("%04d", ins))
end
end
end
-- Dump IR and interleaved snapshots.
local function dump_ir(tr, dumpsnap, dumpreg)
local info = traceinfo(tr)
if not info then return end
local nins = info.nins
out:write("---- TRACE ", tr, " IR\n")
local irnames = vmdef.irnames
local snapref = 65536
local snap, snapno
if dumpsnap then
snap = tracesnap(tr, 0)
snapref = snap[0]
snapno = 0
end
for ins=1,nins do
if ins >= snapref then
if dumpreg then
out:write(format(".... SNAP #%-3d [ ", snapno))
else
out:write(format(".... SNAP #%-3d [ ", snapno))
end
printsnap(tr, snap)
snapno = snapno + 1
snap = tracesnap(tr, snapno)
snapref = snap and snap[0] or 65536
end
local m, ot, op1, op2, ridsp = traceir(tr, ins)
local oidx, t = 6*shr(ot, 8), band(ot, 31)
local op = sub(irnames, oidx+1, oidx+6)
if op == "LOOP " then
if dumpreg then
out:write(format("%04d ------------ LOOP ------------\n", ins))
else
out:write(format("%04d ------ LOOP ------------\n", ins))
end
elseif op ~= "NOP " and op ~= "CARG " and
(dumpreg or op ~= "RENAME") then
local rid = band(ridsp, 255)
if dumpreg then
out:write(format("%04d %-6s", ins, ridsp_name(ridsp, ins)))
else
out:write(format("%04d ", ins))
end
out:write(format("%s%s %s %s ",
(rid == 254 or rid == 253) and "}" or
(band(ot, 128) == 0 and " " or ">"),
band(ot, 64) == 0 and " " or "+",
irtype[t], op))
local m1, m2 = band(m, 3), band(m, 3*4)
if sub(op, 1, 4) == "CALL" then
local ctype
if m2 == 1*4 then -- op2 == IRMlit
out:write(format("%-10s (", vmdef.ircall[op2]))
else
ctype = dumpcallfunc(tr, op2)
end
if op1 ~= -1 then dumpcallargs(tr, op1) end
out:write(")")
if ctype then out:write(" ctype ", ctype) end
elseif op == "CNEW " and op2 == -1 then
out:write(formatk(tr, op1))
elseif m1 ~= 3 then -- op1 != IRMnone
if op1 < 0 then
out:write(formatk(tr, op1))
else
out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
end
if m2 ~= 3*4 then -- op2 != IRMnone
if m2 == 1*4 then -- op2 == IRMlit
local litn = litname[op]
if litn and litn[op2] then
out:write(" ", litn[op2])
elseif op == "UREFO " or op == "UREFC " then
out:write(format(" #%-3d", shr(op2, 8)))
else
out:write(format(" #%-3d", op2))
end
elseif op2 < 0 then
out:write(" ", formatk(tr, op2))
else
out:write(format(" %04d", op2))
end
end
end
out:write("\n")
end
end
if snap then
if dumpreg then
out:write(format(".... SNAP #%-3d [ ", snapno))
else
out:write(format(".... SNAP #%-3d [ ", snapno))
end
printsnap(tr, snap)
end
end
------------------------------------------------------------------------------
local recprefix = ""
local recdepth = 0
-- Format trace error message.
local function fmterr(err, info)
if type(err) == "number" then
if type(info) == "function" then info = fmtfunc(info) end
err = format(vmdef.traceerr[err], info)
end
return err
end
-- Dump trace states.
local function dump_trace(what, tr, func, pc, otr, oex)
if what == "stop" or (what == "abort" and dumpmode.a) then
if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
elseif dumpmode.s then dump_snap(tr) end
if dumpmode.m then dump_mcode(tr) end
end
if what == "start" then
if dumpmode.H then out:write('<pre class="ljdump">\n') end
out:write("---- TRACE ", tr, " ", what)
if otr then out:write(" ", otr, "/", oex) end
out:write(" ", fmtfunc(func, pc), "\n")
elseif what == "stop" or what == "abort" then
out:write("---- TRACE ", tr, " ", what)
if what == "abort" then
out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
else
local info = traceinfo(tr)
local link, ltype = info.link, info.linktype
if link == tr or link == 0 then
out:write(" -> ", ltype, "\n")
elseif ltype == "root" then
out:write(" -> ", link, "\n")
else
out:write(" -> ", link, " ", ltype, "\n")
end
end
if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
else
out:write("---- TRACE ", what, "\n\n")
end
out:flush()
end
-- Dump recorded bytecode.
local function dump_record(tr, func, pc, depth, callee)
if depth ~= recdepth then
recdepth = depth
recprefix = rep(" .", depth)
end
local line
if pc >= 0 then
line = bcline(func, pc, recprefix)
if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
else
line = "0000 "..recprefix.." FUNCC \n"
callee = func
end
if pc <= 0 then
out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n")
else
out:write(line)
end
if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
end
end
------------------------------------------------------------------------------
-- Dump taken trace exits.
local function dump_texit(tr, ex, ngpr, nfpr, ...)
out:write("---- TRACE ", tr, " exit ", ex, "\n")
if dumpmode.X then
local regs = {...}
if jit.arch == "x64" then
for i=1,ngpr do
out:write(format(" %016x", regs[i]))
if i % 4 == 0 then out:write("\n") end
end
else
for i=1,ngpr do
out:write(format(" %08x", regs[i]))
if i % 8 == 0 then out:write("\n") end
end
end
if jit.arch == "mips" or jit.arch == "mipsel" then
for i=1,nfpr,2 do
out:write(format(" %+17.14g", regs[ngpr+i]))
if i % 8 == 7 then out:write("\n") end
end
else
for i=1,nfpr do
out:write(format(" %+17.14g", regs[ngpr+i]))
if i % 4 == 0 then out:write("\n") end
end
end
end
end
------------------------------------------------------------------------------
-- Detach dump handlers.
local function dumpoff()
if active then
active = false
jit.attach(dump_texit)
jit.attach(dump_record)
jit.attach(dump_trace)
if out and out ~= stdout and out ~= stderr then out:close() end
out = nil
end
end
-- Open the output file and attach dump handlers.
local function dumpon(opt, outfile)
if active then dumpoff() end
local colormode = os.getenv("COLORTERM") and "A" or "T"
if opt then
opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
end
local m = { t=true, b=true, i=true, m=true, }
if opt and opt ~= "" then
local o = sub(opt, 1, 1)
if o ~= "+" and o ~= "-" then m = {} end
for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
end
dumpmode = m
if m.t or m.b or m.i or m.s or m.m then
jit.attach(dump_trace, "trace")
end
if m.b then
jit.attach(dump_record, "record")
if not bcline then bcline = require("jit.bc").line end
end
if m.x or m.X then
jit.attach(dump_texit, "texit")
end
if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
if outfile then
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
else
out = stdout
end
m[colormode] = true
if colormode == "A" then
colorize = colorize_ansi
irtype = irtype_ansi
elseif colormode == "H" then
colorize = colorize_html
irtype = irtype_html
out:write(header_html)
else
colorize = colorize_text
irtype = irtype_text
end
active = true
end
-- Public module functions.
module(...)
on = dumpon
off = dumpoff
start = dumpon -- For -j command line option.

View file

@ -1,118 +0,0 @@
{
engine_main_arrays_are_static;
engine_v8;
saveboard_maptext;
loadboard_maptext;
sector;
wall;
sprite;
tsprite;
spriteext;
Numsprites;
numsectors;
numwalls;
numyaxbunches;
spritesortcnt;
guniqhudid;
qsetmode;
rendmode;
totalclock;
randomseed;
xdim;
ydim;
windowxy1;
windowxy2;
yxaspect;
viewingrange;
sectorofwall_noquick;
getceilzofslopeptr;
getflorzofslopeptr;
getzsofslopeptr;
spriteheightofsptr;
changespritesect;
changespritestat;
headspritesect;
headspritestat;
prevspritesect;
prevspritestat;
nextspritesect;
nextspritestat;
tilesiz;
picanm;
show2dsector;
headsectbunch;
nextsectbunch;
krand;
ksqrt;
inside;
getangle;
Mulscale;
cansee;
hitscan;
dragpoint;
neartag;
getzrange;
clipmovex;
rotatesprite_;
setaspect;
getclosestcol_lim;
palookup;
palette;
getblendtab;
setblendtab;
setpalookup;
kopen4load;
kfilelength;
kclose;
kread;
klseek;
dist;
ldist;
updatesector;
updatesectorz;
updatesectorbreadth;
getticks;
gethiticks;
OSD_Printf;
Bcrc32;
luaJIT_BC__defs_editor;
luaJIT_BC_defs_common;
luaJIT_BC_engine_maptext;
luaJIT_BC_engine;
luaJIT_BC_bcarray;
luaJIT_BC_bcheck;
luaJIT_BC_bitar;
luaJIT_BC_xmath;
luaJIT_BC_v;
luaJIT_BC_dump;
luaJIT_BC_dis_x86;
luaJIT_BC_dis_x64;
g_argv;
_getnumber16;
getstring_simple;
listsearchpath;
LM_Register;
LM_Clear;
basepaltable;
};

View file

@ -1,252 +0,0 @@
{
engine_main_arrays_are_static;
engine_v8;
saveboard_maptext;
loadboard_maptext;
sector;
wall;
sprite;
tsprite;
spriteext;
Numsprites;
numsectors;
numwalls;
numyaxbunches;
spritesortcnt;
guniqhudid;
qsetmode;
rendmode;
totalclock;
randomseed;
xdim;
ydim;
windowxy1;
windowxy2;
yxaspect;
viewingrange;
sectorofwall_noquick;
getceilzofslopeptr;
getflorzofslopeptr;
getzsofslopeptr;
spriteheightofsptr;
changespritesect;
changespritestat;
headspritesect;
headspritestat;
prevspritesect;
prevspritestat;
nextspritesect;
nextspritestat;
tilesiz;
picanm;
show2dsector;
headsectbunch;
nextsectbunch;
krand;
ksqrt;
inside;
getangle;
Mulscale;
cansee;
hitscan;
dragpoint;
neartag;
getzrange;
clipmovex;
rotatesprite_;
setaspect;
getclosestcol_lim;
palookup;
palette;
getblendtab;
setblendtab;
setpalookup;
kopen4load;
kfilelength;
kclose;
kread;
klseek;
pathsearchmode;
klistfree;
klistpath;
dist;
ldist;
updatesector;
updatesectorz;
updatesectorbreadth;
getticks;
gethiticks;
OSD_Printf;
Bcrc32;
luaJIT_BC_defs_common;
luaJIT_BC_engine_maptext;
luaJIT_BC_engine;
g_argv;
g_elCONSize;
g_elCON;
El_SetCON;
El_OnError;
basepaltable;
g_elSavecode;
El_FreeSaveCode;
El_SerializeGamevars;
El_RestoreGamevars;
El_GetLabelValue;
s_buildRev;
g_sizes_of_what;
g_sizes_of;
g_elFirstTime;
g_elCallDepth;
block_deletesprite;
myconnectindex;
g_RETURN;
g_elModules;
g_modDir;
g_elSessionVar;
g_mapInfo;
g_volumeNames;
kopen4loadfrommod;
g_scriptModules;
g_scriptModulesNum;
G_ConFile;
G_DoGameStartup;
C_DefineSound;
C_DefineMusic;
C_DefineQuote;
C_DefineVolumeName;
C_DefineVolumeFlags;
C_UndefineVolume;
C_DefineSkillName;
C_UndefineSkill;
C_DefineLevelName;
C_UndefineLevel;
C_DefineProjectile;
C_DefineGameFuncName;
C_DefineGameType;
C_SetDefName;
C_SetCfgName;
SCRIPT_GetNumber;
SCRIPT_PutNumber;
actor;
g_camera;
ud;
g_player;
g_player_ps;
g_playerWeapon;
g_weaponOverridden;
WeaponPickupSprites;
g_tile;
SpriteProjectile;
g_noResetVars;
A_ResetVars;
g_dynTileList;
g_dynSoundList;
apStrings;
screenpeek;
hudweap;
g_mostConcurrentPlayers;
g_deleteQueueSize;
g_blimpSpawnItems;
g_scriptVersion;
g_frameRate;
g_currentMenu;
g_earthquakeTime;
g_moveThingsCount;
CheatKeys;
g_logoFlags;
luaJIT_BC_lunacon;
luaJIT_BC_con_lang;
luaJIT_BC_randgen;
luaJIT_BC_stat;
luaJIT_BC_bitar;
luaJIT_BC_control;
luaJIT_BC_bcarray;
luaJIT_BC_bcheck;
luaJIT_BC_xmath;
luaJIT_BC__defs_game;
luaJIT_BC_v;
luaJIT_BC_dump;
luaJIT_BC_dis_x86;
luaJIT_BC_dis_x64;
luaJIT_BC_lunasave;
luaJIT_BC_fs;
rand_jkiss_u32;
rand_jkiss_dbl;
A_IncurDamage;
G_CheckActivatorMotion;
A_Dodge;
A_MoveSpriteClipdist;
P_DoQuote;
P_SetGamePalette;
G_AddUserQuote;
G_ClearCameraView;
VM_DrawTileGeneric;
G_InitTimer;
G_GetTimeDate;
G_ToggleWallInterpolation;
G_StartTrack;
VM_CheckSquished2;
Menu_Change;
KB_ScanCodeToString;
A_CheckAnySoundPlaying;
S_CheckSoundPlaying;
S_StopEnvSound;
S_StopAllSounds;
S_ChangeSoundPitch;
S_GetMusicPosition;
S_SetMusicPosition;
minitext_;
G_DrawTXDigiNumZ;
G_PrintGameText;
G_ScreenText;
G_ScreenTextSize;
G_PrintYourTime;
G_PrintParTime;
G_PrintDesignerTime;
G_PrintBestTime;
G_UpdateScreenArea;
G_SaveMapState;
G_RestoreMapState;
G_FreeMapState;
};

View file

@ -1,522 +0,0 @@
local ffi = require("ffi")
local C = ffi.C
local bcarray = require("bcarray")
local assert = assert
local error = error
local ipairs = ipairs
local type = type
local decl = assert(decl) -- comes from above (_defs_game.lua or defs_m32.lua)
local ismapster32 = (C.LUNATIC_CLIENT == C.LUNATIC_CLIENT_MAPSTER32)
----------
decl[[
const int32_t qsetmode;
int32_t getclosestcol_lim(int32_t r, int32_t g, int32_t b, int32_t lastokcol);
char *palookup[256]; // MAXPALOOKUPS
uint8_t palette[768];
uint8_t *basepaltable[];
const char *getblendtab(int32_t blend);
void setblendtab(int32_t blend, const char *tab);
int32_t setpalookup(int32_t palnum, const uint8_t *shtab);
]]
if (ismapster32) then
ffi.cdef[[
int32_t _getnumber16(const char *namestart, int32_t num, int32_t maxnumber, char sign, const char *(func)(int32_t));
const char *getstring_simple(const char *querystr, const char *defaultstr, int32_t maxlen, int32_t completion);
typedef const char *(*luamenufunc_t)(void);
void LM_Register(const char *name, luamenufunc_t funcptr, const char *description);
void LM_Clear(void);
]]
end
----------
-- The API table
local engine = {}
local shtab_t -- forward-decl
local function cast_u8ptr(sth)
return ffi.cast("uint8_t *", sth)
end
local shtab_methods = {
-- Remap consecutive blocks of 16 color indices and return this new shade
-- table.
--
-- <idxs16>: table with idxs16[0] .. idxs16[15] >= 0 and <= 15
-- (i.e. 0-based indices of such 16-tuples)
--
-- For example, the table
-- { [0]=0,1, 2,3, 5,4, 6,7, 8,13, 10,11, 12,9, 14,15 }
-- TODO (...)
remap16 = function(sht, idxs16)
if (type(idxs16) ~= "table") then
error("invalid argument #2: must be a table", 2)
end
for i=0,15 do
local idx = idxs16[i]
if (not (idx==nil or type(idx)=="number" and idx >= 0 and idx <= 15)) then
error("invalid reordering table: elements must be numbers in [0 .. 15], or nil", 2)
end
end
local newsht = shtab_t()
for sh=0,31 do
for i=0,15 do
ffi.copy(cast_u8ptr(newsht[sh]) + 16*i,
cast_u8ptr(sht[sh]) + 16*(idxs16[i] or i), 16)
end
end
return newsht
end,
}
local function shtab_mt__index(sht, idx)
local method = shtab_methods[idx]
if (method) then
return method
end
end
local pal256_t = bcarray.new("uint8_t", 256, "color index 256-tuple")
local SIZEOF_PAL256 = ffi.sizeof(pal256_t)
-- The shade table type, effectively a bound-checked uint8_t [32][256]:
shtab_t = bcarray.new(pal256_t, 32, "shade table", nil, nil, { __index = shtab_mt__index })
local SIZEOF_SHTAB = ffi.sizeof(shtab_t)
local blendtab_t = bcarray.new(pal256_t, 256, "blending table")
local SIZEOF_BLENDTAB = ffi.sizeof(blendtab_t)
local RESERVEDPALS = 8 -- KEEPINSYNC build.h: assure that ours is >= theirs
engine.RESERVEDPALS = RESERVEDPALS
local MAXBLENDTABS = 256 -- KEEPINSYNC build.h
local function check_palidx(i)
if (type(i) ~= "number" or not (i >= 0 and i <= 255-RESERVEDPALS)) then
error("invalid argument #1: palette swap index must be in the range [0 .. "..255-RESERVEDPALS.."]", 3)
end
end
local function check_blendidx(i)
if (type(i) ~= "number" or not (i >= 0 and i <= MAXBLENDTABS-1)) then
error("invalid argument #1: blending table index must be in the range [0 .. ".. MAXBLENDTABS-1 .."]", 3)
end
end
local function err_uncommon_shade_table(ret)
if (ret == -1) then
error("loaded engine shade tables don't have 32 gradients of shade", 3)
end
end
local function palookup_isdefault(palnum) -- KEEPINSYNC engine.c
return (C.palookup[palnum] == nil or (palnum ~= 0 and C.palookup[palnum] == C.palookup[0]))
end
function engine.shadetab()
return shtab_t()
end
function engine.blendtab()
return blendtab_t()
end
function engine.getshadetab(palidx)
check_palidx(palidx)
if (palookup_isdefault(palidx)) then
return nil
end
local ret = C.setpalookup(palidx, nil)
err_uncommon_shade_table(ret)
local sht = shtab_t()
ffi.copy(sht, C.palookup[palidx], SIZEOF_SHTAB)
return sht
end
function engine.getblendtab(blendidx)
check_blendidx(blendidx)
local ptr = C.getblendtab(blendidx)
if (ptr == nil) then
return nil
end
local tab = blendtab_t()
ffi.copy(tab, ptr, SIZEOF_BLENDTAB)
return tab
end
local function check_first_time()
if (not ismapster32 and C.g_elFirstTime == 0) then
error("may be called only while LUNATIC_FIRST_TIME is true", 3)
end
end
function engine.setshadetab(palidx, shtab)
check_first_time()
check_palidx(palidx)
if (not ffi.istype(shtab_t, shtab)) then
error("invalid argument #2: must be a shade table obtained by shadetab()", 2)
end
if (not ismapster32 and not palookup_isdefault(palidx)) then
error("attempt to override already defined shade table", 2)
end
local ret = C.setpalookup(palidx, cast_u8ptr(shtab))
err_uncommon_shade_table(ret)
end
function engine.setblendtab(blendidx, tab)
check_first_time()
check_blendidx(blendidx)
if (not ffi.istype(blendtab_t, tab)) then
error("invalid argument #2: must be a blending table obtained by blendtab()", 2)
end
if (not ismapster32 and C.getblendtab(blendidx) ~= nil) then
error("attempt to override already defined blending table", 2)
end
C.setblendtab(blendidx, cast_u8ptr(tab))
end
local function check_colcomp(a)
if (type(a) ~= "number" or not (a >= 0 and a < 256)) then
error("color component must be in the range [0 .. 256)", 3)
end
end
-- TODO: other base palettes?
function engine.getrgb(colidx)
if (type(colidx) ~= "number" or not (colidx >= 0 and colidx <= 255)) then
error("color index must be in the range [0 .. 255]", 2)
end
-- NOTE: In the game, palette[255*{0..2}] is set to 0 in
-- G_LoadExtraPalettes() via G_Startup(). However, that's after Lua state
-- initialization (i.e. when LUNATIC_FIRST_TIME would be true), and in the
-- editor, it's never changed from the purple color. Therefore, I think
-- it's more useful to always return the fully black color here.
if (colidx == 255) then
return 0, 0, 0
end
local rgbptr = C.palette + 3*colidx
return rgbptr[0], rgbptr[1], rgbptr[2]
end
function engine.nearcolor(r, g, b, lastokcol)
check_colcomp(r)
check_colcomp(g)
check_colcomp(b)
if (lastokcol == nil) then
lastokcol = 255
elseif (type(lastokcol)~="number" or not (lastokcol >= 0 and lastokcol <= 255)) then
error("invalid argument #4 <lastokcol>: must be in the range [0 .. 255]", 2)
end
return C.getclosestcol_lim(r, g, b, lastokcol)
end
---------- Mapster32-only functions ----------
if (ismapster32) then
local io = require("io")
local math = require("math")
local string = require("string")
ffi.cdef[[size_t fwrite(const void * restrict ptr, size_t size, size_t nmemb, void * restrict stream);]]
local function validate_more_blendtabs(moreblends, kindname, gettabfunc)
if (moreblends == nil) then
return nil, nil
end
-- Additional blending tables: validate <moreblends> table.
if (type(moreblends) ~= "table") then
error("invalid argument #4: must be a table", 3)
end
local haveblend = { [0]=true }
local blendnumtab, blendptrtab = {}, {}
for i=1,#moreblends do
local tmp = moreblends[i]
local blendspec = (type(tmp) == "number") and { tmp, tmp } or tmp
if (not (type(blendspec) == "table" and #blendspec == 2)) then
error("invalid argument #4: must contain numbers or 2-tables", 3)
end
local blend1, blend2 = math.floor(blendspec[1]), math.floor(blendspec[2])
if (not (type(blend1)=="number" and blend1 >= 1 and blend1 <= 255 and
type(blend2)=="number" and blend2 >= 1 and blend2 <= 255)) then
error("invalid argument #4: "..kindname.." table numbers must be in [1 .. 255]", 3)
end
for bi=blend1,blend2 do
if (haveblend[bi]) then
error("invalid argument #4: duplicate "..kindname.." table number "..bi, 3)
end
haveblend[bi] = true
local ptr = gettabfunc(bi)
if (ptr == nil) then
error("invalid argument #4: "..kindname.." table for number "..bi.." is void", 3)
end
blendnumtab[#blendnumtab+1] = bi
blendptrtab[#blendptrtab+1] = ptr
end
end
assert(#blendnumtab <= 255)
return blendnumtab, blendptrtab
end
-- ok, errmsg, nummoreblends = engine.savePaletteDat(
-- filename [, palnum [, blendnum [, moreblends [, lognumalphatabs]]]])
function engine.savePaletteDat(filename, palnum, blendnum, moreblends, lognumalphatabs)
local sht = engine.getshadetab(palnum or 0)
local tab = engine.getblendtab(blendnum or 0)
if (sht == nil) then
return nil, "no shade table with number "..palnum
elseif (tab == nil) then
return nil, "no blending table with number "..blendnum
end
local blendnumtab, blendptrtab = validate_more_blendtabs(
moreblends, "blending", C.getblendtab)
if (lognumalphatabs ~= nil) then
if (not (type(lognumalphatabs)=="number" and lognumalphatabs >= 1 and lognumalphatabs <= 7)) then
error("invalid argument #5: must be a number in [1 .. 7]", 2)
end
end
local f, errmsg = io.open(filename, "wb+")
if (f == nil) then
return nil, errmsg
end
local truncpal = pal256_t()
ffi.copy(truncpal, C.palette, SIZEOF_PAL256)
for i=0,255 do
truncpal[i] = bit.rshift(truncpal[i], 2)
end
local n1 = C.fwrite(truncpal, 3, 256, f)
f:write("\032\000") -- int16_t numshades
local n3 = C.fwrite(sht, 256, 32, f)
local n4 = C.fwrite(tab, 256, 256, f)
if (n1 ~= 256 or n3 ~= 32 or n4 ~= 256) then
return nil, "failed writing classic PALETTE.DAT data"
end
if (blendnumtab ~= nil) then
f:write("MoreBlendTab")
f:write(string.char(#blendnumtab))
for i=1,#blendnumtab do
f:write(string.char(blendnumtab[i]))
if (C.fwrite(blendptrtab[i], 256, 256, f) ~= 256) then
return nil, "failed writing additional blending table"
end
end
if (lognumalphatabs ~= nil) then
-- XXX: no checking whether these blending tables 1 to
-- 1<<lognumalphatabs have been written.
f:write(string.char(lognumalphatabs))
end
end
f:close()
return true, nil, (blendnumtab ~= nil) and #blendnumtab or 0
end
-- ok, errmsg, numlookups = engine.saveLookupDat(filename, lookups)
function engine.saveLookupDat(filename, lookups)
if (lookups == nil) then
-- set to an invalid value, validate_more_blendtabs will error
lookups = 0
end
local lookupnumtab, lookupptrtab = validate_more_blendtabs(
lookups, "lookup", engine.getshadetab)
local f, errmsg = io.open(filename, "wb+")
if (f == nil) then
return nil, errmsg
end
f:write(string.char(#lookupnumtab))
for i=1,#lookupnumtab do
f:write(string.char(lookupnumtab[i]))
if (C.fwrite(lookupptrtab[i], 1, 256, f) ~= 256) then
return nil, "failed writing lookup table"
end
end
-- Write five base palettes
for i=1,5 do
local bpi = (i==3 or i==4) and 4+3-i or i
local truncbasepal = pal256_t()
ffi.copy(truncbasepal, C.basepaltable[bpi], SIZEOF_PAL256)
for j=0,255 do
truncbasepal[j] = bit.rshift(truncbasepal[j], 2)
end
if (C.fwrite(truncbasepal, 1, 768, f) ~= 768) then
return nil, "failed writing base palette"
end
end
f:close()
return true, nil, #lookupnumtab
end
local hexmap = {
[0] = 0, -14, -- 0, 1: gray ramp
14, 0, -- 2, 3: skin color ramp
0, 14, -- 4, 5: blue ramp (second part first)
14, 0, -- 6, 7: nightvision yellow/green
14, -- 8: red first part...
8, -- 9: yellow (slightly more red than green)
14, 0, -- 10, 11: almost gray ramp, but with a slight red hue
8, -- 12: "dirty" orange
0, -- 13: ...red second part
8, -- 14: blue-purple-red
}
-- Setup base palette 1 (water) to contain one color for each consecutive
-- 16-tuple (which I'm calling a 'hex' for brevity), except for the last
-- one with the fullbrights.
function engine.setupDebugBasePal()
for i=0,14 do
local ptr = C.basepaltable[1] + 3*(16*i)
local src = C.basepaltable[0] + 3*(16*i) + 3*hexmap[i]
local r, g, b = src[0], src[1], src[2]
for j=0,15 do
local dst = ptr + 3*j
dst[0], dst[1], dst[2] = r, g, b
end
end
end
function engine.linearizeBasePal()
for _, begi in ipairs{0, 32, 96, 160} do
local ptr = C.basepaltable[0] + 3*begi
local refcol = ptr + 3*31
for i=0,30 do
for c=0,2 do
ptr[3*i + c] = i*refcol[c]/31
end
end
end
for _, begi in ipairs{128, 144, 192, 208, 224} do
local ptr = C.basepaltable[0] + 3*begi
for i=0,3*15+2 do
ptr[i] = 0
end
end
end
-- Interfaces to Mapster32's status bar menu
local pcall = pcall
function engine.clearMenu()
C.LM_Clear()
end
function engine.registerMenuFunc(name, func, description)
if (type(name) ~= "string") then
error("invalid argument #1: must be a string", 2)
end
if (type(func) ~= "function") then
error("invalid argument #2: must be a function", 2)
end
if (description~=nil and type(description)~="string") then
error("invalid argument #3: must be nil or a string", 2)
end
local safefunc = function()
local ok, errmsg = pcall(func)
if (not ok) then
return errmsg
end
end
C.LM_Register(name, safefunc, description)
end
engine.GETNUMFLAG = {
NEG_ALLOWED = 1,
AUTOCOMPL_NAMES = 2,
AUTOCOMPL_TAGLAB = 4,
RET_M1_ON_CANCEL = 8,
NEXTFREE = 16,
}
function engine.getnumber16(namestart, num, maxnumber, flags)
if (C.qsetmode == 200) then
error("getnumber16 must be called from 2D mode", 2)
end
if (type(namestart)~="string") then
error("invalid argument #1: must be a string", 2)
end
return C._getnumber16(namestart, num, maxnumber, flags or 8, nil) -- RET_M1_ON_CANCEL
end
function engine.getstring(querystr)
if (type(querystr) ~= "string") then
error("invalid argument #2: must be a string", 2)
end
local cstr = C.getstring_simple(querystr, nil, 0, 0)
return cstr~=nil and ffi.string(cstr) or nil
end
end
-- Done!
return engine

View file

@ -1,538 +0,0 @@
-- MAPTEXT
-- Lunatic: routines for reading and writing map-text.
local ffi = require("ffi")
local ffiC = ffi.C
local assert = assert
local loadstring = loadstring
local pairs = pairs
local pcall = pcall
local print = print
local setfenv = setfenv
local tonumber = tonumber
local type = type
local readintostr = assert(string.readintostr)
local io = require("io")
local string = require("string")
ffi.cdef[[
int32_t (*saveboard_maptext)(const char *filename, const vec3_t *dapos, int16_t daang, int16_t dacursectnum);
int32_t (*loadboard_maptext)(int32_t fil, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum);
]]
--== COMMON ==--
local sector_members = {
-- Mandatory positional members first, [pos]=<name>.
"ceilingz", "floorz",
"ceilingpicnum", "floorpicnum",
"ceilingshade", "floorshade";
-- If other positional members are to be added, they must be optional
-- for backwards compatibility.
-- Optional key/value members next.
B = { "ceilingbunch", -1 }, b = { "floorbunch", -1 }, -- default: -1
F = "ceilingstat", f = "floorstat", -- default: 0
H = "ceilingheinum", h = "floorheinum",
P = "ceilingpal", p = "floorpal",
X = "ceilingxpanning", x = "floorxpanning",
Y = "ceilingypanning", y = "floorypanning",
v = "visibility",
_ = "fogpal",
o = "lotag", i = "hitag", e = { "extra", -1 }
}
-- Defines the order in which the members are written out. A space denotes that
-- a newline should appear in the output. KEEPINSYNC with sector_members.
local sector_ord = { mand="12 34 56 ", opt="Bb Ff Hh Pp Xx Yy v _ oie" }
-- KEEPINSYNC with sector_members.
local sector_default = ffi.new("const sectortype", { ceilingbunch=-1, floorbunch=-1, extra=-1 })
local wall_members = {
-- mandatory
"point2", -- special: 0, 1 or 2 in map-text
"x", "y",
"nextwall",
"picnum",
"shade",
"xrepeat", "yrepeat",
"xpanning", "ypanning";
-- optional
f = "cstat",
m = "overpicnum", b = "blend",
p = "pal",
w = { "upwall", -1 }, W = { "dnwall", -1 },
o = "lotag", i = "hitag", e = { "extra", -1 }
}
local wall_ord = { mand="1 23 4 5 6 78 90 ", opt="f mb p wW oie" }
local wall_default = ffi.new("const walltype", { extra = -1, upwall=-1, dnwall=-1 })
local sprite_members = {
-- mandatory
"x", "y", "z",
"ang",
"sectnum",
"picnum",
"cstat",
"shade",
"xrepeat", "yrepeat",
-- optional
p = "pal",
c = { "clipdist", 32 },
b = "blend",
x = "xoffset", y = "yoffset",
s = "statnum",
w = { "owner", -1 },
X = "xvel", Y = "yvel", Z = "zvel",
o = "lotag", i = "hitag", e = { "extra", -1 }
}
local sprite_ord = { mand="123 4 5 6 7 8 90 ", opt="p c b xy s w XYZ oie" }
local sprite_default = ffi.new("const spritetype", { clipdist=32, owner=-1, extra=-1 })
--== SAVING ==--
local function write_struct(f, struct, members, ord)
-- Write mandatory members first.
local str = ord.mand:gsub(".",
function(s)
local num = (s=="0") and 10 or tonumber(s)
return (s==" ") and "\n" or (struct[members[num]]..",")
end)
f:write("{"..str)
local havesth = false
-- Write optional members next.
str = ord.opt:gsub(".",
function(s)
if (s==" ") then
local ohavesth = havesth
havesth = false
return ohavesth and "\n" or ""
end
local memb = members[s]
local mname = (type(memb)=="table") and memb[1] or memb
local mdefault = (type(memb)=="table") and memb[2] or 0
local val = struct[mname]
if (val~=mdefault) then
havesth = true
return s.."="..val..","
else
return ""
end
end)
local neednl = (#str>0 and str:sub(-1)~="\n")
f:write(str..(neednl and "\n" or "").."},\n")
end
-- common
local function check_bad_point2()
local lastloopstart = 0
for i=0,ffiC.numwalls-1 do
local p2 = ffiC.wall[i].point2
if (not (p2 == i+1 or (p2 ~= i and p2 == lastloopstart))) then
-- If we hit this, the map is seriously corrupted!
print(string.format("INTERNAL ERROR: wall[%d].point2=%d invalid", i, p2))
return true
end
if (p2 ~= i+1) then
lastloopstart = i+1
end
end
end
local function lastwallofsect(s)
return ffiC.sector[s].wallptr + ffiC.sector[s].wallnum - 1
end
-- In map-text, instead of saving wall[].point2, we store whether a particular
-- wall is the last one in its loop instead: the on-disk wall[i].point2 is
-- * 2 if wall i is last of its sector (no need to save sector's .wallnum),
-- * 1 if wall i is last of its loop,
-- * 0 otherwise.
-- This function prepares saving to map-text by tweaking the wall[].point2
-- members in-place.
local function save_tweak_point2()
-- Check first.
if (check_bad_point2()) then
return true
end
-- Do it for real.
local lastloopstart = 0
local cursect, curlastwall = 0, lastwallofsect(0)
for i=0,ffiC.numwalls-1 do
local wal = ffiC.wall[i]
if (wal.point2 == i+1) then
wal.point2 = 0
else
-- Wall i is last point in loop.
if (i==curlastwall) then
-- ... and also last wall of sector.
cursect = cursect+1
curlastwall = lastwallofsect(cursect)
wal.point2 = 2
else
wal.point2 = 1
end
lastloopstart = i+1
end
end
end
-- Common: restore tweaked point2 members to actual wall indices.
-- If <alsosectorp> is true, also set sector's .wallptr and .wallnum members.
local function restore_point2(alsosectorp)
local lastloopstart = 0
local cursect, curfirstwall = 0, 0
for i=0,ffiC.numwalls-1 do
local wal = ffiC.wall[i]
local islast = (wal.point2~=0)
if (not islast) then
wal.point2 = i+1
else
-- Wall i is last point in loop.
if (alsosectorp and wal.point2 == 2) then
-- ... and also last wall of sector.
if (cursect==ffiC.MAXSECTORS) then
return true -- Too many sectors.
end
ffiC.sector[cursect].wallptr = curfirstwall
ffiC.sector[cursect].wallnum = i-curfirstwall+1
cursect = cursect+1
curfirstwall = i+1
end
wal.point2 = lastloopstart
lastloopstart = i+1
end
end
end
local function saveboard_maptext(filename, pos, ang, cursectnum)
assert(ffiC.numsectors > 0)
-- First, temporarily tweak wall[].point2.
if (save_tweak_point2()) then
return -1
end
-- We open in binary mode so that newlines get written out as one byte even
-- on Windows.
local f, msg = io.open(ffi.string(filename), "wb")
if (f == nil) then
print(string.format("Couldn't open \"%s\" for writing: %s\n", filename, msg))
restore_point2(false)
return -1
end
-- Write header.
f:write(string.format("--EDuke32 map\n"..
"return {\n"..
"version=10,\n\n"..
"pos={%d,%d,%d},\n"..
"sectnum=%d,\n"..
"ang=%d,\n\n",
pos.x, pos.y, pos.z,
cursectnum,
ang))
-- Sectors.
f:write("sector={\n")
for i=0,ffiC.numsectors-1 do
write_struct(f, ffiC.sector[i], sector_members, sector_ord)
end
f:write("},\n\n")
-- Walls.
f:write("wall={\n")
for i=0,ffiC.numwalls-1 do
write_struct(f, ffiC.wall[i], wall_members, wall_ord)
end
f:write("},\n\n")
-- Sprites.
f:write("sprite={\n")
for i=0,ffiC.MAXSPRITES-1 do
if (ffiC.sprite[i].statnum ~= ffiC.MAXSTATUS) then
write_struct(f, ffiC.sprite[i], sprite_members, sprite_ord)
end
end
f:write("},\n\n")
f:write("}\n");
-- Done.
f:close()
restore_point2(false)
return 0
end
--== LOADING ==--
local function isnum(v)
return (type(v)=="number")
end
local function istab(v)
return (type(v)=="table")
end
-- Checks whether <tab> is a table all values of <tab> are of type <extype>.
local function allxtab(tab, extype)
if (not istab(tab)) then
return false
end
for _,val in pairs(tab) do
if (type(val) ~= extype) then
return false
end
end
return true
end
-- Is table of all numbers?
local function allnumtab(tab) return allxtab(tab, "number") end
-- Is table of all tables?
local function alltabtab(tab) return allxtab(tab, "table") end
-- Is table of tables of all numbers? Additionally, each must contain exactly
-- as many mandatory positional entries as given by the <members> table.
local function tabofnumtabs(tab, members)
for i=1,#tab do
if (not allnumtab(tab[i])) then
return false
end
local nummand = #members -- number of mandatory entries
if (#tab[i] ~= nummand) then
return false
end
end
return true
end
-- Read data from Lua table <stab> into C struct <cs>, using the struct
-- description <members>.
-- Returns true on error.
local function read_struct(cs, stab, members, defaults)
-- Clear struct to default values.
ffi.copy(cs, defaults, ffi.sizeof(defaults))
-- Read mandatory positional members.
for i=1,#members do
cs[members[i]] = stab[i]
end
-- Read optional key/value members.
for k,val in pairs(stab) do
if (members[k]==nil) then
-- No such member abbreviation for the given struct.
return true
end
local memb = istab(members[k]) and members[k][1] or members[k]
cs[memb] = val
end
end
local RETERR = -4
local function loadboard_maptext(fil, posptr, angptr, cursectnumptr)
-- Read the whole map-text as string.
local str = readintostr(fil)
if (str == nil) then
return RETERR
end
-- Strip all one-line comments (currently, only the header).
str = str:gsub("%-%-.-\n", "")
--- Preliminary (pseudo-syntactical) validation ---
-- Whitelist approach: map-text may only contain certain characters. This
-- excludes various potentially 'bad' operations (such as calling a
-- function) in one blow. Also, this assures (by exclusion) that the Lua
-- code contains no long comments, strings, or function calls.
if (not str:find("^[ A-Za-z_0-9{},%-\n=]+$")) then
return RETERR-1
end
-- The map-text code must return a single table.
if (not str:find("^return %b{}\n$")) then
return RETERR-2
end
local func, errmsg = loadstring(str, "maptext")
if (func == nil) then
print("Error preloading map-text Lua code: "..errmsg)
return RETERR-3
end
-- Completely empty the function's environment as an additional safety
-- measure, then run the chunk protected! (XXX: currently a bit pointless
-- because of the asserts below.)
local ok, map = pcall(setfenv(func, {}))
if (not ok) then
print("Error executing map-text Lua code: "..map)
return RETERR-4
end
assert(istab(map))
-- OK, now 'map' contains the map data.
--- Structural validation ---
-- Check types.
if (not isnum(map.version) or not allnumtab(map.pos) or #map.pos~=3 or
not isnum(map.sectnum) or not isnum(map.ang))
then
return RETERR-5
end
if (not (map.version <= 10)) then
return RETERR-6
end
local msector, mwall, msprite = map.sector, map.wall, map.sprite
if (not alltabtab(msector) or not alltabtab(mwall) or not alltabtab(msprite)) then
return RETERR-7
end
if (not tabofnumtabs(msector, sector_members) or
not tabofnumtabs(mwall, wall_members) or
not tabofnumtabs(msprite, sprite_members))
then
return RETERR-8
end
local numsectors, numwalls, numsprites = #msector, #mwall, #msprite
local sector, wall, sprite = ffiC.sector, ffiC.wall, ffiC.sprite
if (numsectors == 0 or numsectors > ffiC.MAXSECTORS or
numwalls > ffiC.MAXWALLS or numsprites > ffiC.MAXSPRITES)
then
return RETERR-9
end
--- From here on, start filling out C structures. ---
ffiC.numsectors = numsectors
ffiC.numwalls = numwalls
-- Header.
posptr.x = map.pos[1]
posptr.y = map.pos[2]
posptr.z = map.pos[3]
angptr[0] = map.ang
cursectnumptr[0] = map.sectnum
-- Sectors.
for i=0,numsectors-1 do
if (read_struct(sector[i], msector[i+1], sector_members, sector_default)) then
return RETERR-10
end
end
-- Walls.
for i=0,numwalls-1 do
if (read_struct(wall[i], mwall[i+1], wall_members, wall_default)) then
return RETERR-11
end
end
-- Sprites.
for i=0,numsprites-1 do
if (read_struct(sprite[i], msprite[i+1], sprite_members, sprite_default)) then
return RETERR-12
end
end
-- XXX: need to consistency-check much more here! Basically, all of
-- astub.c's CheckMapCorruption() for corruption level >=4?
-- See NOTNICE below.
--- Tweakery: mostly setting dependent members. ---
-- sector[]: .wallptr calculated from .wallnum.
local numw = 0
for i=0,numsectors-1 do
assert(numw >= 0 and numw < numwalls) -- NOTNICE, cheap check instead of real one.
sector[i].wallptr = numw
numw = numw + sector[i].wallnum
end
-- .point2 in {0, 1} --> wall index, sector[].wallptr/.wallnum
if (restore_point2(true)) then
return RETERR-13
end
-- Check .point2 at least.
if (check_bad_point2()) then
return RETERR-14
end
-- wall[]: .nextsector calculated by using engine's sectorofwall_noquick()
for i=0,numwalls-1 do
local nw = wall[i].nextwall
if (nw >= 0) then
assert(nw >= 0 and nw < numwalls) -- NOTNICE
wall[i].nextsector = ffiC.sectorofwall_noquick(nw)
else
wall[i].nextsector = -1
end
end
-- All OK, return the number of sprites for further engine loading code.
return numsprites
end
-- Register our Lua functions as callbacks from C.
ffiC.saveboard_maptext = saveboard_maptext
ffiC.loadboard_maptext = loadboard_maptext

View file

@ -1,96 +0,0 @@
-- Virtual file system facilities for Lunatic
local ffi = require("ffi")
local C = ffi.C
local decl = require("lprivate").decl
local assert = assert
local error = error
local type = type
----------
decl[[
int32_t pathsearchmode;
typedef struct _CACHE1D_FIND_REC {
char *name;
int32_t type, source;
struct _CACHE1D_FIND_REC *next, *prev, *usera, *userb;
} CACHE1D_FIND_REC;
void klistfree(CACHE1D_FIND_REC *rec);
CACHE1D_FIND_REC *klistpath(const char *path, const char *mask, int32_t type);
]]
-- The API table
local fs = {}
local CACHE1D = ffi.new[[
struct {
static const int FIND_FILE = 1;
static const int FIND_DIR = 2;
static const int FIND_DRIVE = 4;
static const int FIND_NOCURDIR = 8;
static const int OPT_NOSTACK = 0x100;
// the lower the number, the higher the priority
static const int SOURCE_DRIVE = 0;
static const int SOURCE_CURDIR = 1;
static const int SOURCE_PATH = 2; // + path stack depth
static const int SOURCE_ZIP = 0x7ffffffe;
static const int SOURCE_GRP = 0x7fffffff;
}
]]
-- files = fs.listpath(path, mask)
-- TODO: filter by 'source' (path, zip and/or grp)
-- TODO: directories too, so that one can list them recursively, for example
function fs.listpath(path, mask)
if (type(path)~="string") then
-- TODO: maybe also allow nil
error("Invalid argument #1: must be a string", 2)
end
if (type(mask) ~= "string") then
error("Invalid argument #2: must be a string", 2)
end
-- Normalization, for portability's sake
if (path:find("\\")) then
error("Invalid argument #1: must not contain backslashes", 2)
end
if (mask:find("[\\/]")) then
error("Invalid argument #2: must not contain back or forward slashes", 2)
end
local opsm = C.pathsearchmode
C.pathsearchmode = 0
local rootrec = C.klistpath(path, mask, CACHE1D.FIND_FILE + CACHE1D.FIND_NOCURDIR)
C.pathsearchmode = opsm
if (rootrec == nil) then
-- XXX: may have failed hard or just no matching files
return {}
end
local files = {}
local rec = rootrec
while (rec ~= nil) do
assert(rec.name ~= nil)
files[#files+1] = ffi.string(rec.name)
rec = rec.next
end
C.klistfree(rootrec)
return files
end
return fs

View file

@ -1,35 +0,0 @@
#!/bin/sh
if [ -z "$1" ]; then
# S or G can be used to display SETGLOBALs or GETGLOBALs, respectively
echo "Usage: $0 [S|G] <files.lua...>"
exit 1;
fi
GS=
if [ "$1" = G ]; then
GS=G
shift
elif [ "$1" = S ]; then
GS=S
shift
fi
# Lua 5.2 required to parse ::label:: / goto generated by LunaCON.
LUAC=luac
for f in "$@"; do
if [ ! -f "$f" ]; then
echo "$f: No such file"
exit 2
fi
echo "[$f]"
# Strip LuaJIT specific syntax first. Run luac and extract interesting lines.
sed -r -e "s/[0-9]+U?LL/0/g" "$f" | $LUAC -p -l - | grep "${GS}ETTABUP.*; _ENV " |
# Reformat them.
sed -e 's/.*\[\(.*\)\].*\([SG]ET\)TABUP.*"\(.*\)".*/\1: \2 \3/' |
# Mark where the new module environment starts.
sed -e 's/GET module/& ____________________/'
echo
done

File diff suppressed because it is too large Load diff

View file

@ -1,210 +0,0 @@
-- Savegame facilities for Lunatic.
local string = require("string")
local table = require("table")
local format = string.format
local assert = assert
local getmetatable = getmetatable
local pairs = pairs
local setmetatable = setmetatable
local tostring = tostring
local type = type
local cansave_cdata = require("lprivate").cansave_cdata
module(...)
---=== Serialization, based on the idea from PiL ===---
assert(tostring(0/0)=="nan")
assert(tostring(1/0)=="inf")
-- Serialize a 'primitive' Lua value.
local function basicSerialize(o)
-- Compare with sb_get_initial_strbuf() below.
-- XXX: nil?
if (type(o) == "number") then
-- NOTE: NaN and infinity handled.
return tostring(o)
elseif (type(o) == "boolean") then
return o and "t" or "f"
elseif (type(o) == "string") then
-- TODO: return refname if it's shorter
return format("%q", o)
end
end
local function isSerializeable(obj)
return (getmetatable(obj)=="serializeable" or cansave_cdata(obj))
end
-- 'Save buffer' class. Objects of this class keep track of a string buffer
-- contiaining Lua code to recreate the values of particular variables from a
-- user Lunatic environment.
local savebuffer_mt = {
__index = {
-- Called on starting processing one module.
startmod = function(self, modname)
self:addrawf("if (N==%q) then", modname)
-- Will contain all tables and cdata refs of this module, indexed
-- by number.
self:addraw("local T={}")
self:resetRefs()
end,
-- Called on finishing processing one module.
endmod = function(self)
self:addraw("end")
end,
--== vvv INTERNAL vvv ===---
resetRefs = function(self)
self.numrefs = 0
self.recursionDepth = 0
self.val2ref = {}
self.havereq = {}
end,
-- Get the code necessary to create this object, usually 'require'ing a
-- module into a local variable.
getRequire = function(self, value)
local reqcode = value:_get_require()
if (self.havereq[reqcode] == nil) then
self.havereq[reqcode] = true
self.strbuf[#self.strbuf+1] = reqcode
end
end,
emitT = function(self, refcode, valcode, obj)
self:addrawf("%s=%s", refcode, valcode)
self.val2ref[obj] = refcode
end,
emitAssign = function(self, lhscode, refcode)
self:addrawf("%s=%s", lhscode, refcode)
end,
--== ^^^ INTERNAL ^^^ ===---
-- Add an entry of Lua object <value> that can be referenced by
-- <lhscode> on the left hand side of an assignment.
-- <excludedKeys>: if non-nil, should be table
-- [<key_string>] = true. See EXCLUDE_KEYS below.
-- Returns 'true' if <value> cannot be serialized.
add = function(self, lhscode, value, excludedKeys)
local valcode = basicSerialize(value)
if (valcode == nil) then
-- <value> is a not a 'basic' Lua object, but one passed by
-- reference.
if (not self.val2ref[value]) then
-- Object is being serialized for the first time.
-- Create a slot in 'T' for this object by which we can
-- refer to it in the following.
self.numrefs = self.numrefs+1
local refcode = format("T[%d]", self.numrefs)
if (isSerializeable(value)) then
-- We have a serializeable object from Lunatic
-- (e.g. actorvar).
self:getRequire(value)
local restoreCode = value:_serialize()
if (restoreCode == nil) then -- XXX: check UNUSED?
return true
end
self:emitT(refcode, restoreCode, value)
valcode = refcode
elseif (type(value)=="table") then
-- Create a new table for this gamevar.
-- TODO: emit table initializations where possible.
self:emitT(refcode, "{}", value)
for k,v in pairs(value) do
local keystr = basicSerialize(k)
if (keystr == nil) then
return true
end
-- EXCLUDE_KEYS
local isNORESET = (self.recursionDepth == 0 and
excludedKeys and excludedKeys[k])
-- Generate the name under which the table element
-- is referenced.
local refcode2 = format("%s[%s]", refcode, keystr)
if (isNORESET) then
self:emitAssign(refcode2, format("M._V[%s]", keystr))
else
-- Recurse!
self.recursionDepth = self.recursionDepth+1
local ret = self:add(refcode2, v)
self.recursionDepth = self.recursionDepth-1
if (ret) then
return true
end
end
end
valcode = refcode
else
-- We have anything else: can't serialize.
return true
end
else
-- Object was previously serialized, get Lua expression it
-- can be referenced with.
valcode = self.val2ref[value]
end
end
self:emitAssign(lhscode, valcode)
end,
-- Add a single string to the buffer.
addraw = function(self, str)
self.strbuf[#self.strbuf+1] = str
end,
-- Add a single formatted string to the buffer.
addrawf = function(self, fmt, ...)
self:addraw(format(fmt, ...))
end,
-- Get the Lua code recreating the values as a string.
getcode = function(self)
self.strbuf[#self.strbuf+1] = "" -- add newline at EOS
return table.concat(self.strbuf, "\n")
end,
}
}
local function sb_get_initial_strbuf()
return {
"local nan, inf = 0/0, 1/0",
"local t, f = true, false",
}
end
-- Create a new savebuffer object.
function savebuffer()
-- .numrefs: how many table or cdata objects we have serialized
-- .val2ref: [<Lua object>] = <Lua code string>
-- .havereq = [<string>] = true
-- .strbuf: array of Lua code pieces
local sb = {
numrefs=0, val2ref={}, havereq={},
recursionDepth = 0,
strbuf=sb_get_initial_strbuf()
}
return setmetatable(sb, savebuffer_mt)
end

View file

@ -1,20 +0,0 @@
/* The Lunatic Interpreter, part of EDuke32. Editor stuff. */
#ifdef USE_LUAJIT_2_1
# include <luajit-2.1/lualib.h>
#else
# include <luajit-2.0/lualib.h>
#endif
#include "lunatic_editor.h"
int Em_CreateState(L_State *estate)
{
return L_CreateState(estate, "m32", NULL);
}
void Em_DestroyState(L_State *estate)
{
L_DestroyState(estate);
}

View file

@ -1,19 +0,0 @@
/* The Lunatic Interpreter, part of EDuke32. Editor stuff. */
#ifndef EDUKE32_LUNATIC_M32_H_
#define EDUKE32_LUNATIC_M32_H_
#include "lunatic.h"
#ifdef __cplusplus
extern "C" {
#endif
int Em_CreateState(L_State *estate);
void Em_DestroyState(L_State *estate);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -1,661 +0,0 @@
/* The Lunatic Interpreter, part of EDuke32. Game-side stuff. */
#include "compat.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef USE_LUAJIT_2_1
# include <luajit-2.1/lualib.h>
# include <luajit-2.1/lauxlib.h>
#else
# include <luajit-2.0/lualib.h>
# include <luajit-2.0/lauxlib.h>
#endif
#ifdef __cplusplus
}
#endif
#include "build.h" // printext256
#include "lunatic_game.h"
#include "osd.h"
#include "gamedef.h" // EventNames[]
L_State g_ElState;
// this serves two purposes:
// the values as booleans and the addresses as keys to the Lua registry
uint8_t g_elEvents[MAXEVENTS];
// same thing for actors:
el_actor_t g_elActors[MAXTILES];
// Session variable. Never restored except by 'readgamevar'.
int32_t g_elSessionVar[8]; // MAXSESSIONVARS, KEEPINSYNC con_lang.lua
// Set to 1 on error in event.
int32_t g_elEventError;
// Will be set to 0 after the first time that user Lua modules are run.
int32_t g_elFirstTime = 1;
int32_t g_elCallDepth = 0;
int32_t g_RETURN;
// for timing events and actors
static int32_t g_timingInited = 0;
// Used as Lua registry key to the tweak_traceback_msg() function, set to 1 if
// such a function has been registered.
static uint8_t g_tweakTracebackMsg = 0;
// forward-decls...
static int32_t SetEvent_CF(lua_State *L);
static int32_t SetActor_CF(lua_State *L);
#ifdef __cplusplus
extern "C" {
#endif
// in lpeg.o
extern int luaopen_lpeg(lua_State *L);
#ifdef __cplusplus
}
#endif
// See: Good Practice in (Pseudo) Random Number Generation for
// Bioinformatics Applications, by David Jones
ATTRIBUTE_OPTIMIZE("O2")
LUNATIC_EXTERN uint32_t rand_jkiss_u32(rng_jkiss_t *s)
{
uint64_t t;
s->x = 314527869 * s->x + 1234567;
s->y ^= s->y << 5; s->y ^= s->y >> 7; s->y ^= s->y << 22;
t = 4294584393ULL * s->z + s->c; s->c = t >> 32; s->z = t;
return s->x + s->y + s->z;
}
ATTRIBUTE_OPTIMIZE("O2")
LUNATIC_EXTERN double rand_jkiss_dbl(rng_jkiss_t *s)
{
double x;
unsigned int a, b;
a = rand_jkiss_u32(s) >> 6; /* Upper 26 bits */
b = rand_jkiss_u32(s) >> 5; /* Upper 27 bits */
x = (a * 134217728.0 + b) / 9007199254740992.0;
return x;
}
void El_PrintTimes(void)
{
int32_t i;
const char nn = Bstrlen("EVENT_");
// Try environment variable specifying the base name (sans ".actors.csv" or
// ".events.csv") for a CSV file to output, for further processing in e.g.
// GSL shell: http://www.nongnu.org/gsl-shell/
const char *basefn = getenv("LUNATIC_TIMING_BASEFN");
if (basefn != NULL)
{
const int32_t baselen = Bstrlen(basefn);
const int32_t addnlen = Bstrlen(".actors.csv"); // MUST equal that of ".events.csv"
char *fullfn = (char *)Xmalloc(baselen + addnlen + 1);
BFILE *outf;
if (fullfn == NULL)
return;
Bmemcpy(fullfn, basefn, baselen);
// EVENTS
Bmemcpy(fullfn+baselen, ".events.csv", addnlen+1);
outf = Bfopen(fullfn, "w");
if (outf == NULL)
{
OSD_Printf("Couldn't open \"%s\" for writing timing data: %s", fullfn, strerror(errno));
goto finish;
}
Bfprintf(outf, "evtname,numcalls,total_ms,mean_us\n"); // times in usecs are per-call
for (i=0; i<MAXEVENTS; i++)
if (g_eventCalls[i])
Bfprintf(outf, "%s,%d,%f,%f\n", EventNames[i]+nn, g_eventCalls[i], g_eventTotalMs[i],
1000*g_eventTotalMs[i]/g_eventCalls[i]);
Bfclose(outf);
// ACTORS
Bmemcpy(fullfn+baselen, ".actors.csv", addnlen+1);
outf = Bfopen(fullfn, "w");
if (outf == NULL)
{
OSD_Printf("Couldn't open \"%s\" for writing timing data: %s", fullfn, strerror(errno));
goto finish;
}
Bfprintf(outf, "tilenum,numcalls,total_ms,min_us,mean_us,max_us\n");
for (i=0; i<MAXTILES; i++)
if (g_actorCalls[i])
Bfprintf(outf, "%d,%d,%f,%f,%f,%f\n", i, g_actorCalls[i], g_actorTotalMs[i],
1000*g_actorMinMs[i],
1000*g_actorTotalMs[i]/g_actorCalls[i],
1000*g_actorMaxMs[i]);
Bfclose(outf);
OSD_Printf("Wrote timing data to \"%s.*.csv\"\n", basefn);
finish:
Bfree(fullfn);
return;
}
else
{
// If not writing out CSV files, print timing data to log instead.
char buf[32];
int32_t maxlen = 0;
int32_t haveev=0, haveac=0;
for (i=0; i<MAXEVENTS; i++)
{
int32_t len = Bstrlen(EventNames[i]+nn);
Bassert(len < (int32_t)sizeof(buf));
maxlen = max(len, maxlen);
}
for (i=0; i<MAXEVENTS; i++)
if (g_eventCalls[i])
{
int32_t n=Bsprintf(buf, "%s", EventNames[i]+nn);
if (!haveev)
{
haveev = 1;
OSD_Printf("\n -- event times: [event]={ total calls, total time [ms], mean time/call [us] }\n");
}
for (; n<maxlen; n++)
buf[n] = ' ';
buf[maxlen] = 0;
OSD_Printf(" [%s]={ %8d, %10.3f, %10.3f },\n",
buf, g_eventCalls[i], g_eventTotalMs[i],
1000*g_eventTotalMs[i]/g_eventCalls[i]);
}
for (i=0; i<MAXTILES; i++)
if (g_actorCalls[i])
{
if (!haveac)
{
haveac = 1;
OSD_Printf("\n -- actor times: [tile]={ total calls, total time [ms], {min,mean,max} time/call [us] }\n");
}
OSD_Printf(" [%5d]={ %8d, %9.3f, %9.3f, %9.3f, %9.3f },\n",
i, g_actorCalls[i], g_actorTotalMs[i],
1000*g_actorMinMs[i],
1000*g_actorTotalMs[i]/g_actorCalls[i],
1000*g_actorMaxMs[i]);
}
}
}
////////// ERROR REPORTING //////////
#define EL_MAXERRORS 20
static int32_t el_numErrors=0, el_tooMuchErrors;
static char *el_errorMsgs[EL_MAXERRORS];
int8_t el_addNewErrors = 1; // add new errors to display?
// Compare against all other error messages.
// Strictly seen, this is quadratic-time, but EL_MAXERRORS is small and
// errors should be fixed anyway.
static int32_t cmp_against_others(const char *str, int32_t slen)
{
int32_t i;
for (i=0; i<el_numErrors; i++)
if (!Bstrncmp(str, el_errorMsgs[i], slen))
return 1;
return 0;
}
LUNATIC_EXTERN void El_OnError(const char *str)
{
if (el_addNewErrors && !el_tooMuchErrors)
{
char *errstr = NULL;
const char *nl = Bstrchr(str, '\n');
// First, check whether the error message matches an already saved one
if (nl)
{
// cut off string after the newline
if (cmp_against_others(str, nl-str))
return;
}
else
{
// save string fully
if (cmp_against_others(str, Bstrlen(str)))
return;
}
// If the (EL_MAXERRORS+1)'th distinct error appeared, we have too many.
if (el_numErrors==EL_MAXERRORS)
{
el_tooMuchErrors = 1;
return;
}
// Otherwise, allocate storage for the potentially clipped error string...
if (nl)
{
errstr = (char *)Xmalloc(nl-str+1);
Bmemcpy(errstr, str, nl-str);
errstr[nl-str] = 0;
}
else
{
errstr = Xstrdup(str);
}
// ...and save it:
el_errorMsgs[el_numErrors++] = errstr;
}
}
void El_ClearErrors(void)
{
int32_t i;
for (i=0; i<EL_MAXERRORS; i++)
DO_FREE_AND_NULL(el_errorMsgs[i]);
el_numErrors = el_tooMuchErrors = 0;
}
void El_DisplayErrors(void)
{
int32_t i;
for (i=0; i<el_numErrors; i++)
printext256(8, 8+8*i, 242, 0, el_errorMsgs[i], 0);
if (el_tooMuchErrors)
printext256(8, 8+8*EL_MAXERRORS, 242, 0, "(more distinct errors ...)", 0);
}
////////// STATE CREATION/DESTRUCTIION //////////
static int our_traceback_CF(lua_State *L)
{
Bassert(lua_gettop(L)==1);
if (lua_type(L, 1)==LUA_TBOOLEAN)
{
lua_pushvalue(L, 1); // duplicate it
return 1; // and tell Lua to return it
}
Bassert(lua_type(L, 1)==LUA_TSTRING);
// call debug.traceback with the string
L_PushDebugTraceback(L);
lua_pushvalue(L, 1);
lua_call(L, 1, 1);
Bassert(lua_gettop(L)==2); // Lua will pop off args
if (g_tweakTracebackMsg)
{
// Get tweak_traceback_msg() onto the stack.
lua_pushlightuserdata(L, &g_tweakTracebackMsg);
lua_gettable(L, LUA_REGISTRYINDEX);
lua_pushvalue(L, -2); // push copy of error message string
Bassert(lua_type(L, -1)==LUA_TSTRING);
// Call tweak_traceback_msg(). CAREFUL, it's unprotected!
lua_call(L, 1, 1);
}
return 1;
}
// Registers a function: str = tweak_traceback_msg(str)
static int32_t SetTweakTracebackMsg_CF(lua_State *L)
{
Bassert(lua_gettop(L)==1);
L_CheckAndRegisterFunction(L, &g_tweakTracebackMsg);
g_tweakTracebackMsg = 1;
return 0;
}
////// Lua C-API interfaces for C game functions that may call events.
// http://www.freelists.org/post/luajit/intermitten-lua-pcall-crash-on-x86-64-linux,1
// Some of these are duplicate declarations:
#ifdef __cplusplus
extern "C" {
#endif
extern void P_AddWeaponMaybeSwitchI(int32_t snum, int32_t weap);
void P_CheckWeaponI(int playerNum);
extern int32_t A_ShootWithZvel(int32_t i, int32_t atwith, int32_t override_zvel);
extern int A_Spawn(int j, int pn);
extern void VM_FallSprite(int32_t i);
extern int32_t VM_ResetPlayer2(int32_t snum, int32_t flags);
extern void A_RadiusDamage(int32_t i, int32_t r, int32_t, int32_t, int32_t, int32_t);
extern void G_OperateSectors(int sectNum, int spriteNum);
extern void G_OperateActivators(int32_t low,int32_t snum);
int32_t A_InsertSprite(int16_t whatsect,int32_t s_x,int32_t s_y,int32_t s_z,int16_t s_pn,int8_t s_s,
uint8_t s_xr,uint8_t s_yr,int16_t s_a,int16_t s_ve,int16_t s_zv,int16_t s_ow,int16_t s_ss);
extern void A_AddToDeleteQueue(int32_t i);
extern int32_t A_PlaySound(uint32_t num, int32_t i);
void A_DeleteSprite(int spriteNum);
extern void G_ShowView(vec3_t vec, int32_t a, int32_t horiz, int32_t sect,
int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t unbiasedp);
extern void G_GameExit(const char *msg);
#ifdef __cplusplus
}
#endif
static void G_ShowViewXYZ(int32_t x, int32_t y, int32_t z, int32_t a, int32_t horiz, int32_t sect,
int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t unbiasedp)
{
vec3_t vec = { x, y, z };
G_ShowView(vec, a, horiz, sect, x1, y1, x2, y2, unbiasedp);
}
#define LARG(index) lua_tointeger(L, index)
#define ONE_ARG LARG(1)
#define TWO_ARGS LARG(1), LARG(2)
#define THREE_ARGS LARG(1), LARG(2), LARG(3)
#define CALL_WITH_RET(Name, ...) \
int32_t ret = Name(__VA_ARGS__); \
lua_pushinteger(L, ret); \
return 1
#define CALL_WITHOUT_RET(Name, ...) \
Name(__VA_ARGS__); \
return 0
#define DEFINE_RET_CFUNC(Name, ...) \
static int32_t Name##_CF(lua_State *L) \
{ \
CALL_WITH_RET(Name, __VA_ARGS__); \
}
#define DEFINE_VOID_CFUNC(Name, ...) \
static int32_t Name##_CF(lua_State *L) \
{ \
CALL_WITHOUT_RET(Name, __VA_ARGS__); \
}
// NOTE: player struct -> player index -> player struct ugliness because
// pointers to FFI cdata apparently can't be reliably passed via lua_getpointer().
// Not to mention that lua_getpointer() returns _const_ void*.
DEFINE_VOID_CFUNC(P_AddWeaponMaybeSwitchI, TWO_ARGS)
DEFINE_VOID_CFUNC(P_CheckWeaponI, ONE_ARG)
DEFINE_RET_CFUNC(A_ShootWithZvel, THREE_ARGS)
DEFINE_RET_CFUNC(A_Spawn, TWO_ARGS)
DEFINE_VOID_CFUNC(VM_FallSprite, ONE_ARG)
DEFINE_RET_CFUNC(VM_ResetPlayer2, TWO_ARGS)
DEFINE_VOID_CFUNC(A_RadiusDamage, LARG(1), LARG(2), LARG(3), LARG(4), LARG(5), LARG(6))
DEFINE_VOID_CFUNC(G_OperateSectors, TWO_ARGS)
DEFINE_VOID_CFUNC(G_OperateActivators, TWO_ARGS)
DEFINE_RET_CFUNC(A_InsertSprite, LARG(1), LARG(2), LARG(3), LARG(4), LARG(5), LARG(6),
LARG(7), LARG(8), LARG(9), LARG(10), LARG(11), LARG(12), LARG(13))
DEFINE_VOID_CFUNC(A_AddToDeleteQueue, ONE_ARG)
DEFINE_RET_CFUNC(A_PlaySound, TWO_ARGS)
DEFINE_VOID_CFUNC(A_DeleteSprite, ONE_ARG)
DEFINE_VOID_CFUNC(G_ShowViewXYZ, LARG(1), LARG(2), LARG(3), LARG(4), LARG(5), LARG(6),
LARG(7), LARG(8), LARG(9), LARG(10), LARG(11))
#define CFUNC_REG(Name) { #Name, Name##_CF }
static struct { const char *name; lua_CFunction func; } cfuncs[] =
{
CFUNC_REG(P_AddWeaponMaybeSwitchI),
CFUNC_REG(P_CheckWeaponI),
CFUNC_REG(A_ShootWithZvel),
CFUNC_REG(A_Spawn),
CFUNC_REG(VM_FallSprite),
CFUNC_REG(VM_ResetPlayer2),
CFUNC_REG(A_RadiusDamage),
CFUNC_REG(G_OperateSectors),
CFUNC_REG(G_OperateActivators),
CFUNC_REG(A_InsertSprite),
CFUNC_REG(A_Spawn),
CFUNC_REG(A_AddToDeleteQueue),
CFUNC_REG(A_PlaySound),
CFUNC_REG(A_DeleteSprite),
CFUNC_REG(G_ShowViewXYZ),
};
// Creates a global table "CF" containing the functions from cfuncs[].
static void El_PushCFunctions(lua_State *L)
{
int32_t i;
lua_newtable(L);
for (i=0; i<(signed)sizeof(cfuncs)/(signed)sizeof(cfuncs[0]); i++)
{
lua_pushstring(L, cfuncs[i].name);
lua_pushcfunction(L, cfuncs[i].func);
lua_settable(L, -3);
}
lua_setglobal(L, "CF");
}
//////
LUNATIC_CB int32_t (*El_RestoreGamevars)(const char *savecode);
static void El_StateSetup(lua_State *L)
{
luaopen_lpeg(L);
lua_pop(L, lua_gettop(L)); // pop off whatever lpeg leaves on the stack
// create misc. global functions in the Lua state
lua_pushcfunction(L, SetEvent_CF);
lua_setglobal(L, "gameevent_internal");
lua_pushcfunction(L, SetActor_CF);
lua_setglobal(L, "gameactor_internal");
lua_pushcfunction(L, SetTweakTracebackMsg_CF);
lua_setglobal(L, "set_tweak_traceback_internal");
El_PushCFunctions(L);
Bassert(lua_gettop(L)==0);
// This is for engine-side Lua:
lua_pushcfunction(L, &our_traceback_CF);
}
static void El_OnOutOfMem(void)
{
G_GameExit("Out of memory in Lunatic.");
}
// 0: success, <0: failure
int32_t El_CreateState(L_State *estate, const char *name)
{
int32_t i;
if (!g_timingInited)
{
g_timingInited = 1;
for (i=0; i<MAXTILES; i++)
g_actorMinMs[i] = 1e308;
}
L_ErrorFunc = El_OnError;
L_OutOfMemFunc = El_OnOutOfMem;
return L_CreateState(estate, name, &El_StateSetup);
}
void El_DestroyState(L_State *estate)
{
L_DestroyState(estate);
g_tweakTracebackMsg = 0;
// XXX: It would be cleaner to also clear stuff like g_elEvents[], but
// currently, when the game Lua state is recreated, the array should have
// the same values as before, so we're skipping that for now.
}
////////// Lua_CFunctions //////////
// gameevent(EVENT_..., lua_function)
static int32_t SetEvent_CF(lua_State *L)
{
int32_t eventidx;
Bassert(lua_gettop(L) == 2);
eventidx = luaL_checkint(L, 1);
Bassert((unsigned)eventidx < MAXEVENTS);
L_CheckAndRegisterFunction(L, &g_elEvents[eventidx]);
g_elEvents[eventidx] = 1;
return 0;
}
// gameactor(actortile, strength, act, mov, movflags, lua_function)
static int32_t SetActor_CF(lua_State *L)
{
int32_t actortile;
el_actor_t *a;
Bassert(lua_gettop(L) == 6);
actortile = luaL_checkint(L, 1);
Bassert((unsigned)actortile < MAXTILES);
a = &g_elActors[actortile];
L_CheckAndRegisterFunction(L, a);
// Set actor properties. They can only be nil if we're chaining actor code.
if (!lua_isnil(L, 2))
a->strength = luaL_checkint(L, 2);
if (!lua_isnil(L, 5))
a->movflags = luaL_checkint(L, 5);
if (!lua_isnil(L, 3))
Bmemcpy(&a->act, lua_topointer(L, 3), sizeof(con_action_t));
if (!lua_isnil(L, 4))
Bmemcpy(&a->mov, lua_topointer(L, 4), sizeof(con_move_t));
a->haveit = 1;
return 0;
}
//////////////////////////////
static int32_t call_regd_function3(lua_State *L, void *keyaddr,
int32_t spriteNum, int32_t playerNum, int32_t lDist)
{
#if !defined NDEBUG
const int32_t top = lua_gettop(L);
#endif
lua_pushcfunction(L, &our_traceback_CF);
// get the Lua function from the registry
lua_pushlightuserdata(L, keyaddr);
lua_gettable(L, LUA_REGISTRYINDEX);
lua_pushinteger(L, spriteNum);
lua_pushinteger(L, playerNum);
lua_pushinteger(L, lDist);
// -- call it! --
{
const int32_t i = lua_pcall(L, 3, 0, -5);
const int32_t haveerr = (i != 0);
Bassert(lua_iscfunction(L, -1-haveerr));
lua_remove(L, -1-haveerr);
Bassert(lua_gettop(L) == top+haveerr);
return i;
}
}
static int32_t g_eventIdx = 0;
static void El_EventErrorPrint(const char *errmsg)
{
OSD_Printf(OSD_ERROR "event \"%s\" runtime error: %s\n",
EventNames[g_eventIdx], errmsg);
}
int32_t El_CallEvent(L_State *estate, int32_t eventidx, int32_t spriteNum, int32_t playerNum, int32_t lDist, int32_t *iReturn)
{
// XXX: estate must be the one where the events were registered...
// make a global?
lua_State *const L = estate->L;
int32_t i;
const int32_t o_RETURN = g_RETURN;
g_RETURN = *iReturn;
g_elCallDepth++;
i = call_regd_function3(L, &g_elEvents[eventidx], spriteNum, playerNum, lDist);
g_elCallDepth--;
*iReturn = g_RETURN;
g_RETURN = o_RETURN;
if (i != 0)
{
g_elEventError = 1;
g_eventIdx = eventidx;
return L_HandleError(L, i, &El_EventErrorPrint);
}
return 0;
}
static int32_t g_actorTile, g_iActor;
static void El_ActorErrorPrint(const char *errmsg)
{
OSD_Printf(OSD_ERROR "actor %d (sprite %d) runtime error: %s\n",
g_actorTile, g_iActor, errmsg);
}
int32_t El_CallActor(L_State *estate, int32_t actortile, int32_t spriteNum, int32_t playerNum, int32_t lDist)
{
lua_State *const L = estate->L;
int32_t i;
g_elCallDepth++;
i = call_regd_function3(L, &g_elActors[actortile], spriteNum, playerNum, lDist);
g_elCallDepth--;
if (i != 0)
{
g_actorTile = actortile;
g_iActor = spriteNum;
return L_HandleError(L, i, &El_ActorErrorPrint);
}
return 0;
}

View file

@ -1,62 +0,0 @@
/* The Lunatic Interpreter, part of EDuke32. Game-side stuff. */
#ifndef EDUKE32_LUNATIC_H_
#define EDUKE32_LUNATIC_H_
#include "lunatic.h"
#include "events_defs.h" // MAXEVENTS
#include "actors.h" // con_move_t, con_action_t
#ifdef __cplusplus
extern "C" {
#endif
extern L_State g_ElState;
// actor initialization data
typedef struct
{
con_move_t mov;
int16_t movflags;
int16_t strength;
con_action_t act;
uint8_t haveit; // shouldn't be used directly
} el_actor_t;
extern uint8_t g_elEvents[MAXEVENTS]; // shouldn't be used directly
extern el_actor_t g_elActors[MAXTILES];
extern int32_t g_elEventError;
// -- functions --
void El_PrintTimes(void);
void El_ClearErrors(void);
void El_DisplayErrors(void);
int32_t El_CreateState(L_State *estate, const char *name);
void El_DestroyState(L_State *estate);
int32_t El_CallEvent(L_State *estate, int32_t eventidx, int32_t spriteNum, int32_t playerNum, int32_t lDist, int32_t *iReturn);
int32_t El_CallActor(L_State *estate, int32_t actortile, int32_t spriteNum, int32_t playerNum, int32_t lDist);
extern int8_t el_addNewErrors; // add new errors to display?
void El_OnError(const char *str);
extern int32_t (*El_RestoreGamevars)(const char *savecode);
static inline int32_t El_HaveEvent(int32_t eventidx) { return g_elEvents[eventidx]!=0; }
static inline int32_t El_HaveActor(int32_t actortile) { return g_elActors[actortile].haveit!=0; }
typedef struct {
uint32_t x, y, z, c;
} rng_jkiss_t;
uint32_t rand_jkiss_u32(rng_jkiss_t *s);
double rand_jkiss_dbl(rng_jkiss_t *s);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -1,85 +0,0 @@
local ffi = require("ffi")
local ffiC = ffi.C
local sector = sector
local inside = inside
local math = require("math")
local xmath = require("xmath")
local stat = require("stat")
local function resetseed()
math.randomseed(834572183572)
end
local function getmapbounds()
local inf = 1/0
local min = { x=inf, y=inf }
local max = { x=-inf, y=-inf }
for i=0,ffiC.numsectors-1 do
for w in wallsofsect(i) do
local wal = wall[w]
min.x = math.min(wal.x, min.x)
max.x = math.max(wal.x, max.x)
min.y = math.min(wal.y, min.y)
max.y = math.max(wal.y, max.y)
end
end
return min, max
end
local function getpoints(n, min, max)
local posns, sects = {}, {}
resetseed()
for i=1,n do
local x = math.random(min.x, max.x)
local y= math.random(min.y, max.y)
posns[i] = xmath.vec2(x, y)
sects[i] = math.random(0, ffiC.numsectors-1)
end
return posns, sects
end
-- Compare 'inside' implementations
--
-- N: number of calls
function compinside(n)
if (type(n) ~= "number") then
error("N must be a number")
end
local sti = stat.new()
local sts = stat.new()
local min, max = getmapbounds()
local posns, sects = getpoints(n, min, max)
local isi, iss = {}, {}
for i=1,n do
local t = ffiC.gethiticks()
isi[i] = inside(posns[i], sects[i])
t = ffiC.gethiticks()-t
sti:add(t)
local t = ffiC.gethiticks()
iss[i] = sector[sects[i]]:contains(posns[i])
t = ffiC.gethiticks()-t
sts:add(t)
-- if (isi[i]~=iss[i]) then
-- print("unequal: "..i.." "..sects[i].." "..posns[i].x.." "..posns[i].y.." ("..tostring(isi[i])..","..tostring(iss[i])..")")
-- end
assert(isi[i]==iss[i])
end
print("====================")
print("inside(): " .. sti:getstatstr())
print("contains(): " .. sts:getstatstr())
end

View file

@ -1,119 +0,0 @@
-- Random walker as updatesector() timing test, for Lunatic-m32
-- Load with: lua "require 'randwalk'"
local ffi = require "ffi"
local ffiC = ffi.C
local xmath = require "xmath"
local stat = require "stat"
-- set to nil to disable saving positions
g_posns = {}
local USF_BREADTH = sector.UPDATE_FLAGS.BREADTH
local function updatesectorbreadth(pos, sectnum)
return updatesector(pos, sectnum, USF_BREADTH)
end
-- [STATS, TIMES] = RANDWALK(N, SPRITENUM, MINLEN, MAXLEN [, RANDOFS [, FUNCI [, LOGFN]]])
function randwalk(N, spritenum, minlen, maxlen, randofs, funci, logfn)
-- Set the random seed to an arbitrary but fixed value so that the "random" walk
-- is deterministically reproducible for particular arguments.
math.randomseed(834572183572)
randofs = randofs or 0
for i=1,randofs do
math.random()
end
local funcnames = { "updatesector", "updatesectorbreadth", "updatesectorz" }
local funcs = { updatesector, updatesectorbreadth, updatesectorz }
local updatesectorfunc = funcs[funci]
-- stats
local st = { all=stat.new(), succ=stat.new(), fail=stat.new() }
local times = {}
local successp = {}
local pos = xmath.vec3(sprite[spritenum])
local sectnum = sprite[spritenum].sectnum
for i=1,N do
--[[
-- get random integers for the displacement
local ax,ay,az = math.random(minlen,maxlen), math.random(minlen,maxlen), math.random(minlen,maxlen)
-- random signs
local sx,sy,sz = math.random(0,1), math.random(0,1), math.random(0,1)
sx, sy, sz = 2*sx-1, 2*sy-1, 2*sz-1 -- {0,1} -> {-1,1}
ax, ay, az = sx*ax, sy*ay, sz*az
--]]
---[[
local ang = 2*math.pi*math.random()
local len = math.random(minlen, maxlen)
local ax, ay, az = len*math.cos(ang), len*math.sin(ang), 0
--]]
local newpos = pos + xmath.ivec3(ax,ay,az)
local t = ffiC.gethiticks()
local newsect = updatesectorfunc(newpos, sectnum)
t = ffiC.gethiticks()-t
st.all:add(t)
times[i] = t
successp[i] = (newsect >= 0)
if (newsect >= 0) then
st.succ:add(t)
pos = newpos
sectnum = newsect
else
st.fail:add(t)
end
if (g_posns ~= nil) then
g_posns[i] = pos
end
end
local stallres = st.all:getstats()
print("====================")
printf("Random %s walk starting with sprite %d",
funcnames[funci], spritenum)
printf("Hop length: [%d .. %d], RNG offset: %d, %s",
minlen, maxlen, randofs, logfn)
printf("-- Times [ms] (total=%f)", N*stallres.mean)
print("All: ".. tostring(stallres))
print("Succeeded: ".. st.succ:getstatstr())
print("Failed: ".. st.fail:getstatstr())
if (logfn ~= nil) then
local logfile, errmsg = io.open(logfn, "w")
if (logfile == nil) then
printf("Couldn't open log file \"%s\" for writing: %s",
logfn, errmsg)
else
logfile:write(" \t \t \t\t\n")
for i=1,N do
if (g_posns ~= nil) then
logfile:write(string.format("%s %d %d %d %d\n",
tostring(times[i]), successp[i] and 1 or 0,
g_posns[i].x, g_posns[i].y, g_posns[i].z))
else
logfile:write(string.format("%s %d\n", tostring(times[i]), successp[i] and 1 or 0))
end
end
logfile:close()
end
end
return st, ts
end

View file

@ -1,63 +0,0 @@
#!/usr/bin/env luajit
local stat = require "stat"
if (#arg ~= 2) then
print("Usage: profdemo.lua <eduke32> <demo_arg>")
print("Example: ./profdemo.lua ../../eduke32 -d3:1,8")
print(" (for 8 repetitions of demo 3 at 1 frame per gametic)")
print("")
os.exit(1)
end
local eduke32 = arg[1]
local demoarg = arg[2]
local numrepstr = demoarg:match(",[0-9]+$")
local numreps = tonumber(numrepstr and numrepstr:sub(2)) or 1
local st = { game=stat.new(), drawrooms=stat.new(), drawrest=stat.new() }
local stperx = { game=stat.new(), drawrooms=stat.new(), drawrest=stat.new() }
local unit, unitperx = {}, {}
for i=1,numreps do
local fh = io.popen(eduke32.." "..demoarg)
while (true) do
local str = fh:read("*l")
if (str == nil) then
break
end
local NUMRE = "([0-9]+%.[0-9]+)"
local UNITRE = "[mu]?s"
local RE = "(== demo [0-9]+ ([a-z]+) times: "..NUMRE.." ("..UNITRE..") %("..NUMRE.." ("..UNITRE.."/[a-z]+)%))"
local wholematch, whatstr, time1, unit1, time2, unit2 = str:match(RE)
if (wholematch ~= nil) then
if (numreps==1) then
print(wholematch)
else
st[whatstr]:add(tonumber(time1))
stperx[whatstr]:add(tonumber(time2))
unit[whatstr] = unit1
unitperx[whatstr] = unit2
end
end
end
end
if (numreps > 1) then
local keys = { "game", "drawrooms", "drawrest" }
for i=1,#keys do
local key = keys[i]
if (unit[key] ~= nil) then
print("== "..key.." times:")
print(" "..st[key]:getstatstr().." ["..unit[key].."]")
print(" "..stperx[key]:getstatstr().." ["..unitperx[key].."]")
end
end
end

View file

@ -1,91 +0,0 @@
-- Pseudo random number generation module for Lunatic
local ffi = require("ffi")
local ffiC = ffi.C
local rawset = rawset
local type = type
local decl = decl -- comes from above (_defs_game.lua)
local print = print -- for commented out debug block in new() below
module(...)
-- PRNG state struct
ffi.cdef[[
typedef struct {
uint32_t x, y, z, c;
} rng_jkiss_t;
typedef union { unsigned char u[16]; double d[2]; } uchar_double_u_t;
]]
-- PRNG functions
decl[[
uint32_t rand_jkiss_u32(rng_jkiss_t *s);
double rand_jkiss_dbl(rng_jkiss_t *s);
uint32_t Bcrc32(const void* data, size_t length, uint32_t crc);
]]
local function get_rand_u32(tin)
tin.d[0] = ffiC.gethiticks() % 1
tin.d[1] = ffiC.gethiticks() % 1
return ffiC.Bcrc32(tin.u, 16, 0)
end
local mt = {
__tostring = function(s)
return "rand.new("..s.x..","..s.y..","..s.z..","..s.c..")"
end,
__index = {
getu32 = ffiC.rand_jkiss_u32,
getdbl = ffiC.rand_jkiss_dbl,
-- Initialize the JKISS PRNG using the CRC32 of the result of several
-- profiling timer calls interpreted as byte sequence.
init_time_md4 = function(s)
local tin = ffi.new("uchar_double_u_t")
local tout = ffi.new("uint32_t [4]")
repeat
s.y = get_rand_u32(tin)
until (s.y ~= 0) -- y must not be zero!
s.x = get_rand_u32(tin)
s.z = get_rand_u32(tin)
s.c = get_rand_u32(tin) % 698769068 + 1 -- Should be less than 698769069
end,
},
}
local jkiss = ffi.metatype("rng_jkiss_t", mt)
function new(x,y,z,c)
local s
if (x == nil or type(x)=="boolean") then
-- initialize with arbitrary but fixed state
s = jkiss(123456789, 987654321, 43219876, 6543217)
if (x) then
s:init_time_md4()
end
else
s = jkiss(x,y,z,c)
end
--[[
local r=ffi.new("rng_jkiss_t")
r.x = 123456789; r.y = 987654321; r.z = 43219876; r.c = 6543217;
local N = 1e7
local t=ffiC.gethiticks()
for i=1,N do
ffiC.rand_jkiss_dbl(r)
end
print("RANDGEN TIME: "..ffiC.gethiticks()-t) -- x86_64: approx. 100 ms
--]]
return s
end

View file

@ -1,103 +0,0 @@
-- Statistics module for Lunatic.
local ffi = require("ffi")
local math = require("math")
local string = require("string")
local tostring = tostring
module(...)
ffi.cdef[[
typedef struct {
double n;
// vvv internal vvv
double m, s;
double min, max;
} runningstat_t;
typedef struct {
const double n;
const double mean, var, std;
const double min, max;
} runningstat_res_t;
]]
local NaN = 0/0
local res_mt = {
__tostring = function(s)
return
string.format("N=%d; mean=%.5g, std=%.5g; min=%.5g, max=%.5g",
s.n, s.mean, s.std, s.min, s.max)
end
}
local rstatres = ffi.metatype("runningstat_res_t", res_mt)
local mt = {
__tostring = function(s)
-- XXX: with this and the other serializing of ffi types, take care of
-- NaN and Infs when reading back (e.g. by "nan=0/0" in that context)
return "stat.new("..s.n..","..s.m..","..s.s..","..s.min..","..s.max..")"
end,
-- See: Accurately computing running variance, by John D. Cook
-- http://www.johndcook.com/standard_deviation.html
__index = {
add = function(s, num)
if (s.n > 0) then
-- N>0, recurrence
s.n = s.n+1
local lastm = s.m
s.m = lastm + (num-lastm)/s.n
s.s = s.s + (num-lastm)*(num-s.m)
if (num < s.min) then s.min = num end
if (num > s.max) then s.max = num end
else
-- N==0, initialization
s.n = 1
s.m = num
s.s = 0
s.min = num
s.max = num
end
end,
getstats = function(s)
local var = s.n > 1 and s.s/(s.n-1) or NaN
return rstatres(s.n, s.m, var, math.sqrt(var), s.min, s.max)
end,
getstatstr = function(s)
return tostring(s:getstats())
end,
reset = function(s)
s.n = 0
s.m, s.s, s.min, s.max = NaN, NaN, NaN, NaN
end,
},
}
local rstat = ffi.metatype("runningstat_t", mt)
function new(n,m,s, min,max)
if (n == nil) then
-- initialization containing no elements
return rstat(0, NaN, NaN, NaN, NaN)
elseif (m == nil) then
-- same as initialization with N==0 above (one element)
return rstat(1, n, 0, n, n)
else
-- generic initialization (internal use only)
return rstat(n,m,s, min,max)
end
end

View file

@ -1,44 +0,0 @@
--
-- strict.lua
-- checks uses of undeclared global variables
-- All global variables must be 'declared' through a regular assignment
-- (even assigning nil will do) in a main chunk before being used
-- anywhere or assigned to inside a function.
--
local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget
local mt = getmetatable(_G)
if mt == nil then
mt = {}
setmetatable(_G, mt)
end
mt.__declared = {}
local function what ()
local d = getinfo(3, "S")
return d and d.what or "C"
end
mt.__newindex = function (t, n, v)
if not mt.__declared[n] then
local w = what()
if w ~= "main" and w ~= "C" then
error("assign to undeclared variable '"..n.."'", 2)
end
mt.__declared[n] = true
end
rawset(t, n, v)
end
-- PK: change from original (undeclared-refs in main allowed):
mt.__index = function (t, n)
if not mt.__declared[n] then
local w = what()
if w ~= "main" and w ~= "C" then
error("variable '"..n.."' is not declared", 2)
end
end
return rawget(t, n)
end

View file

@ -1,906 +0,0 @@
-- test script for ELua/Lunatic Interpreter
-- error=nil -- must not affect "require"
local require = require
local string = require("string")
local bit = require("bit")
local math = require("math")
local pcall = pcall
local DBG_ = require("_LUNATIC_DBG")
local gv, sector, wall, sprite, spriteext = gv, sector, wall, sprite, spriteext
local actor, player, projectile = actor, player, projectile
local gameevent, gameactor = gameevent, gameactor
local spritesofsect = spritesofsect
local hitscan = hitscan
local assert, error, print, tostring = assert, error, print, tostring
local D = require("CON.DEFS")
print('---=== ELua Test script ===---')
local function printf(fmt, ...)
print(string.format(fmt, ...))
end
local function checkfail(funcstr, expectedmsg)
if (DBG_ == nil) then
return
end
local status, errmsg = pcall(DBG_.loadstring(funcstr))
if (status) then
print('^21ERROR:^O '..funcstr.." DIDN'T fail")
else
if (expectedmsg==nil or string.find(errmsg, expectedmsg, 1, true)) then
print("^11SUCCESS:^O "..funcstr.." failed: "..errmsg)
else
-- XXX: beginning with "^10" is counted as error in OSD_Printf()
print("^10ERROR*:^O "..funcstr.." failed: "..errmsg..
", but expected error message was: "..expectedmsg)
end
end
end
gameevent
{
"ENTERLEVEL",
function()
local vol, lev = gv.currentEpisode(), gv.currentLevel()
printf('volume=%d, level=%d', vol, lev)
if (vol ~= 4) then
-- Tweak some sector pals.
print('tweaking sector pals')
print('numsectors: ' .. gv.numsectors .. ' of ' .. gv.MAXSECTORS)
local SF = sector.STAT
for i = 0, gv.numsectors/2 do
local sec = sector[i]
sec.floorpal = 1;
sector[i].floor.shade = sec.floor.shade + 4
sector[i].ceilingpal = 2;
local ceil = sec.ceiling
ceil.shade = sector[i].ceiling.shade + 8
ceil.statbits:flip(SF.SMOOSH)
sec.floorstatbits:flip(SF.SWAPXY)
end
end
if (vol==1 and lev==1) then -- E1L1
print('tweaking some sprites 2')
local i = 562
spriteext[i].alpha = 0.5;
sprite[i].cstat = bit.bor(sprite[i].cstat, 2+512);
spriteext[i].pitch = 128;
spriteext[i].roll = 256;
i = 107 -- pistol ammo at rooftop
spriteext[i].pitch = 128;
spriteext[i].roll = 256;
for spr in spritesofsect(307) do -- some fence sprites in E1L1
printf('spr %d', spr)
sprite[spr].pal = 6
end
actor[562].flags = bit.bor(actor[562].flags, 2); -- pal 6 with goggles on front SEENINE
end
---[[
if (vol==1 and lev==8) then
local havebunch = false
for i=0,gv.numsectors-1 do
havebunch = havebunch or (sector[i].ceilingbunch >= 0)
end
if (havebunch) then
print('tweaking bunch 1');
-- trueror1.map
for i, what in sectorsofbunch(1, gv.BOTH_CF) do
sector[i][what].z = sector[i][what].z - 3*1024;
end
end
end
--]]
end
}
gameevent
{
"JUMP",
function()
print("tweaking forcefield with lotag 34 (E2L1)")
for w=0,gv.numwalls-1 do
local wal = wall[w]
if (wal.overpicnum == D.W_FORCEFIELD or wal.overpicnum == D.W_FORCEFIELD+1) then
if (wal.lotag==34) then
wal.cstat = wal.cstatbits:test(85) and 0 or 85
end
end
end
end
}
local unsafe = pcall(function() string.UNSAFE=true; end)
checkfail("tostring = nil", "attempt to write into the global environment")
--DBG_.printkv('_G in test.elua', _G)
-- direct gv array access forbidden
checkfail('gv.sprite[0].yrepeat = 100', "access forbidden")
checkfail('print(sprite[100000].ceilingpal)', "out-of-bounds sprite[] read access")
checkfail('print(gv.sprite[0])', "access forbidden")
-- set metatable forbidden
checkfail('setmetatable(sprite, {})', "attempt to read undeclared variable 'setmetatable'")
gameevent
{
"ENTERLEVEL",
function()
-- OOB write access.
-- Note that indexing ("reading") sector fails, even if the user wants to
-- assign to a sector member. Potentially confusing error message.
checkfail('sector[-1].ceilingpal = 4', "out-of-bounds sector[] read access")
-- wallnum member is read-only
checkfail('sector[0].wallnum = 0', "attempt to write to constant location") -- this comes from LJ/FFI
-- direct sector write access forbidden
checkfail('sector[4] = sector[6]', "cannot write directly to sector[]")
-- creating new keys forbidden... handled by LuaJIT
checkfail('wall[4].QWE = 123', "has no member named 'QWE'")
-- no pointer arithmetic!
checkfail('local spr = sprite[0]; local x=spr+1', "attempt to perform arithmetic on")
-- actor[].t_data[] is not accessible for now
checkfail('local i = actor[0].t_data[15]', "has no member named 't_data'")
-- sprite.picnum may happen as a thinko/typo kind of error (spr.picnum was meant)
checkfail("local pic = sprite.picnum", "invalid access to static data")
checkfail("require('engine').setshadetab(200, nil)",
"setshadetab() may be run only while LUNATIC_FIRST_TIME is true")
checkfail("sprite[0]:set_picnum(-10)", "invalid tile number")
end
}
-- gv.numsectors is read-only
checkfail('gv.numsectors = 4', "attempt to write to constant location")
-- cannot create new fields in 'gv'
checkfail('gv.QWE = 4', "write access forbidden")
-- that would be horrible...
checkfail('sprite._nextspritesect[4] = -666', "cannot write directly to nextspritesect[]")
-- we're indexing a plain array!
checkfail('print(sprite._nextspritesect[4].whatfield)', "attempt to index a number value")
-- our 'require' has only safe stuff
--checkfail("require('os')")
-- gamevars are created using a special different mechanism
checkfail("new_global = 345", "attempt to write into the global environment")
-- Can't reassign to existing vars either
assert(actor ~= nil)
checkfail("actor = 345", "attempt to write into the global environment")
-- can't redefine constants in 'gv'
checkfail('gv.CEILING = 3', "attempt to write to constant location")
-- string.dump is unavailable
checkfail('local s=require[[string]]; local tmp=s.dump(gameevent)',
"attempt to call field 'dump' (a nil value)")
if (not unsafe) then
-- changing base module tables is disallowed
checkfail('local s=require[[string]]; s.format=nil', "modifying base module table forbidden")
else
print('WARNING: RUNNING WITH UNPROTECTED BASE MODULES')
end
print('')
-- This is problematic, even though pretty much every access will yield a
-- "missing declaration" error.
-- See http://luajit.org/ext_ffi_api.html#ffi_C about what stuff ffi.C contains.
checkfail('gv.luaJIT_setmode(nil, 0, 0)', "missing declaration for symbol 'luaJIT_setmode'")
checkfail('gv.luaJIT_BC_con_lang', "attempt to call a nil value")
checkfail('gv.gethiticks = nil', "attempt to write to constant location")
checkfail('gameactor{1680, 0}', "must provide a function with last numeric arg or .func")
checkfail("do local bt=require'test.test_bitar'; bt.QWE=1; end", "modifying module table forbidden")
-- the cdata returned by player[] can't be made into a pointer!
checkfail("do local pl=player[0]; i=pl[1]; end")
checkfail("do local ud=gv.ud.camerasprite; end", "access forbidden") -- test for proper decl()
checkfail("gv.g_sizes_of=nil; print(gv.g_sizes_of[0])", "write access forbidden")
checkfail("gv.cam.sect=-1", "invalid sector number")
checkfail("local flag=gv.SFLAG_NULL", "missing declaration")
-- NOTE: player[0] is forbidden at file scope, this is just for testing purposes.
player[0].wackedbyactor = -1 -- should succeed
checkfail("player[0].curr_weapon = -1", "Invalid weapon ID")
player[0].curr_weapon = 1
checkfail("local w = player[0].weapon[-1]", "out-of-bounds weapon read access")
-- XXX: This gives a very strange error message: "attempt to write to constant location". Why?
-- (note how I forgot to index weapon with a weapon index or name)
--player[0].weapon.firesound = 1e5
checkfail("player[0].weapon.SHOTGUN.firesound = 1e5", "invalid sound number")
checkfail("player[0].weapon.SHOTGUN.firesound = 0/0", "must be a non-NaN number")
checkfail("player[0].weapon.SHOTGUN.firesound = 1/0", "invalid sound number")
checkfail("gameactor{1680, action=require('con').action{numframes=-10}, function() end}",
"action has negative number of frames")
-- NOTE: It should only be relied on that setting e.g. .firesound to -1 sets it
-- to 0, not other negative values.
player[0].weapon.SHOTGUN.firesound = -1/0
assert(player[0].weapon.SHOTGUN.firesound == 0)
gameevent{gv.EVENT_JUMP,
function(actori, playeri, dist)
printf("jump i=%d p=%d d=%d", actori, playeri, dist)
error("greetings from EVENT_JUMP")
end
}
--[[
gameevent
{
"PROCESSINPUT",
-- Input test.
-- NOTE: I don't think that exposing g_player[].sync (aka "input") is a good idea...
func = function(actori, playeri, dist)
local IB = player._INPUT_BITS
local input = player[playeri]._input
if (bit.band(input.bits, IB.JUMP) ~= 0) then
print("JUMPED")
-- ... because for example this doesn't work
-- (P_HandleSharedKeys, where the JETPACK bit is tested, runs
-- before P_ProcessInput):
input.bits = bit.bor(input.bits, IB.JETPACK)
end
end
}
--]]
local WEAPON = gv.WEAPON
-- Bad hack to test out pitch/roll: EVENT_GAME is discouraged from Lunatic.
local PITCH_PICNUM = { [D.SEENINE]=true, }
gameevent{ "GAME",
function(aci)
local spr = sprite[aci]
if (PITCH_PICNUM[spr.picnum]) then
local height = spr:getheightofs()
local sexy = spriteext[aci]
sexy.pitch = gv.totalclock
sexy.mdoff.x = -height/16 -- test xoff + pitch
sexy.mdoff.z = -height -- test zoff + pitch
-- Test roll + yoff
sexy.roll = gv.totalclock
sexy.mdoff.y = -height/16
end
end
}
-- test event chaining
gameevent
{
"JUMP",
flags = actor.FLAGS.chain_beg,
function(actori, playeri, dist)
local ps = player[playeri]
print("\n--- I'm first!")
-- DBG_.oom()
local pistol = ps.weapon.PISTOL
if (pistol.shoots ~= D.RPG) then
pistol.shoots = D.RPG
else
pistol.shoots = D.SHOTSPARK1
end
ps.weapon[WEAPON.PISTOL].firesound = D.LIGHTNING_SLAP
-- This succeeds, because sound2time is a time, not a sound.
ps.weapon.SHOTGUN.sound2time = 5000
printf("set shotgun's sound2time to %d", ps.weapon.SHOTGUN.sound2time)
-- Set pipebomb and tripbomb to timed mode.
-- XXX: Provide either named constants or methods?
-- XXX: These are probably reset to default on new game.
ps.pipebombControl = 2
ps.tripbombControl = 2
-- Test of INTERNAL member _pals.
-- NOTE: setting colors partially is bad! E.g. after an item is
-- picked up, col[0] and col[1] remain and tint everything greenish.
if (DBG_ ~= nil) then
ps._pals[2] = 20
ps._pals.f = 30
end
end
}
local xmath = require "xmath"
gameevent
{
gv.EVENT_ENTERLEVEL,
function()
if (DBG_ ~= nil) then
DBG_.testmembread()
end
-- NOTE: times are for helixhorned (Core2Duo 3GHz)
local i
local N = 1e6
local t = gv.gethiticks()
for i=3,N do
gv.gethiticks()
end
t = gv.gethiticks()-t
-- x86_64: 35ns/call, x86: 280 ns/call
-- Windows 32-bit: about 1 us/call?
printf("%d gethiticks() calls took %.03f ms (%.03f us/call)",
N, t, (t*1000)/N)
local sum=0
t = gv.gethiticks()
for i=1,N do sum = sum+gv.ksqrt(i) end
t = gv.gethiticks()-t
-- x86_64: 14ns/call
printf("%d ksqrt() calls took %.03f ms (%.03f us/call) [sum=%f]",
N, t, (t*1000)/N, sum)
sum=0
t = gv.gethiticks()
for i=1,N do sum = sum+math.sqrt(i) end
t = gv.gethiticks()-t
-- x86_64: 7ns/call
printf("%d math.sqrt() calls took %.03f ms (%.03f us/call) [sum=%f]",
N, t, (t*1000)/N, sum)
printf("sqrt(0xffffffff) = %f(ksqrt) %f(math.sqrt)",
gv.ksqrt(0xffffffff), math.sqrt(0xffffffff))
local pl = player[0]
-- MAX < current is "allowed"
pl.max_ammo_amount[WEAPON.RPG] = 17
pl:give_weapon(WEAPON.RPG)
pl.ammo_amount[WEAPON.RPG] = 54
pl:give_weapon(WEAPON.SHRINKER)
-- This looks much prettier:
pl.ammo_amount.SHRINKER = 2
-- MORTER2 from test/weaponvars.con
local PNUM = 1653
local proj = projectile[PNUM]
if (proj ~= nil) then
printf('Have projectile %d', PNUM)
player[0].weapon.SHOTGUN.shoots = PNUM
proj.drop = 0
proj:set_trail(D.SMALLSMOKE)
else
printf('^10Do NOT have projectile %d, test/weaponvars.con not loaded?', PNUM)
end
if (gv._LUNATIC_STRICT == 0) then
t = gv.gethiticks()
local N=1
for n=1,N do
for i=0,gv.MAXSPRITES-1 do
sprite[i].blend = 1
end
for i=gv.MAXSPRITES-1,0,-1 do
sprite[i].shade = 1
end
for i=0,gv.MAXSPRITES-1 do
sprite[i].xoffset = 0
end
for i=gv.MAXSPRITES-1,0,-1 do
sprite[i].yoffset = 1
end
end
t = gv.gethiticks()-t
printf("%d x four 0..MAXSPRITES-1 iterations took %.03f us per outer iteration", N, (1000*t)/N)
-- Results on x86:
-- N=1: 480-1000 us (too large variance)
-- N=10: 190-210 us * 10 = 1.9-2.1 ms
-- N=100: about 160 us * 100 = about 16 ms
end
-- Make the DUKECAR in E1L1 into a zombie actor (temporarily)
-- NOTE: Use static value (not the one from 'D').
if (sprite[24].picnum==2491) then
sprite.changestat(24, actor.STAT.ZOMBIEACTOR)
end
checkfail("gameevent('GAME', function() print('qwe') end)",
"must be called from top level")
-- Test vec3 + wall. Pseudo wall member 'z' will be accessed.
local mpos = xmath.vec3()
for i=0,gv.numwalls-1 do
mpos = mpos + wall[i]
end
mpos = mpos/gv.numwalls
local impos = xmath.ivec3(mpos)^20 -- test ivec3 with dvec3 arg, test '^' op
assert(impos.z == -20)
printf("Map center point: (%d,%f)", mpos.x, impos.y)
end
}
gameevent{"LOADACTOR", function(i)
local spr = sprite[i]
if (i==614 and spr.picnum==930) then
-- "police line" ribbon in E1L1
-- clear the hitag so that it doesn't spawn as FALLER from premap
-- Rather a HACK: relies on an implementation detail (A_Spawn()
-- "hard-wired" code).
spr.hitag = 0
end
end}
gameactor
{
-- "police line" ribbon
930, nil, 1,
func = function(i)
local spr = sprite[i]
local r = math.random
local d = 20
-- NOTE: __add metamethod is called because of the RHS:
local v = spr + xmath.vec3(r(-d,d), r(-d,d))
spr:setpos(v):updatesect()
-- Test vec3 constructor with cdata.
local tempvec = xmath.vec3(player[0].pos)
end
}
local stat = require("stat")
local hs = stat.new()
local con = require("con")
local AC, MV = con.AC, con.MV
local CAC, CMV, CAI = require("CON.ACTION"), require("CON.MOVE"), require("CON.AI")
assert(CAC); assert(CMV); assert(CAI)
local AC, MV = {}, {}
AC.TROOPSTAND = assert(CAC.ATROOPSTAND) -- or con.action{0,1,5,1,1}
AC.TROOPFLINTCH = con.action{50, 1, 1, 1, 6}
MV.SHRUNKVELS = con.move{hvel=32}
con.ai(AC.TROOPFLINTCH, MV.SHRUNKVELS, 0) -- unused; TODO: test
local TROOPSTRENGTH = 30
local AF = actor.FLAGS
local CS = sprite.CSTAT
-- Crosshair sprite.
-- NOTE: This ought to be a gamevar -- if a savegame is restored, a new one
-- will be spawned.
local chair
gameactor{ D.APLAYER, AF.chain_end,
function(aci, pli)
if (chair == nil) then
chair = con.spawn(555, aci)
printf("Spawned our crosshair: sprite %d", chair)
local spr = sprite[chair]
-- Set to STAT_MISC because otherwise interpolation goes crazy (old
-- value never updated; dunno why...)
sprite.changestat(chair, actor.STAT.MISC)
spr.xrepeat, spr.yrepeat = 96, 96
spr.cstatbits:set(CS.CENTER)
end
local ps = player[pli]
local ray = xmath.kangvec(ps.ang, -(ps.horiz-100)*2048)
local hit = hitscan(ps.pos, ps.cursectnum, ray, 0)
if (hit.sector >= 0) then
sprite[chair]:setpos(hit.pos)
sprite.changesect(chair, hit.sector)
end
end
}
-- Add NODAMAGEPUSH flag to NEWBEAST.
gameactor { D.NEWBEAST, AF.chain_end + AF.NODAMAGEPUSH, function() end }
-- Also test actor code chaining: strength is doubled.
gameactor
{
D.LIZTROOP, AF.chain_end+AF.enemy, 2*TROOPSTRENGTH,
action = AC.TROOPSTAND,
func = function(i, playeri, dist)
sprite[i].pal = math.random(32)
-- sprite[i].ang = bit.band(sprite[i].ang-20, 2047)
local spr = sprite[i]
local t = gv.gethiticks()
local hit = hitscan(spr, spr.sectnum, {x=10, y=10, z=0}, gv.CLIPMASK0)
hs:add(1000*(gv.gethiticks()-t))
if (hs.n == 300) then
printf("hitscan: %s", hs:getstatstr())
hs:reset()
error("greetings from LIZTROOP actor")
end
local actr = actor[i]
if (actr:get_count() % 30 == 0) then
spr.cstatbits:flip(CS.YFLIP)
end
-- Test of bitint's ":test()" for actor[].flags.
actr.flagsbits:test(AF.NVG)
if (dist < 4096) then
-- Duke Vader / Anakin Nukewalker?
actor[i]:set_action(AC.TROOPFLINTCH)
actor[i]:set_move(MV.SHRUNKVELS)
if (dist < 1024) then
con.killit()
end
end
if (actr:has_action(CAC.ATROOPWALKING)) then
if (actr:get_count() % 50 == 0) then
actr.movflagsbits:flip(actor.MOVFLAGS.spin)
end
end
end,
-- NOTE: the animate callback is not yet documented and thus not official API!
animate = function(tspr)
local tspr2 = tspr:dup()
if (tspr2) then
tspr2.x = tspr2.x + 512*math.cos(gv.totalclock/60)
tspr2.y = tspr2.y + 512*math.sin(gv.totalclock/60)
tspr2.cstatbits:set(CS.TRANS_BITMASK)
end
-- XXX: inserted tsprites have floor shadow in classic! (r_shadow)
-- G_DoSpriteAnimations() is passed as callback to the engine on occasion,
-- in other words, created tsprites may be fed back to G_DoSpriteAnimations()!
-- classic: shows shadow for both "ghost" liztroop and aim "reticle"
-- Polymost: only for "ghost"
-- Polymer: none
local aimv = 256*xmath.bangvec(tspr.ang)
local hit = hitscan(tspr^(16*256), tspr.sectnum, aimv, gv.CLIPMASK1)
if (hit.wall >= 0) then
local aimtspr = tspr:dup()
if (aimtspr) then
aimtspr.pal = 2
aimtspr:set_picnum(555)
aimtspr:setpos(hit.pos, hit.sector)
end
end
end,
}
gameactor
{
1275, -- newspaper, E4L6 sprite #513
action = con.action{0, 4, delay=20}, -- Same as {0, 4, 1, 1, 20}
move = 1, -- so that the ID is nonzero and it will move
func = function(aci)
local a = actor[aci]
local delay = math.sin(0.1 * 2*math.pi*gv.totalclock/120)
a:set_action_delay(20 + 10*delay)
if (sprite[aci].pal ~= 0) then
a:set_hvel(1024/30)
a:set_vvel(-1024/30)
else
a:set_hvel(50*delay)
end
a.movflags = actor.MOVFLAGS.geth + actor.MOVFLAGS.getv
end,
}
gameevent
{
"DISPLAYROOMS",
function(aci, pli)
local ps = player[pli]
local cam = gv.cam
if (ps.newowner < 0) then
cam.pos.z = cam.pos.z + 64*16*math.sin(gv.totalclock/30)
end
if (ps.cursectnum >= 0) then
local hit = sector[ps.cursectnum]:zrangeat(cam.pos)
if (gv.totalclock%200==0) then
printf("hit %s %d at z=%d, %s %d at z=%d",
hit.c.spritep and "sprite" or "sector", hit.c.num, hit.c.z,
hit.f.spritep and "sprite" or "sector", hit.f.num, hit.f.z)
end
end
end
}
gameevent
{
"DISPLAYREST",
function()
for i=0,10 do
for j=1,100 do
-- XXX: This is slower in the Polymodes than with classic!
-- con._gametext(2822, j, i, 12, 0, 0, 16, 0,0,gv.xdim,gv.ydim,8192)
end
end
-- Clear showing every sector with the pavement floor tile. (Unless we're
-- in such a sector or an adjoining one.)
-- XXX: We need a better place to do this, maybe an event in
-- G_DisplayRest() where show2dsector[] is tweaked?
-- NOT YET OFFICIAL API.
local show2dsector = sector.showbitmap
for i=0,gv.numsectors-1 do
if (sector[i].floorpicnum==815) then
show2dsector:set0(i)
end
end
end
}
gameactor
{
D.APLAYER, actor.FLAGS.chain_beg,
function(aci, pli)
if (con._squished(aci, pli)) then
printf("Squished LunaCON test")
end
end
}
gameactor
{
-- Innocent sign, similar to test/weaponvars.con actor 909 (tree trunk)
1211, actor.FLAGS.replace,
function(aci, pli)
local a = actor[aci]
if (a:get_count() >= 120) then
local i = con.spawn(D.TRANSPORTERSTAR, aci)
sprite[i].z = sprite[i].z - 1024*16
con.shoot(D.MORTER, aci, -4096)
a:set_count(0)
end
end
}
local function testbit(num, b)
return bit.band(num,b)~=0 and 1 or 0
end
local FADE_SPEED = {
[WEAPON.KNEE] = 1/2.5,
1/128,
1/5,
1/3,
1/2,
1, -- pipebomb: ~1 sec
2,
3,
5,
127, -- freezer; such a fast fade is not visible, but it clears any
-- existing one (if of higher priority)
[WEAPON.GROW] = 9.9, -- test banker's rounding -- should be like 10
}
-- Test player[]:fadecol(), a better palfrom.
gameevent
{
"CHANGEWEAPON",
function (aci, pli)
local ps = player[pli]
local neww, curw = gv.RETURN, ps.curr_weapon
local r, g, b = testbit(neww, 1), testbit(neww, 2), testbit(neww, 4)
local speed = FADE_SPEED[neww] or 1
player[pli]:fadecol(0.5, r, g, b, speed, neww-5)
end
}
-- Time the above p:fadecol() test for verification of the <speed> argument.
local last_f, last_t = 0, 0
local last_secs = nil
gameevent
{
"DISPLAYREST",
function(aci, pli)
local ps = player[pli]
-- WARNING: _pals in INTERNAL and off-limits to users!
local curf = ps._pals.f
if (curf > last_f) then
-- Starting a tint
last_secs = nil
last_f = curf
last_t = gv.getticks()
elseif (last_t > 0 and curf==0) then
-- Fade has ended
last_secs = (gv.getticks()-last_t)/1000
last_f, last_t = 0, 0
end
if (last_secs ~= nil) then
con.minitext(10, 10, string.format("Last fade time: %.03f s (%.03f gametics)",
last_secs, last_secs*30))
end
end,
}
printf("EVENT_INIT = %d", gv.EVENT_INIT) -- tests default defines
local bittest = require "test.test_bitar"
bittest.sieve()
require("test.test_geom", 123123)
require("test.test_rotspr")
do
-- Test ksin vs. sinb
local xmath = require "xmath"
local sum = 0
local N = 1000
local t = gv.gethiticks()
for i=0,N*2048-1 do
sum = sum+xmath.ksin(i)
end
t = gv.gethiticks()-t
sum = sum*1e12
printf("ksin: sum*1e12=%.03f, %.03fus per 0-2047 cycle", sum, t)
sum = 0
local t = gv.gethiticks()
for i=0,N*2048-1 do
sum = sum+xmath.sinb(i)
end
t = gv.gethiticks()-t
sum = sum*1e12
printf("sinb: sum*1e12=%.03f, %.03fus per 0-2047 cycle", sum, t)
-- Results (helixhorned x86):
-- ksin: sum*1e12=0.000, 3.801us per 0-2047 cycle
-- sinb: sum*1e12=0.052, 2.886us per 0-2047 cycle
end
do
-- Test getflorzofslopeptr()/sector[]:floorzat()
local N = 100
local t = gv.gethiticks()
for n=1,N do
for i=0,gv.numsectors-1 do
local sec = sector[i]
local w1 = sec.wallptr
sec:floorzat(wall[w1])
end
end
printf("%d x %d floorzat: %.03f us", N, gv.numsectors, (gv.gethiticks()-t)*1000)
-- Results for 100 x 325 floorzat (helixhorned x86):
-- cdecl getflorzofslope(): 572.165 us
-- __fastcall getflorzofslope(): 830.147 us (sic, but tested only once!)
end
print('---=== END TEST SCRIPT ===---')
--[[
function check_sector_idx()
error("bla")
end
spritesofsect(0)
--]]
--DBG_.oom()
-- This will complain about wrong usage of 'error'. In particular,
-- the nil must not propagate to C!
checkfail('error(nil)', "error using 'error': error message must be a string")
local D = require("CON.DEFS")
checkfail('require("CON.DEFS").APLAYER=123', "modifying base module table forbidden")
-- Test with lunatic/test/rotfixed_actor.con.
print("DUKECAR="..tostring(D.DUKECAR))
do
print('---------- getangle test')
local function CreateGetAngFunc(roundfunc)
return function(x, y)
local ang = (1024/math.pi)*math.atan2(y, x) -- note the swapped args
return bit.band(roundfunc(ang), 2047)
end
end
local ourgetang = CreateGetAngFunc(math.ceil)
local ourgetangf = CreateGetAngFunc(math.floor)
local function printang(x, y)
printf('%4d,%4d: %13d, %16d, %16d', x, y,
gv.getangle(x, y), ourgetang(x, y), ourgetangf(x, y))
end
print " x, y: getangle(x, y) | math.atan2/ceil | math.atan2/floor"
printang(10, 100)
printang(10, -100)
printang(-10, -100)
printang(-10, 100)
printang(0, 0)
printang(1, 0)
printang(0, 1)
printang(-1, 0)
printang(0, -1)
local N=1e5
local r = 0
local t = gv.gethiticks()
for i=1,N do
r = r + gv.getangle(10, i)
end
local t1 = gv.gethiticks()-t
r = 0
t = gv.gethiticks()
for i=1,N do
r = r + ourgetang(10, i)
end
local t2 = gv.gethiticks()-t
printf('Time for %d runs: getangle: %.03f ms, math.atan2: %.03f ms', N, t1, t2)
print('----------')
end
if (LUNATIC_FIRST_TIME) then
require("test.shadexfog").test_create0()
end

View file

@ -1,109 +0,0 @@
gamevar tmp 0 0
/*
actor LIZTROOP
getactor[THISACTOR].mdflags tmp
orvar tmp 16
setactor[THISACTOR].mdflags tmp
enda
*/
gamevar ceilz 0 0
gamevar ceilhit 0 0
gamevar florz 0 0
gamevar florhit 0 0
gamevar pi 0 0
gamevar doxp 0 0
gamevar xp 0 0
define Q 500
gamevar pdist 0 2
onevent EVENT_GAME
ifactor LIZTROOP
findplayer pdist
// Test both view/wall/floor sprites for
// spriteext[].xpanning (Polymost only)
setvar doxp 0
ifactor 937 // --- vvv --- E2L1
setvar doxp 1
else ifactor FEMMAG1
setvar doxp 1
else ifactor PIGCOP
setvar doxp 1
else ifactor 849 // 64x24: in classic, better than nonpow2-x tile 937
setvar doxp 1
ifvarn doxp 0
{
getactor[THISACTOR].xpanning xp
addvar xp 1
setactor[THISACTOR].xpanning xp
}
getwall[0].blend xp // test wall[].blend
endevent
onevent EVENT_ANIMATESPRITES
ifactor LIZTROOP
{
setvarvar tmp totalclock
andvar tmp 255
settspr[THISACTOR].tsprshade tmp
}
endevent
gamevar lizi 0 0
onevent EVENT_DISPLAYREST
findnearactor LIZTROOP 4096 lizi
ifvarn lizi -1
{
redefinequote Q nearest LIZTROOP is %d units away (Manhattan dist.)
qsprintf Q Q /**/ actorvar[lizi].pdist
gametextz STARTALPHANUM 10 100 Q 2 0 16 /*bounds:*/ 0 0 xdim ydim 32768
}
getplayer[THISACTOR].i pi
// This blows up on C-CON:
getzrange sprite[pi].x sprite[pi].y sprite[pi].z player[THISACTOR].cursectnum
/*out:*/ ceilz ceilhit florz florhit
/*in:*/ 128 CLIPMASK0
redefinequote Q hit %d at %d, %d at %d
qsprintf Q Q /**/ ceilhit ceilz florhit florz
minitext 100 8 Q 0 0
gametext STARTALPHANUM 320 16 Q 0 0 16 /*bounds:*/ 0 0 xdim ydim
gametextz STARTALPHANUM 320 24 Q 0 0 16 /*bounds:*/ 0 0 xdim ydim 32768
endevent
// Following code by Fox, from
// http://forums.duke4.net/topic/955-eduke32-scripting/page__view__findpost__p__153176
gamevar SIN 0 0
gamevar COS 0 0
gamevar HORIZ 0 0
gamevar HITSECT 0 0
gamevar HITWALL 0 0
gamevar HITSPRITE 0 0
gamevar HITX 0 0
gamevar HITY 0 0
gamevar HITZ 0 0
onevent EVENT_DISPLAYREST
sin SIN player[THISACTOR].ang
cos COS player[THISACTOR].ang
setvar HORIZ 100
subvarvar HORIZ player[THISACTOR].horiz
subvarvar HORIZ player[THISACTOR].horizoff
mulvar HORIZ 32
hitscan player[THISACTOR].posx player[THISACTOR].posy player[THISACTOR].posz player[THISACTOR].cursectnum
COS SIN HORIZ HITSECT HITWALL HITSPRITE HITX HITY HITZ CLIPMASK1
redefinequote Q hitscan: hit sector %d, wall %d, sprite %d
qsprintf Q Q /**/ HITSECT HITWALL HITSPRITE
minitext 100 80 Q 0 0
endevent

View file

@ -1,86 +0,0 @@
// Inputs to "state checknearwall".
gamevar wd 128 0 // wall distance
gamevar cfd 1024 0 // ceiling/floor distance
// Outputs of "state checknearwall".
gamevar hitwall -1 0
// Temporary variables.
gamevar tmp 0 0
gamevar ret 0 0
gamevar x 0 0
gamevar y 0 0
gamevar z 0 0
gamevar sectnum 0 0
define CM_BLK_WAL 1
state getcoords
setvarvar x sprite[THISACTOR].x
setvarvar y sprite[THISACTOR].y
setvarvar z sprite[THISACTOR].z
setvarvar sectnum sprite[THISACTOR].sectnum
ends
// Inputs: (wd, cfd)
// Returns: hitwall
// -1: hit no wall
// >=0: hit that wall
state checknearwall
setvar hitwall -1
setvarvar tmp wd, shiftvarl tmp 14
state getcoords
clipmovenoslide ret (x, y, z, sectnum) (tmp, 0) (wd, cfd, cfd) CM_BLK_WAL
ifvarn ret 0 { setvarvar hitwall ret, subvar hitwall 32768, break }
state getcoords
clipmovenoslide ret (x, y, z, sectnum) (0, tmp) (wd, cfd, cfd) CM_BLK_WAL
ifvarn ret 0 { setvarvar hitwall ret, subvar hitwall 32768, break }
mulvar tmp -1
state getcoords
clipmovenoslide ret (x, y, z, sectnum) (tmp, 0) (wd, cfd, cfd) CM_BLK_WAL
ifvarn ret 0 { setvarvar hitwall ret, subvar hitwall 32768, break }
state getcoords
clipmovenoslide ret (x, y, z, sectnum) (0, tmp) (wd, cfd, cfd) CM_BLK_WAL
ifvarn ret 0 { setvarvar hitwall ret, subvar hitwall 32768, break }
ends
////////// Example usage //////////
onevent EVENT_PROCESSINPUT
state checknearwall
endevent
define Q_tmp 500
definequote Q_tmp <write on me>
define Q_no 501
definequote Q_no Hit no wall
define Q_yes 502
definequote Q_yes Hit wall %d
define Q_info 503
definequote Q_info pos: %d %d %d sect: %d
onevent EVENT_DISPLAYREST
qsprintf Q_tmp Q_info, x y z sectnum
minitext (100, 8) Q_tmp (0, 0)
ifvarn hitwall -1
{
qsprintf Q_tmp Q_yes hitwall
minitext (100, 16) Q_tmp (0, 0)
}
else
{
minitext (100, 16) Q_no (0, 0)
}
endevent

View file

@ -1,110 +0,0 @@
local bit = require("bit")
local band = bit.band
local tostring = tostring
local gv = gv
local actor = actor
local sector, wall, sprite = sector, wall, sprite
local printf = printf
local sectorsofbunch = sectorsofbunch
local con = require("con")
local D = require("CON.DEFS")
----------
local TROR_GLASSBREAKER = 2959 -- red 'T'
-- Actor controlling the timing of a TROR hplane breaking.
gameactor
{
TROR_GLASSBREAKER,
function(aci, pli, dist)
local spr = sprite[aci]
if (not (spr.lotag == 712 and spr.hitag == 119)) then -- check BREAKER_MAGIC
sprite.changestat(aci, actor.STAT.DEFAULT)
return
end
local cnt = actor[aci]:get_count()
local finish = (cnt >= 6)
if (cnt == 0) then
-- NOTE: INTERNAL interface, DON'T USE!
con._sound(aci, D.GLASS_BREAKING)
end
local bunchnum = spr.extra
for sectnum, what in sectorsofbunch(bunchnum, gv.BOTH_CF) do
local cf = sector[sectnum][what]
cf:set_picnum(D.GLASS2 + cnt)
cf.statbits:clear(sector.STAT.BLOCK + sector.STAT.HITSCAN)
if (finish) then
cf.statbits:clear(sector.STAT.TRANS_BITMASK)
end
end
if (finish) then
con.killit()
end
end
}
local DHP = sector.DAMAGEHPLANE
gameevent
{
"DAMAGEHPLANE",
function(aci, pli, RETURN)
local what, sectnum = sector.damagehplane_whatsect(RETURN)
local sec = sector[sectnum]
-- Part I: make various screens breakable when it's a ceiling picnum.
if (what == "ceiling") then
-- hit ceiling
if (sec.ceilingpicnum >= 263 and sec.ceilingpicnum <= 275) then
sec:set_ceilingpicnum(D.W_SCREENBREAK + gv.krand()%3)
gv.RETURN = DHP.GLASSBREAK
return con.longjmp()
end
end
gv.RETURN = DHP.DEFAULT
end
}
gameevent
{
"DAMAGEHPLANE",
function(aci, pli, RETURN)
local what, sectnum = sector.damagehplane_whatsect(RETURN)
local sec = sector[sectnum]
-- Part II: breakable TROR hplanes
local cf = sec[what]
-- printf("damage %s of sector %d (pic %d, bunch %d, hittable: %s)", what, sectnum,
-- cf.picnum, cf.bunch, tostring(cf.statbits:test(sector.STAT.HITSCAN)))
if (cf.bunch >= 0 and (cf.picnum==198 or cf.picnum==D.GLASS2) and
cf.statbits:test(sector.STAT.HITSCAN)) then
local bi = con.insertsprite(TROR_GLASSBREAKER, wall[sec.wallptr], sectnum, actor.STAT.ACTOR)
local breaker = sprite[bi]
breaker.cstat = sprite.CSTAT.INVISIBLE
breaker.lotag, breaker.hitag = 712, 119 -- BREAKER_MAGIC
breaker.extra = cf.bunch
gv.RETURN = DHP.SUPPRESS
return con.longjmp()
end
gv.RETURN = DHP.DEFAULT
end
}

View file

@ -1,66 +0,0 @@
// In CON, the dangling else ambiguity is resolved in a very peculiar way:
// A cascade "ifP1 ifP2 ifP3 Stmt1 ... else Stmt2" is considered as if the
// predicates P1, P2, P3, ... are combined with a logical AND.
// So effectively, there is no ambiguity at all, and one of Stmt1 and Stmt2
// is guaranteed to be taken.
define Q 114
define GLOBAL 0
gamevar x 0 GLOBAL
// divisible by 2, 3, 5?
gamevar xm2 0 GLOBAL
gamevar xm3 0 GLOBAL
gamevar xm5 0 GLOBAL
state testelse
setvarvar xm2 x, modvar xm2 2
setvarvar xm3 x, modvar xm3 3
setvarvar xm5 x, modvar xm5 5
redefinequote Q ERROR: one path of if/else must be taken
ifvarn xm2 0
ifvarn xm3 0
ifvarn xm5 0
redefinequote Q x=%d is not divisible by either 2, 3 or 5
else
redefinequote Q x=%d is divisible by either 2, 3 or 5
qsprintf Q Q x
userquote Q
ends
onevent EVENT_ENTERLEVEL
setvar x 0
whilevarn x 31
{
state testelse
addvar x 1
}
endevent
// Test deferred code. This CON code doesn't make much sense, but check out
// the generated Lua code...
onevent EVENT_JUMP
ifpdistl 100 ifpdistg 0 ifcanseetarget
{
palfrom 32 32 32
ifcanseetarget
quote 29
ifcanseetarget
quote 30
setvar x 0
state testelse
}
else ifcanseetarget
quote 31
else ifvare 1 1
{
// Test empty brace-enclosed statement under if condition.
}
else
{
quote Q
}
endevent

View file

@ -1,97 +0,0 @@
local require = require
local string = require("string")
local con = require("con")
local xmath = require("xmath")
local gv = gv
local sprite = sprite
local actor = actor
local player = player
local assert = assert
local printf = printf
local gameevent = gameevent
local spritesofstat = spritesofstat
local Inf = 0/1
module(...)
-- Insert MUSICANDSFX? (Delete it otherwise.)
insp = false
-- Hitag and lotag of last deleted MUSICANDSFX sprite.
tag = {}
tag.hi, tag.lo = 0, 0
-- Preliminary dummy of a local gamevar.
local ournumjumps = 0
local lastv = xmath.vec3()
ilastv = xmath.ivec3()
require "end_gamevars"
-- We may cache globals defined in the gamevar section afterwards, but not
-- refer to locals defined prior to the gamevar section in it.
local tag = tag
local D = require("CON.DEFS")
gameevent{"JUMP", actor.FLAGS.chain_beg,
function(aci, pli)
local ps = player[pli]
ournumjumps = ournumjumps+1
if (insp) then
-- Insert MUSICANDSFX sprite with same lo-/hitag as last deleted one.
printf("delmusicsfx: jump count=%d, inserting", ournumjumps)
local spr = sprite[con.spawn(D.MUSICANDSFX, aci)]
spr.lotag, spr.hitag = tag.lo, tag.hi
else
-- Delete nearest MUSICANDSFX sprite.
local nearestdst = Inf
local nearesti = -1
for i in spritesofstat(actor.STAT.FX) do
local dst = (sprite[i]-ps.pos):len2()
if (nearesti == -1 or (dst < nearestdst and dst < sprite[i].hitag)) then
printf("MSFX %d dist %d", i, dst)
nearesti = i
nearestdst = dst
end
end
if (nearesti >= 0) then
local spr = sprite[nearesti]
tag.lo, tag.hi = spr.lotag, spr.hitag
lastv.x, lastv.y, lastv.z = spr.x, spr.y, spr.z+0.5
ilastv.x, ilastv.y, ilastv.z = spr.x, spr.y, spr.z
actor.delete(nearesti)
end
assert(nearesti < 0 or sprite[nearesti].picnum==D.MUSICANDSFX)
printf("delmusicsfx: jump count=%d, deleting sprite %d", ournumjumps, nearesti)
end
insp = not insp
end}
-- Display the number of times we jumped on the screen.
gameevent
{
"DISPLAYREST",
function()
con.minitext(240, 10, string.format("jumped %d times", ournumjumps))
end
}

View file

@ -1,16 +0,0 @@
actor 1890 1
// The following "else { if" must not be translated to an "elseif".
ifcount 1 nullop else
{
ifvare 0 0
spawn 1
else
spawn 2
ifvare 1 1
spawn 3
else ifvare 1 2
spawn 4
}
enda

View file

@ -1,48 +0,0 @@
definequote 125 FIRST
definequote 126 SECOND
definequote 127 THIRD
onevent EVENT_ENTERLEVEL
echo 125
endevent
onevent EVENT_ENTERLEVEL
echo 126
break
echo 126
endevent
onevent EVENT_ENTERLEVEL
echo 127
endevent
eventloadactor 930 // "police line" ribbon
// make non-destroyable
setactor[THISACTOR].hitag 0
enda
eventloadactor 2491 // DUKECAR
killit
enda
/*
Outputs:
========
C-CON:
------
(Since r5097:)
THIRD
SECOND
FIRST
(Before r5097:)
THIRD
SECOND
LunaCON:
--------
THIRD
SECOND
FIRST
*/

View file

@ -1,151 +0,0 @@
// Test for gamearrays / their persistence.
define QWESZ 10
define ASDSZ 12
define MAXTILES 30720 // XXX
gamearray qwe QWESZ
gamearray asd ASDSZ
define NEWQWESZ 9
define NEWASDSZ 14
gamearray qwe1 1 // should auto-resize when reading in
gamearray asd1 NEWASDSZ
gamevar qwesz 0 0
gamevar asdsz 0 0
// Test gamevars that are named like Lua keywords.
gamevar local 0 0
gamevar i 0 0
gamevar tmp 0 0
define Q_qwe_fn 400
define Q_asd_fn 401
definequote Q_qwe_fn LTEST_QWE_ARRAY.bin
definequote Q_asd_fn LTEST_ASD_ARRAY.bin
// Wouldn't run in C-CON otherwise; redefinequote/qsprintf errors out when
// passed a non-allocated quote as destination.
definequote 500 TEMP
definequote 501 TEMP
definequote 505 tile y sizes: 0:%d 1:%d 2:%d 3:%d 4:%d 5:%d 6:%d 7:%d 8:%d
definequote 506 1: 0x0a0a0d0d==168430861 ? %d
definequote 507 2: 0x0a0a0d0d==168430861 ? %d
gamevar MINUSONE -1 0
state error
setactor[MINUSONE].cstat 0
// This doesn't work in C-CON:
// setactor[-1].cstat 0
// error: symbol `1' is not a game variable.
ends
onevent EVENT_ENTERLEVEL
setvar i 0
whilevarn i QWESZ
{
setarray qwe[i] i
addvar i 1
}
// Test nested array syntax:
setarray qwe[qwe[5]] 0
// qwe[] now:
// 0 1 2 3 4 0 6 7 8 9
setvar i 0
whilevarn i ASDSZ
{
setvarvar tmp i
addvar tmp 100
setarray asd[i] tmp
setvarvar local tmp
addvar i 1
}
copy qwe[4] /*->*/ asd[3] 3
setarray asd[4] 0x0a0a0d0d
// asd[] now:
// 100 101 102 4 0x0a0a0d0d 6 106 107 108 109 110 111
qsprintf 506 506 asd[4]
userquote 506
resizearray qwe NEWQWESZ // shrink
resizearray asd NEWASDSZ // grow
ifvarn qwe[4] 4 state error
ifvarn asd[4] 0x0a0a0d0d state error
writearraytofile qwe Q_qwe_fn
writearraytofile asd Q_asd_fn
readarrayfromfile qwe1 Q_qwe_fn
readarrayfromfile asd1 Q_asd_fn
qsprintf 507 507 asd[4]
userquote 507
ifvarn asd[4] 0x0a0a0d0d state error
// test array sizes
getarraysize qwe qwesz
getarraysize asd asdsz
ifvarn qwesz NEWQWESZ state error
ifvarn asdsz NEWASDSZ state error
// test array sizes with *1 arrays
getarraysize qwe1 qwesz
getarraysize asd1 asdsz
ifvarn qwesz NEWQWESZ state error
ifvarn asdsz NEWASDSZ state error
redefinequote 500 [%d]: qwe=%d, qwe1=%d, asd=%d, asd1=%d
redefinequote 501 Because C-CON can't qsprintf into an uninitialized one
setvar i 0
whilevarvarn i -1
{
ifvarl i NEWQWESZ
qsprintf 501 500 i qwe[i] qwe1[i] asd[i] asd1[i]
else
qsprintf 501 500 i -1 -1 asd[i] asd1[i]
userquote 501
addvar i 1
ifvare i NEWASDSZ
setvar i -1
}
redefinequote 500 MUST REACH
userquote 500
setvar i 120
modvar i 25
addlogvar i
//// Test system gamearrays
getarraysize tilesizx qwesz
ifvarn qwesz MAXTILES state error
copy tilesizy[0] /*->*/ qwe[0] 9
qsprintf 505 505 qwe[0] qwe[1] qwe[2] qwe[3] qwe[4] qwe[5] qwe[6] qwe[7] qwe[8]
userquote 505
// Must give a translation error:
// resizearray tilesizy 100
endevent

View file

@ -1,94 +0,0 @@
// From http://wiki.eduke32.com/wiki/How_to_make_a_clock (author: The Commander)
// with modifications
gamevar sec 0 0
gamevar min 0 0
gamevar hour 0 0
gamevar mday 0 0
gamevar mon 0 0
gamevar year 0 0
gamevar wday 0 0
gamevar yday 0 0
onevent EVENT_DISPLAYREST
gettimedate sec min hour mday mon year wday yday
ifvarl hour 20 ifvarg hour 9
digitalnumberz 2992 285 16 hour 0 0 0 0 0 xdim ydim 65536
ifvarg hour 19
digitalnumberz 2992 282 16 hour 0 0 0 0 0 xdim ydim 65536
ifvarl hour 10
{
digitalnumberz 2992 288 16 hour 0 0 0 0 0 xdim ydim 65536
rotatesprite 269 16 65536 0 2992 0 0 0 0 0 xdim ydim
}
ifvarg min 9
digitalnumberz 2992 308 16 min 0 0 0 0 0 xdim ydim 65536
ifvarl min 10
{
digitalnumberz 2992 315 16 min 0 0 0 0 0 xdim ydim 65536
rotatesprite 296 16 65536 0 2992 0 0 0 0 0 xdim ydim
}
gametextz STARTALPHANUM 20 20 1 0 0 0 0 0 xdim ydim 32768
endevent
// Following code by Hendricks266, from
// http://forums.duke4.net/topic/1093-what-are-you-working-on-for-duke-right-now/page__view__findpost__p__152626
gamevar zoom 0 0
definequote 10000 abcdefghijklm
definequote 10001 nopqrstuvwxyz
onevent EVENT_DISPLAYREST
setvarvar zoom totalclock
modvar zoom 240
mulvar zoom 8192
divvar zoom 15
digitalnumberz DIGITALNUM 160 5 1234567890 0 0 16 windowx1 windowy1 windowx2 windowy2 zoom
gametextz STARTALPHANUM 320 30 10000 0 0 16 windowx1 windowy1 windowx2 windowy2 zoom
rotatesprite 160 70 zoom 0 SPINNINGNUKEICON 0 0 0 windowx1 windowy1 windowx2 windowy2
endevent
// Following code from the wiki 'showview' entry, with modifications
gamevar x 0 0
gamevar y 0 0
gamevar z 0 0
gamevar ang 0 0
gamevar horiz 0 0
gamevar temp 0 0
gamevar sect 0 0
onevent EVENT_DISPLAYREST
getplayer[THISACTOR].posx x
getplayer[THISACTOR].posy y
getplayer[THISACTOR].posz z
getplayer[THISACTOR].ang ang
getplayer[THISACTOR].horiz horiz
getplayer[THISACTOR].horizoff temp
addvarvar horiz temp
// Head cam.
subvar z 16384
updatesectorz x y z sect
// This upper view should be a bit wider.
ifvarg sect -1
showviewunbiased x y z ang horiz sect 16 8 156 39
// Groin cam.
addvar z 16384
addvar z 4096
updatesectorz x y z sect
ifvarg sect -1
showview x y z ang horiz sect 16 39 156 70
endevent

View file

@ -1,27 +0,0 @@
definegametype 0 46051 Cooperative, not Dukematch
// NOTE: we need to define the rest anew too (or at least #4), because
// 'definegametype' does "g_numGametypes = idx+1", as opposed to
// "g_numGametypes = max(g_numGametypes, idx+1)".
definegametype 1 46051 Cooperative
definegametype 2 16410 Dukematch (no spawn)
definegametype 3 216088 Team Dukematch
definegametype 4 213018 Team Dukematch (no spawn)
gamevar temp 0 0
// Original version by Fox, from
// http://forums.duke4.net/topic/775-eduke32-20-and-polymer/page__view__findpost__p__183890
onevent EVENT_DISPLAYREST
// Notes:
// - index (THISACTOR) is never used.
// - incorrect if screenpeek != myconnectindex, e.g. by pressing [K]:
// - all three are equivalent:
getuserdef[THISACTOR].statusbarscale temp
getuserdef .statusbarscale temp
getuserdef[].statusbarscale temp
redefinequote 0 %ld
qsprintf 0 0 temp
gametext STARTALPHANUM 10 10 0 0 0 0 0 0 xdim ydim
endevent

View file

@ -1,137 +0,0 @@
-- A spawner (NUKEBUTTON+3) of colored TRANSPORTERSTAR+4 sprites in a helical
-- arrangement.
local require = require
local math = require("math")
local con = require("con")
local xmath = require("xmath")
local gv = gv
local actor = actor
local player = player
local sprite = sprite
local gameactor = gameactor
module(...)
-- Dual-typed per-actor array: false if a broken nuke switch is not enabled,
-- start game tic of when it was enabled otherwise.
-- NOTE: for objects that are not supposed to be deleted such as this one, it
-- would also be OK to use a plain table.
local nukeswStart = con.actorvar(false)
-- This one should be a per-actor variable because it holds info about
-- "volatile" actors.
local starPal = con.actorvar(0)
-- Color per decasecond, can be changed from outside.
COLOR = { 1, 2, 6, 7, 8 }
require("end_gamevars")
local bangvec = xmath.bangvec
local angvec = xmath.angvec
local D = require("CON.DEFS")
local GTICSPERSEC = gv.GTICSPERSEC
gameactor
{
D.TRANSPORTERSTAR+4,
flags = actor.FLAGS.NOCLIP,
move = con.move{100},
movflags = actor.MOVFLAGS.geth,
func = function(aci)
local spr = sprite[aci]
-- NOTE: this is prettier than calling it 'a', even if 'act' is used to
-- denote an action in other places:
local act = actor[aci]
if (act:has_action(0)) then
act:set_action(1) -- TODO: actor constructors, i.e. 'init' callbacks
local decasec = math.floor((gv.gametic - nukeswStart[spr.owner])/(GTICSPERSEC*10)) % 12
local pal = COLOR[decasec+1]
if (pal ~= nil) then
starPal[aci] = pal
end
-- Every 2nd minute, we stop coloring the spawned stars. This tests
-- per-actor variable resetting to the default value.
spr.pal = starPal[aci]
end
if (act:checkbump()) then
con.killit()
end
-- Test spr:changesect() vs. sprite.changesect()
local sectnum = spr.sectnum
for i=0,gv.numsectors-1 do
if (spr.pal ~= 2 and spr.pal ~= 7) then
sprite.changesect(aci, i) -- noticeably faster...
else
spr:changesect(i) -- ...than this
end
end
sprite.changesect(aci, sectnum)
end
}
local CS = sprite.CSTAT
local SPAWNSPERTIC = 10 --> 300/second --> 18000 per minute
local TWOPI = 2*math.pi
gameactor
{
D.NUKEBUTTON+3, -- destroyed end-of-level nuke switch
function(aci)
local spr = sprite[aci]
for pi in player.all() do
-- XXX: how to emulate "use switch" distance checking code, but in
-- an actor-centric fashion?
if (not nukeswStart[aci] and player.holdskey(pi, "OPEN")
and (player[pi].pos - spr):len2sq() < 256^2) then
-- Enable us.
nukeswStart[aci] = gv.gametic
spr.cstatbits:clear(CS.TRANS_BITMASK)
spr.cstatbits:set(CS.TRANS1)
break
end
end
local startgtic = nukeswStart[aci]
if (not startgtic) then
return
end
local hei, zofs = spr:getheightofs()
local radius = hei/2
for i=0,SPAWNSPERTIC-1 do
-- Make one second go once round the circle, spawning
-- SPAWNSPERTIC*GTICSPERSEC stars.
local ii = ((gv.gametic*SPAWNSPERTIC)%(GTICSPERSEC*SPAWNSPERTIC)) + i
local v = (radius/16)*angvec(ii*TWOPI/(GTICSPERSEC*SPAWNSPERTIC))
local circvec = xmath.vec3(0, v.x, 16*v.y):rotate(spr.ang)
local pos = spr^(zofs + radius) + 256*bangvec(spr.ang) + circvec
con.insertsprite{D.TRANSPORTERSTAR+4, pos, spr.sectnum, actor.STAT.ACTOR, aci,
xrepeat=3, yrepeat=3, ang=spr.ang}
end
end
}

View file

@ -1,179 +0,0 @@
// Map state test.
// To warp to a particular map, press the steroids key.
// Volume: player angle / 512, displayed on screen.
// Level: current weapon.
// Clear mapstate: press [QuickKick]
define Q_tmp 400
definequote Q_tmp ====== TEMP =======
definequote 401 WILL WARP TO VOLUME %d
define SQ 402
definequote SQ <source>
definequote 403 <dest>
definequote 404 <dest>
definequote 405 <dest>
definequote 406 <dest>
definequote 407 <dest>
definequote 408 <dest>
definequote 409 <dest>
definequote 410 <dest>
definequote 411 <dest>
definequote 412 <dest>
definequote 413 <dest>
definequote 414 <dest>
define Q_cms 500
definequote Q_cms Cleared mapstate for volume %d level %d
// Test gamevar flags GAMEVAR_NODEFAULT and GAMEVAR_NORESET.
// Global.
define GV_NODEFAULT 1024
define GV_NORESET 131072
define GV_NODEFAULT_NORESET 132096
// Per-player.
define PV_NODEFAULT 1025
define PV_NORESET 131073
define PV_NODEFAULT_NORESET 132097
// Per-actor (not very meaningful, as the APLAYER actor will in general have a
// different sprite for different levels).
define AV_NODEFAULT 1026
define AV_NORESET 131074
define AV_NODEFAULT_NORESET 132098
// global
gamevar numwarps 0 0
gamevar numwarpsD 0 GV_NODEFAULT
gamevar numwarpsR 0 GV_NORESET
gamevar numwarpsDR 0 GV_NODEFAULT_NORESET
// per-player
gamevar Pnumwarps 0 1
gamevar PnumwarpsD 0 PV_NODEFAULT
gamevar PnumwarpsR 0 PV_NORESET
gamevar PnumwarpsDR 0 PV_NODEFAULT_NORESET
// per-actor
gamevar Anumwarps 0 2
gamevar AnumwarpsD 0 AV_NODEFAULT
gamevar AnumwarpsR 0 AV_NORESET
gamevar AnumwarpsDR 0 AV_NODEFAULT_NORESET
gamevar pal 0 0
gamevar dq 0 0
gamevar y 0 0
state calcvolume
getplayer[THISACTOR].ang gs
andvar gs 2047
divvar gs 512
ends
state dispnumwarps
gametextz STARTALPHANUM 20 y dq 0 pal 0 0 0 xdim ydim 32768
addvar dq 1
addvar y 8
ends
onevent EVENT_DISPLAYREST
state calcvolume
qsprintf Q_tmp /*<-*/ 401 /**/ gs
gametext STARTALPHANUM 20 20 Q_tmp 0 0 0 0 0 xdim ydim
setvar y 40
setvar dq 403
setvar pal 0
// global
redefinequote SQ numwarps (no flags): %d
qsprintf dq SQ numwarps
state dispnumwarps
redefinequote SQ numwarps (NODEFAULT): %d
qsprintf dq SQ numwarpsD
state dispnumwarps
redefinequote SQ numwarps (NORESET): %d
qsprintf dq SQ numwarpsR
state dispnumwarps
redefinequote SQ numwarps (NODEFAULT, NORESET): %d
qsprintf dq SQ numwarpsDR
state dispnumwarps
// per-player
setvar pal 14
redefinequote SQ Pnumwarps (no flags): %d
qsprintf dq SQ Pnumwarps
state dispnumwarps
redefinequote SQ Pnumwarps (NODEFAULT): %d
qsprintf dq SQ PnumwarpsD
state dispnumwarps
redefinequote SQ Pnumwarps (NORESET): %d
qsprintf dq SQ PnumwarpsR
state dispnumwarps
redefinequote SQ Pnumwarps (NODEFAULT, NORESET): %d
qsprintf dq SQ PnumwarpsDR
state dispnumwarps
// per-actor
setvar pal 10
redefinequote SQ Anumwarps (no flags): %d
qsprintf dq SQ Anumwarps
state dispnumwarps
redefinequote SQ Anumwarps (NODEFAULT): %d
qsprintf dq SQ AnumwarpsD
state dispnumwarps
redefinequote SQ Anumwarps (NORESET): %d
qsprintf dq SQ AnumwarpsR
state dispnumwarps
redefinequote SQ Anumwarps (NODEFAULT, NORESET): %d
qsprintf dq SQ AnumwarpsDR
state dispnumwarps
endevent
onevent EVENT_USESTEROIDS
// global
addvar numwarps 1
addvar numwarpsD 1
addvar numwarpsR 1
addvar numwarpsDR 1
// per-player
addvar Pnumwarps 1
addvar PnumwarpsD 1
addvar PnumwarpsR 1
addvar PnumwarpsDR 1
// per-actor
addvar Anumwarps 1
addvar AnumwarpsD 1
addvar AnumwarpsR 1
addvar AnumwarpsDR 1
savemapstate
state calcvolume
startlevel gs currentweapon
endevent
onevent EVENT_QUICKKICK
state calcvolume
qsprintf Q_tmp Q_cms gs currentweapon
userquote Q_tmp
// Calc linear map index.
mulvar gs 64 // MAXLEVELS (XXX: should be exposed via CON)
addvarvar gs currentweapon
clearmapstate gs
endevent
onevent EVENT_ENTERLEVEL
loadmapstate
endevent
onevent EVENT_USEJETPACK
savegamevar numwarpsD
endevent
onevent EVENT_USENIGHTVISION
readgamevar numwarpsD
endevent

View file

@ -1,63 +0,0 @@
local xmath = require("xmath")
local sprite = sprite
--
local maputil = {}
-- csfunc = maputil.CreateCrossSpriteFunc(use_sprite_i_func, use_sprite_j_func)
--
-- use_sprite_i_func: function(spri, ud), where
-- <spri> is a reference to sprite i
-- use_sprite_j_func: function(sprj, spri, ud), where
-- <spri> is a reference to sprite i
-- <sprj> is a reference to sprite j
--
-- csfunc: function(userdata, process_sprite_j_func), where
-- <userdata> is passed as <ud> to the functions described above
-- <process_sprite_j_func>: function(sprj, spri, ud), the user-provided
-- function that processes sprite j in some way
function maputil.CreateCrossSpriteFunc(use_sprite_i_func, use_sprite_j_func)
return function(userdata, process_sprite_j_func)
for i in sprite.all() do
if (use_sprite_i_func(sprite[i], userdata)) then
for j in sprite.all() do
if (use_sprite_j_func(sprite[j], sprite[i], userdata)) then
process_sprite_j_func(sprite[j], sprite[i], userdata)
end
end
end
end
end
end
-- Functions for "for all sprites Y of tile X that are closer than D, ..."
-- Passed userdefs: { picnumi, picnumj, maxldist }
local function snearpic_usei(spr, ud)
return spr.picnum == ud[1]
end
local function snearpic_usej(sprj, spri, ud)
return sprj.picnum == ud[2] and xmath.ldist(spri, sprj) <= ud[3]
end
local snearpic_func = maputil.CreateCrossSpriteFunc(snearpic_usei, snearpic_usej)
-- maputil.for_sprites_near_picnum(picnumi, picnumj, dist, process_sprite_func)
--
-- Runs the following loop:
--
-- for all sprites i with tile <picnumi>,
-- for all sprites j of tile <picnumj> that are closer to sprite i than <dist> [*],
-- call process_sprite_func(sprite[j], sprite[i])
--
-- [*] using xmath.ldist()
function maputil.for_sprites_near_picnum(picnumi, picnumj, dist, process_sprite_func)
snearpic_func({picnumi, picnumj, dist}, process_sprite_func)
end
return maputil

View file

@ -1,57 +0,0 @@
// NOTE: This file is misnamed, "break" is local control flow, of course.
state teststate_break
ifvare 1 1
{
redefinequote 114 BEFORE
echo 114
break
redefinequote 114 AFTER
echo 114
}
redefinequote 114 STILL LIVE INNER
echo 114
ends
definequote 499 ===
definequote 500 MAXSPRITES: %d
definequote 501 MAXSTATUS: %d
definequote 502 MAX_WEAPONS: %d
// XXX: EVENT_INIT not reached with LunaCON
onevent EVENT_ENTERLEVEL
state teststate_break
redefinequote 114 STILL LIVE OUTER
echo 114
qsprintf 499 /*<-*/ 500 MAXSPRITES
echo 499
qsprintf 499 /*<-*/ 501 MAXSTATUS
echo 499
qsprintf 499 /*<-*/ 502 MAX_WEAPONS
echo 499
endevent
// Test number parsing
gamevar MINUS_ONE -1 0
gamevar INT32_MIN 0x80000000 0
gamevar INT32_MIN_ -0x80000000 0
gamevar INT32_MINh 0x80000000h 0
gamevar INT32_MIN_h -0x80000000h 0
gamevar INT32_MIN_P1 0x80000001 0
gamevar INT32_MIN_P1h 0x80000001h 0
//gamevar INT32_MAX_ -0x80000001 0 // error
gamevar INT32_MAX 0x7fffffff 0
gamevar INT32_MAXh 0x7fffffffh 0
// 9 digits? truncate it, but only because there's an F there.
gamevar SOMEHEX 0xFFEFC0001 0
// This one throws an error:
//gamevar BADHEX 0xBFEFC0001 0

View file

@ -1,22 +0,0 @@
definequote 125 SPAWNED HEAVYHBOMB
definequote 126 RAN EVENT_EGS
definequote 127 RAN TEST STATE
state teststate1
return
quote 127
ends
onevent EVENT_EGS
ifactor HEAVYHBOMB
{
state teststate1 // after teststate1's return, return from EVENT_EGS!
quote 126
}
endevent
onevent EVENT_FIRE
// the concrete actor is irrelevant, only placeholder
spawn HEAVYHBOMB // --> EVENT_EGS
quote 125
endevent

View file

@ -1,75 +0,0 @@
gamevar vol 0 0
gamevar lev 0 0
// out-of-bounds volume/level numbers
gamevar badvol_oob 99 0
gamevar badlev_oob 999 0
// volume/level numbers for which no level is defined
gamevar badvol_nd 3 0
gamevar badlev_nd 32 0
define Q_tmp 255
definequote Q_tmp <temp>
define Q_tmp2 256
definequote Q_tmp2 <temp>
define Q_last_level 1000
definequote Q_last_level Last level (1-based): %d
define Q_dtime 1001
definequote Q_dtime Designer time for last level: %s
state print_designer_time
// This must not invoke an oob access on the C side if ud.last_level < 0!
qgetsysstr Q_tmp2 STR_DESIGNERTIME
qsprintf Q_tmp Q_dtime Q_tmp2
userquote Q_tmp
ends
onevent EVENT_NEWGAME
getuserdef .last_level lev
qsprintf Q_tmp Q_last_level lev
userquote Q_tmp
state print_designer_time
endevent
onevent EVENT_ENTERLEVEL
state print_designer_time
// must fail, since the current player is -1 in this event
qgetsysstr Q_tmp STR_PLAYERNAME
endevent
onevent EVENT_USESTEROIDS
getuserdef .volume_number vol
setuserdef .volume_number badvol_oob // LunaCON errors here
qgetsysstr Q_tmp STR_VOLUMENAME // C-CON errors here
setuserdef .volume_number vol
endevent
onevent EVENT_USEJETPACK
getuserdef .volume_number vol
getuserdef .level_number lev
setuserdef .volume_number badvol_oob // LunaCON errors here
setuserdef .level_number badlev_oob
qgetsysstr Q_tmp STR_MAPNAME // C-CON errors here
setuserdef .volume_number vol
setuserdef .level_number lev
endevent
onevent EVENT_JUMP
getuserdef .volume_number vol
getuserdef .level_number lev
setuserdef .volume_number badvol_nd
setuserdef .level_number badlev_nd
qgetsysstr Q_tmp STR_MAPFILENAME // LunaCON, C-CON error here
setuserdef .volume_number vol
setuserdef .level_number lev
endevent

View file

@ -1,71 +0,0 @@
// Tests for the qsubstr command.
// This one should be safe to run in C-CON.
// overlong string at definition (should warn)
define QSRC 400
definequote QSRC -123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789=123456789
define QSRC_SHORT 401
definequote QSRC_SHORT -123456789
define QDST 500
definequote QDST
// for gettimedate
gamevar sec 0 0
gamevar x 0 0
onevent EVENT_ENTERLEVEL
// first, a plain, non-border-case call
qsubstr QDST QSRC 10 10 // -> "-123456789"
userquote QDST
// length zero yields the empty string
qsubstr QDST QSRC 20 0 // -> ""
userquote QDST
// start+length > strlen of source: no error
qsubstr QDST QSRC_SHORT 8 6 // -> "89"
userquote QDST
// start == strlen of source
qsubstr QDST QSRC_SHORT 10 1 // -> ""
userquote QDST
// Make QDST contain
// -123456789<NUL>123456789-...
qstrcpy QDST QSRC_SHORT
qstrcpy QSRC_SHORT QSRC
qstrcpy QSRC_SHORT QDST
userquote QSRC_SHORT // -> "-123456789"
// start == strlen of source, trailing bytes after source <NUL>
qsubstr QDST QSRC_SHORT 10 2 // -> ""
userquote QDST
// start is beyond-<NUL> of the source
qsubstr QDST QSRC_SHORT 11 9 // -> ""
userquote QDST
// copy whole string (127 chars + 1 <NUL> == MAXQUOTELEN)
qsubstr QDST QSRC 0 1000 // -> "-123456789-123456789-...=123456"
userquote QDST
/** TESTS FOR source == dest **/
qsubstr QDST QDST 0 20 // -> "-123456789-123456789"
userquote QDST
qsubstr QDST QDST 5 10 // -> "56789-1234"
userquote QDST
/** TESTS FOR invalid input **/
gettimedate sec x x x x x x x
ifvarand sec 1
qsubstr QDST QSRC -1 10 // invalid start
else
qsubstr QDST QSRC 0 -1 // invalid length
// Reached only in C-CON:
qsubstr QDST QSRC 128 0 // invalid start
endevent

View file

@ -1,120 +0,0 @@
// Don't try in C-CON!
gamevar slen 0 0
gamevar n -12345678 0 // a number having length 9 in the decimal representation
// overlong string at definition (should warn)
definequote 400 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789=123456789-
// alloc some quotes, test empty quote syntax
definequote 401
definequote 402
definequote 403
definequote 404
definequote 405
definequote 406
onevent EVENT_ENTERLEVEL
redefinequote 114 QWE
redefinequote 115 012345678|
// qstrcat X Y, where X==Y: duplicates the quote
qstrcat 115 115
userquote 115 // "012345678|012345678|"
// simple qstrcpy test
qstrcpy 116 115
userquote 116 // "012345678|012345678|"
redefinequote 117 %s -- %d -- %s -- %d slen=%d
// test:
// - same destination quote as the format quote
// - multiple conversions
qstrlen slen 115 // -> 10
qsprintf 117 /*<-*/ 117 /*args:*/ 116 9999 114 5555 slen
userquote 117 // "012345678|012345678| -- 9999 -- QWE -- 5555 slen=10"
redefinequote 117 %s -- %d -- %s -- %d slen=%d
// test:
// - same destination quote as one of the source quotes under %s conversion
qsprintf 116 /*<-*/ 117 /*args:*/ 116 9999 116 5555 slen
userquote 116 // "012345678|012345678| -- 9999 -- 012345678|012345678| -- 5555 slen=10"
// aliased qstrcpy test (a no-op)
qstrcpy 115 115
userquote 115 // still "012345678|012345678|"
qstrcat 115 115 // len 40
qstrcat 115 115 // len 80
qstrcat 115 115
// result now: 12 x "012345678|" concantenated with "0123456" (= total length 127 = MAXQUOTELEN-1)
qstrcat 115 115 // this one is redundant, but must not overflow the quote buffer
userquote 115
////////// qstrncat test
redefinequote 300 012345678|
qstrncat 300 300 2
userquote 300 // "012345678|01"
//////////
redefinequote 117 X%s
qsprintf 117 /*<-*/ 117 /*args:*/ 115
userquote 117 // result: "X" .. 12 x "012345678|" .. "012345" (= total length 127 = MAXQUOTELEN-1)
// 32 %d (or %ld) conversions
redefinequote 117 %ld|%d|%ld|%d|%ld|%ld|%d|%ld|%d|%ld/%ld|%d|%ld|%d|%ld|%ld|%d|%ld|%d|%ld/%ld|%d|%ld|%d|%ld|%ld|%d|%ld|%d|%ld/%d/%d
// string shorter than MAXQUOTELEN-1:
qsprintf 116 /*<-*/ 117 /*args:*/ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// result: "0|1|2|3|4|5|6|7|8|9/0|1|2|3|4|5|6|7|8|9/0|1|2|3|4|5|6|7|8|9/0/1"
userquote 116
// string longer than MAXQUOTELEN-1:
qsprintf 116 /*<-*/ 117 /*args:*/ n n n n n n n n n n /**/ n n n n n n n n n n /**/ n n
userquote 116
// result: 10 x "-12345678" .. "/-12345678|-12345678|-123456" (= total length 127 = MAXQUOTELEN-1)
// overlong string at redefinition
redefinequote 117 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789=123456789-
userquote 117 // should end in "=1234567" (= total length 127 = MAXQUOTELEN-1)
userquote 400 // same thing
// test passing empty quote
userquote 401
// qgetsysstr test
qgetsysstr 401 STR_MAPNAME
qgetsysstr 402 STR_MAPFILENAME
// qgetsysstr 403 STR_PLAYERNAME
qgetsysstr 404 STR_VERSION
qgetsysstr 405 STR_GAMETYPE
qgetsysstr 406 STR_VOLUMENAME
redefinequote 400 %s | %s | %s | %s | %s | %s
qsprintf 400 /*<-*/ 400 /*args:*/ 401 402 403 404 405 406
userquote 400
endevent
// Test of 'getpname' command.
onevent EVENT_JUMP
getpname 400 THISACTOR
userquote 400
endevent
// Following code by Hendricks266, from
// http://forums.duke4.net/topic/1382-duke-64-mod-thread/page__view__findpost__p__150497
gamevar temp 0 0
gamevar x 0 0
definequote 666 This quote sucks
onevent EVENT_DISPLAYREST
qstrlen temp 666
setvarvar x tilesizx[STARTALPHANUM] // rough approximation
mulvarvar x temp
divvar x 2
addvar x 160 // put your centered position here
gametext STARTALPHANUM x 30 666 0 0 16 0 0 xdim ydim
endevent
//////////

View file

@ -1,56 +0,0 @@
// Use as root file, not as additional CON module.
// Test dynamic tile remapping.
dynamicremap
// Make MASKWALL2 (fence in E1L1 rooftop) execute the C side code of DUKECAR.
// This must be before the original DUKECAR definition, because label
// redefinitions are ignored in CON.
// XXX: Does MASKWALL2's code get executed, too?
define DUKECAR 913
include GAME.CON
// NOTE:
// DEFS.CON 520:6: warning: label "DUKECAR" not redefined with new value 2491 (old: 913)
gamevar LOGO_FLAGS 6399 0 // 255+2048+4096, test "no E4 intro", "no E1 bonus" bits
useractor 4 58 1 NO NO 0
// usertype 4 is "rotation-fixed actor"
// tile 58 is the space suit
state killme
enda
// Test useractor type "enemy"
define CYCLOIDHEAD 490
// Will have a flat-sprite-on-floor shadow and be an autoaim target.
useractor 1 CYCLOIDHEAD 10
ifpdistl 1024 tip
state killme
enda
onevent EVENT_JUMP
mail 2
money 5
paper 3
endevent
// Speed up sector effects a little 8-)
gamevar ra_temp 0 0
eventloadactor GPSPEED
getactor[THISACTOR].lotag ra_temp
mulvar ra_temp 4
setactor[THISACTOR].lotag ra_temp
enda
gamevar ii 0 0
onevent EVENT_PROCESSINPUT
setvar ii 0
whilevarvarn ii MAXSPRITES
{
ifvare sprite[ii].picnum 58 // space suit
ssp ii CLIPMASK0
addvar ii 1
}
endevent

View file

@ -1,349 +0,0 @@
// ScreenText Test
/*
This is half a test and half a demonstration.
For example, some of the spacing and offset stuff could be replaced with tests of other things, like the backwards bit, line wrapping, and space lengths.
Also, if/when Lunatic adds support for \n characters.
*/
definequote 999
definequote 1000 AbCdEfGhIjKlM
definequote 1001 nOpQrStUvWxYz
definequote 1010 X1234567890-.!?;:'\ / %
definequote 1020 ^2R^8G^1B^7Y
definequote 1337 1337
definequote 2000 Left
definequote 2001 Center
definequote 2002 Right
definequote 2003 Top
definequote 2004 Center
definequote 2005 Bottom
definequote 3000 Spacing
definequote 3001 X-Offset
definequote 3002 %d: UiUiUiUiUi
definequote 3003 Justification
definequote 4000 Normal: aBcDeF
definequote 4001 Upper: aBcDeF
definequote 4002 Lower: aBcDeF
definequote 4003 Inverted: aBcDeF
definequote 5000 Rotate
definequote 5001 Top, Left:
definequote 5002 Center, Center:
definequote 5003 Bottom, Right:
gamevar font STARTALPHANUM 0
gamevar x 0 0
gamevar y 0 0
gamevar z 65536 0
gamevar blockangle 0 0
gamevar charangle 0 0
gamevar q 1000 0
gamevar shade 0 0
gamevar pal 0 0
gamevar o 16 0
gamevar alpha 0 0
gamevar xspace 5 0
gamevar yline 8 0
gamevar xbetween 0 0
gamevar ybetween 0 0
gamevar f 0 0
gamevar x1 0 0
gamevar y1 0 0
gamevar x2 0 0
gamevar y2 0 0
gamevar temp 0 0
gamevar doaddtvblend 0 0
state resetbounds
setvarvar x1 windowx1
setvarvar y1 windowy1
setvarvar x2 windowx2
setvarvar y2 windowy2
ends
onevent EVENT_ENTERLEVEL
state resetbounds
endevent
define TEXT_XRIGHT 0x00000001
define TEXT_XCENTER 0x00000002
define TEXT_YBOTTOM 0x00000004
define TEXT_YCENTER 0x00000008
define TEXT_INTERNALSPACE 0x00000010
define TEXT_TILESPACE 0x00000020
define TEXT_INTERNALLINE 0x00000040
define TEXT_TILELINE 0x00000080
define TEXT_XOFFSETZERO 0x00000100
define TEXT_XJUSTIFY 0x00000200
define TEXT_YOFFSETZERO 0x00000400
define TEXT_YJUSTIFY 0x00000800
define TEXT_LINEWRAP 0x00001000
define TEXT_UPPERCASE 0x00002000
define TEXT_INVERTCASE 0x00004000
define TEXT_IGNOREESCAPE 0x00008000
define TEXT_LITERALESCAPE 0x00010000
define TEXT_BACKWARDS 0x00020000
define TEXT_GAMETEXTNUMHACK 0x00040000
define TEXT_DIGITALNUMBER 0x00080000
define TEXT_BIGALPHANUM 0x00100000
define TEXT_GRAYFONT 0x00200000
state increment_line
ifvarg q 0
screentext font x y z blockangle charangle q shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
addvar y 8
ends
state increment_line_test
ifvarg q 0
{
qsprintf 999 q temp
screentext font x y z blockangle charangle 999 shade pal o alpha xspace yline temp ybetween f x1 y1 x2 y2
}
addvar temp 1
addvar y 8
ends
onevent EVENT_DISPLAYREST
state resetbounds
// screentext font x y z blockangle charangle q shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
// spacing
setvar f 0
setvar x 35
setvar y 3
setvar pal 0
setvar temp 0
setvar q 3000
state increment_line_test
setvar temp -3
setvar q 3002
state increment_line_test
state increment_line_test
state increment_line_test
state increment_line_test
state increment_line_test
state increment_line_test
state increment_line_test
// justification
setvar f 0
orvar f TEXT_XCENTER
orvar f TEXT_XJUSTIFY
screentext font 160 y z blockangle charangle 3003 shade 12 o alpha xspace yline 320 ybetween f x1 y1 x2 y2
addvar y 8
setvar f 0
// x-offset
setvar temp 0
setvar q 3001
state increment_line_test
orvar f TEXT_XOFFSETZERO
setvar temp 5
setvar q 3002
state increment_line_test
state increment_line_test
state increment_line_test
state increment_line_test
state increment_line_test
state increment_line_test
addvar y 4
// case
setvar f 0
setvar q 4000
setvar pal 23
state increment_line
setvar q 4001
orvar f TEXT_UPPERCASE
state increment_line
setvar q 4002
orvar f TEXT_INVERTCASE
state increment_line
setvar q 4003
xorvar f TEXT_UPPERCASE
state increment_line
// Calculate periodically cycling alpha value. PERIODIC_ALPHA.
setvarvar temp totalclock
shiftvarl temp 2
sin alpha temp // alpha is now in [-2^14 .. 2^14]
shiftvarr alpha 7 // [-2^7 .. 2^7]
addvar alpha 128 // [0 .. 256]
ifvarg alpha 254 { setvar alpha 255 setvar doaddtvblend 1 }
else ifvarl alpha 2 { setvar doaddtvblend 0 }
ifvare doaddtvblend 1
{
shiftvarr alpha 2 // [0 63]
ifvarg alpha 31
{
setvar temp 63
subvarvar temp alpha
setvarvar alpha temp
orvar o 32 // RS_TRANS2
}
// alpha now in [0 .. 31]
addvar alpha 101 // additive translucency tables are assumed to be at [101 .. 132]
mulvar alpha -1
}
// divider line
rotatespritea 240 65 32768 512 WINDOWBORDER1 0 2 0 85 x1 y1 x2 y2
setvar pal 12
// x-alignment
setvar f 0
setvar x 220
screentext font x 127 z blockangle charangle 2000 shade pal o alpha xspace yline 0 ybetween f x1 y1 x2 y2
orvar f TEXT_XCENTER
screentext font x 137 z blockangle charangle 2001 shade pal o alpha xspace yline 0 ybetween f x1 y1 x2 y2
xorvar f TEXT_XCENTER
orvar f TEXT_XRIGHT
screentext font x 147 z blockangle charangle 2002 shade pal o alpha xspace yline 0 ybetween f x1 y1 x2 y2
rotatespritea x 132 32768 0 WINDOWBORDER2 127 0 0 85 x1 y1 x2 y2
// y-alignment
setvar f TEXT_UPPERCASE
screentext font 180 162 z blockangle charangle 2003 shade pal o alpha xspace yline 0 ybetween f x1 y1 x2 y2
orvar f TEXT_YCENTER
screentext font 208 162 z blockangle charangle 2004 shade pal o alpha xspace yline 0 ybetween f x1 y1 x2 y2
xorvar f TEXT_YCENTER
orvar f TEXT_YBOTTOM
screentext font 260 162 z blockangle charangle 2005 shade pal o alpha xspace yline 0 ybetween f x1 y1 x2 y2
rotatespritea 240 163 32768 512 WINDOWBORDER1 127 0 0 85 x1 y1 x2 y2
setvar pal 0
// bluefont vs. redfont XOFFSETZERO comparison
setvar f 0
orvar f TEXT_XOFFSETZERO
screentext font 0 175 65536 blockangle charangle 1010 shade pal o alpha xspace yline 13 ybetween f x1 y1 x2 y2
orvar f TEXT_BIGALPHANUM
orvar f TEXT_YBOTTOM
screentext BIGALPHANUM 0 200 z blockangle charangle 1010 shade pal o alpha xspace yline 13 ybetween f x1 y1 x2 y2
// block rotation
setvar y 2
screentext MINIFONT 170 y z blockangle charangle 5001 shade 10 o alpha 2 yline 1 ybetween 0 x1 y1 x2 y2
screentext MINIFONT 237 y z blockangle charangle 5002 shade 14 o alpha 2 yline 1 ybetween TEXT_XCENTER x1 y1 x2 y2
screentext MINIFONT 317 y z blockangle charangle 5003 shade 16 o alpha 2 yline 1 ybetween TEXT_XRIGHT x1 y1 x2 y2
setvar f 0
setvar x 175
setvar y 12
setvar pal 10
setvar blockangle 0
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
addvar blockangle 128
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
addvar blockangle 128
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
addvar blockangle 128
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
addvar blockangle 128
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
setvar blockangle -256
setvar x 245
setvar y 40
setvar f 0
setvar pal 14
orvar f TEXT_XCENTER
orvar f TEXT_YCENTER
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
addvar blockangle 128
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
addvar blockangle 128
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
addvar blockangle 128
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
addvar blockangle 128
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
setvar blockangle -512
setvar x 317
setvar y 19
setvar f 0
setvar pal 16
orvar f TEXT_XRIGHT
orvar f TEXT_YBOTTOM
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
addvar blockangle 128
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
addvar blockangle 128
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
addvar blockangle 128
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
addvar blockangle 128
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
setvar pal 15
// character rotation
setvar blockangle 0
setvar charangle 256
setvar f 0
setvar x 175
setvar y 78
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
addvar charangle 256
addvar y 13
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
addvar charangle 256
addvar y 13
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
addvar charangle 256
addvar y 13
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
setvar charangle 0
setvar x 225
setvar y 78
screentext font x y z 128 -128 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
screentext font x y z 256 -256 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
screentext font x y z 384 -384 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
screentext font x y z 512 -512 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
setvar x 312
setvar y 90
orvar f TEXT_XRIGHT
orvar f TEXT_YCENTER
screentext font x y z -128 128 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
screentext font x y z -256 256 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
screentext font x y z -384 384 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
screentext font x y z -512 512 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
// alpha, sideways, and escape colors
setvar pal 13
setvar blockangle 0
setvar f 0
orvar f TEXT_XCENTER
screentext font 0 100 z 1536 charangle 1020 shade pal o 0 xspace yline xbetween ybetween f x1 y1 x2 y2
orvar f TEXT_IGNOREESCAPE
screentext font 10 100 z 1536 charangle 1020 shade pal o 85 xspace yline xbetween ybetween f x1 y1 x2 y2
orvar f TEXT_LITERALESCAPE
screentext font 20 100 z 1536 charangle 1020 shade pal o 170 xspace yline xbetween ybetween f x1 y1 x2 y2
ifvarand o 32 xorvar o 32 // clear RS_TRANS2
setvar alpha 0 // Clear alpha so it's only active from PERIODIC_ALPHA to here.
endevent

View file

@ -1,906 +0,0 @@
--[[
Usage: in Mapster32,
> lua "shadexfog=reload'shadexfog'"
-- for example
> lua "shadexfog.create(100, 255,255,255)"
> lua "shadexfog.translate(100, 2)"
In EDuke32, simply pass this module at the command line.
--]]
local assert = assert
local error = error
local print = print
local printf = printf
local tonumber = tonumber
local type = type
local unpack = unpack
local bit = require("bit")
local math = require("math")
local string = require("string")
local min, max = math.min, math.max
local floor = math.floor
local sector, wall, sprite = sector, wall, sprite
local engine = require("engine")
local gv = gv
----------
local shadexfog = {}
-- Example:
-- lua "shadexfog.createremap(30, {[2]=0, [3]=1, [12]=0, [13]=1})"
-- creates a pal 30 which maps the blue and orange ramps to the gray ones.
-- (Compare with the rows of http://wiki.eduke32.com/wiki/File:Pala.png)
--
-- Sexdecatuple remappings of Duke3D pals loaded from LOOKUP.DAT:
-- Remappings that are not expressible as such and identity maps (pal 3 and 9)
-- omitted.
--
-- 2: { [0]=8, [1]=13, [2]=8, [3]=13, [4]=13, [5]=8, [6]=8, [7]=13, [9]=8, [10]=8, [11]=13, [12]=8, [14]=8, }
-- 5: { [8]=2, [13]=3, }
-- 7: { [0]=10, [1]=9, [2]=10, [3]=9, [4]=9, [5]=10, [6]=10, [7]=9, [8]=10, [11]=9, [12]=9, [13]=9, [14]=9, }
-- 8: { [0]=6, [1]=7, [2]=6, [3]=7, [4]=7, [5]=6, [8]=6, [9]=7, [10]=6, [11]=7, [12]=7, [13]=7, [14]=6, }
-- 11: { [4]=7, [5]=6, }
-- 12: { [4]=1, [5]=0, }
-- 15: { [4]=3, [5]=2, }
-- 17: { [2]=5, [3]=4, [4]=7, [5]=6, [6]=5, [7]=4, [12]=5, [14]=4, }
-- 18: { [4]=1, [5]=0, }
-- 19: { [2]=8, [3]=13, [4]=1, [5]=0, [6]=8, [7]=13, [12]=8, [14]=13, }
-- 20: { [2]=5, [3]=4, [4]=1, [5]=0, [6]=5, [7]=4, [12]=5, [14]=4, }
-- 21: { [4]=13, [5]=8, }
-- 22: { [4]=7, [5]=6, }
-- 25: { [6]=8, [7]=13, }
function shadexfog.createremap(palnum, remaptab)
local sht = engine.getshadetab(0)
engine.setshadetab(palnum, sht:remap16(remaptab))
end
-- Create 32 palookups corrensponding to different *shade levels* of a fog
-- palookup, called a "shade-x-fog" palookup set in the following.
--
-- Pals <startpalnum> .. <startpalnum>+31 will be taken.
-- <fogr>, <fogg>, <fogb>: intensities of the fog color, [0 .. 255]
function shadexfog.create(startpalnum, fogr, fogg, fogb)
local MAXPALNUM = 255-31-engine.RESERVEDPALS
if (not (startpalnum >= 1 and startpalnum <= MAXPALNUM)) then
error("invalid startpalnum, max="..MAXPALNUM, 2)
end
local basesht = engine.getshadetab(0)
-- Encode the shade in different pal numbers! The shade tables are
-- constructed with a fog in their place.
for dummyshade=0,31 do
local sht = engine.shadetab()
for f=0,31 do
for i=0,255 do
local r, g, b = engine.getrgb(basesht[dummyshade][i])
local nr, ng, nb =
(r*(32-f) + fogr*f) / 32,
(g*(32-f) + fogg*f) / 32,
(b*(32-f) + fogb*f) / 32
sht[f][i] = engine.nearcolor(nr, ng, nb)
end
end
engine.setshadetab(startpalnum + dummyshade, sht)
end
end
local function trans(what, startpalnum, fogintensity)
if (what.pal >= startpalnum and what.pal <= startpalnum+31) then
-- Auto-detect earlier translation with the same <startpalnum>.
what.shade = what.pal - startpalnum
end
local shade = min(max(what.shade, 0), 31)
what.pal = startpalnum + shade
what.shade = fogintensity
end
-- shadexfog.translate(startpalnum, fogintensity [, vis])
--
-- Translate the whole map for use with a shade-x-fog palookup set.
-- .pal becomes the <startpalnum> + former .shade
-- .shade becomes the <fogintensity> [0 .. 31]
-- If <vis> is passed and >= 0, set all sector's visibility to that value.
--
-- Notes:
-- - auto-detects when the translation has been applied with the *same*
-- <startpalnum> (if a different one is desired, must reload map).
-- - if shades < 0 or > 31 present, loss of information
function shadexfog.translate(startpalnum, fogintensity, vis)
for i=0,gv.numsectors-1 do
trans(sector[i].ceiling, startpalnum, fogintensity)
trans(sector[i].floor, startpalnum, fogintensity)
if (vis and vis >= 0) then
sector[i].visibility = vis
end
end
for i=0,gv.numwalls-1 do
trans(wall[i], startpalnum, fogintensity)
end
end
if (gv.LUNATIC_CLIENT == gv.LUNATIC_CLIENT_EDUKE32 and LUNATIC_FIRST_TIME) then
shadexfog.create(100, 255,255,255)
print("created shadexfog palookups")
end
---------- BASE SHADE TABLE TESTS ----------
-- sht = shadexfog.create_depth_shtab([palnum])
function shadexfog.create_depth_shtab(palnum)
local sht = engine.shadetab()
for s=0,31 do
for i=0,255 do
sht[s][i] = s
end
end
if (palnum) then
engine.setshadetab(palnum, sht)
end
return sht
end
function shadexfog.create_vismarker_shtab(palnum)
local sht = engine.getshadetab(0)
for i=0,255 do
sht[1][i] = 242
sht[30][i] = 245
end
if (palnum) then
engine.setshadetab(palnum, sht)
end
return sht
end
-- Basic test of whether for a color index i corresponding to a color (r,g,b),
-- getclosestcol() returns a color index ii corresponding to the same color.
-- (In the Duke3D palette, there are duplicates, so the requirement i==ii is
-- too strict.)
function shadexfog.test_nearcolor()
for i=0,255 do
local r, g, b = engine.getrgb(i)
local ii = engine.nearcolor(r, g, b)
local rr, gg, bb = engine.getrgb(ii)
if (r~=rr or g~=gg or b~=bb) then
printf("diff %d: %d,%d,%d %d,%d,%d", i, r,g,b, rr,gg,bb)
end
end
end
-- Change the .pal member of all sector ceilings/floors, walls and sprites to
-- <palnum>.
function shadexfog.challpal(palnum)
for i=0,gv.numsectors-1 do
sector[i].ceilingpal = palnum
sector[i].floorpal = palnum
end
for i=0,gv.numwalls-1 do
wall[i].pal = palnum
end
for i in sprite.all() do
sprite[i].pal = palnum
end
end
-- Create our version of the base shade table (palookup 0)
--
-- NOTE: Nope, the base shade table is NOT created by applying a linear ramp to
-- the base palette colors!!!
local function create_base_shtab(basesht)
local basesht = basesht or engine.getshadetab(0)
local sht = engine.shadetab()
sht[0] = basesht[0]
for sh=1,31 do
for i=0,255-16 do
-- NOTE that this fails, see BASESHT_0_NOT_IDENTITY:
-- assert(basesht[0][i] == i)
local r, g, b = engine.getrgb(i)
local f = 1
r = ((32-f*sh+0.5)*r)/32
g = ((32-f*sh+0.5)*g)/32
b = ((32-f*sh+0.5)*b)/32
r, g, b = max(0,r), max(0,g), max(0,b) -- if f is > 1
sht[sh][i] = engine.nearcolor(r, g, b)
end
for i=255-16+1,255 do
-- fullbrights
sht[sh][i] = basesht[0][i]
end
end
return sht
end
local function create_base_shtab_2(basesht)
local basesht = basesht or engine.getshadetab(0)
local perm16 = { [0]=0,1, 2,3, 5,4, 6,7, 8,13, 10,11, 12,9, 14,15 }
basesht = basesht:remap16(perm16)
local iperm16 = {}
for i=0,15 do
iperm16[perm16[i]] = i
end
local iperm = {}
for i=0,255 do
iperm[i] = 16*(iperm16[floor(i/16)]) + i%16
end
local baseidx = {}
for i=0,255-16 do
baseidx[i] = i < 192 and 32*floor(i/32) or 16*floor(i/16)
end
local sht = engine.shadetab()
for sh=0,31 do
for i=0,255-16 do
local bi = baseidx[i]
local cidx = bi + floor(((31-sh)*(i - bi))/31)
sht[sh][i] = iperm[cidx]
end
for i=255-16+1,255 do
-- fullbrights
sht[sh][i] = basesht[0][i]
end
end
return sht:remap16(iperm16)
end
local ismapster32 = (gv.LUNATIC_CLIENT == gv.LUNATIC_CLIENT_MAPSTER32)
if (ismapster32) then
-- Wrapper around engine.savePaletteDat() that errors on unexpected events.
function shadexfog.save(filename, palnum, blendnum, moreblends, lognumalphatabs)
local ok, errmsg, nummoreblends = engine.savePaletteDat(
filename, palnum, blendnum, moreblends, lognumalphatabs)
if (not ok) then
error(errmsg)
end
printf('Wrote base palette, shade and translucency tables to "%s".', filename)
if (nummoreblends > 0) then
printf(" Also wrote %d additional translucency tables.", nummoreblends)
end
end
function shadexfog.saveLookupDat(filename, lookups)
local ok, errmsg, numlookups = engine.saveLookupDat(filename, lookups)
if (not ok) then
error(errmsg)
end
printf('Wrote %d lookup tables and 5 base palettes to "%s".',
numlookups, filename)
end
end
-- Create our (failed) version of the base shade table at set it to palookup
-- number <palnum>.
-- <secver>: use second attempt?
function shadexfog.create0(palnum, secver)
local sht0 = secver and create_base_shtab_2() or create_base_shtab()
engine.setshadetab(palnum, sht0)
end
function shadexfog.test_create0()
local basesht = engine.getshadetab(0)
for i=0,255 do
if (basesht[0][i] ~= i) then
-- BASESHT_0_NOT_IDENTITY
printf("Base shade table at index %d: %d", i, basesht[0][i])
end
end
local sht = create_base_shtab(basesht)
local ok = true
for sh=1,31 do
for i=0,255 do
local ouri, origi = sht[sh][i], basesht[sh][i]
-- if (sht[sh][i] ~= basesht[sh][i]) then
if (math.abs(ouri - origi) > 1) then
printf("Constructed shade table DIFFERS AT shade %d index %d: orig %d ours %d",
sh, i, basesht[sh][i], sht[sh][i])
ok = false
goto out
end
end
end
::out::
if (ok) then
printf("Constructed shade table IDENTICAL WITH original one")
end
end
---------- Blending table tests ----------
-- shadexfog.create_trans(startblendidx, func [, numtables [, fullbrightsOK]])
--
-- <func>: must be
-- rr, gg, bb = f(r,g,b, R,G,B, level, numtables)
-- If reverse translucency bit clear, (r,g,b) is background and (R,G,B) is
-- foreground (incoming).
-- ('level' is the table index, from 1 to <numtables>)
-- <numtables>: number of tables to create, from <startblendidx> on. Default: 1
function shadexfog.create_trans(startblendidx, func, numtables, fullbrightsOK)
numtables = numtables or 1
local lastokcol = fullbrightsOK and 255 or 255-16
local tab = engine.blendtab()
for level=1,numtables do
for i=0,255 do
local r,g,b = engine.getrgb(i)
for j=0,255 do
local R,G,B = engine.getrgb(j)
local rr, gg, bb = func(r,g,b, R,G,B, level, numtables)
tab[i][j] = engine.nearcolor(rr, gg, bb, lastokcol)
end
end
engine.setblendtab(startblendidx + level-1, tab)
end
end
local function check_numtables(numtables)
if (numtables ~= nil) then
if (type(numtables) ~= "number" or not (numtables >= 1 and numtables <= 128)) then
error("invalid argument #2: must be a number in [1 .. 128]", 2)
end
if (bit.band(numtables, numtables-1) ~= 0) then
error("invalid argument #2: must be a power of two", 2)
end
end
end
-- shadexfog.create_alpha_trans(startblendidx [, numtables [, fullbrightsOK]])
--
-- Creates <numtables> blending tables of smooth alpha translucency, with
-- fractions 1/(2*numtables), 2/(2*numtables) ... numtables/(2*numtables).
-- <numtables> must be a power of two in [1 .. 128].
function shadexfog.create_alpha_trans(startblendidx, numtables, fullbrightsOK)
check_numtables(numtables)
shadexfog.create_trans(
startblendidx,
function(r,g,b, R,G,B, alpha, numtabs)
local f = alpha/(2*numtabs)
local F = 1-f
return f*r+F*R, f*g+F*G, f*b+F*B
end,
numtables, fullbrightsOK
)
end
-- shadexfog.create_additive_trans(startblendidx [, numtables [, fullbrightsOK]])
function shadexfog.create_additive_trans(startblendidx, numtables, fullbrightsOK)
shadexfog.create_trans(
startblendidx,
function(r,g,b, R,G,B, level, numtabs)
local f = level/numtabs
return min(f*r+R, 255), min(f*g+G, 255), min(f*b+B, 255)
end,
numtables, fullbrightsOK
)
end
-- shadexfog.create_brightpass_trans(startblendidx [, numtables [, fullbrightsOK]])
function shadexfog.create_brightpass_trans(startblendidx, numtables, fullbrightsOK)
shadexfog.create_trans(
startblendidx,
function(r,g,b, R,G,B, alpha, numtabs)
local a = alpha/numtabs
local F = 1 - min(a, (R+G+B) / (3*255))
local f = 1 - F
return f*r+F*R, f*g+F*G, f*b+F*B
end,
numtables, fullbrightsOK
)
end
if (not ismapster32) then
return shadexfog
end
--========== Mapster32 Lua menu hooks ==========--
local getnumber16 = engine.getnumber16
local GNF = engine.GETNUMFLAG
local GNF_BOOL = GNF.NEXTFREE
local df = GNF.RET_M1_ON_CANCEL -- default getnumber16() flags
local MAXUSERPALOOKUP = 256-1-8 -- KEEPINSYNC engine.lua:check_palidx()
-- wrapped_func = CreateMenuFunction(argdesc)
--
-- <argdesc>: table with [0]=<func> and then, entries { name, init, max [, noret] }
local function CreateMenuFunction(argdesc)
return function()
local func = argdesc[0]
assert(type(func) == "function")
local args = {}
for i=1,#argdesc do
local ad = argdesc[i]
assert(type(ad) == "table" and #ad == 3 or #ad == 4)
local moreflags = ad[4] or 0
args[i] = getnumber16(ad[1]..": ", ad[2], ad[3], bit.bor(df, moreflags))
if (bit.band(moreflags, GNF.NEG_ALLOWED)==0 and args[i] < 0) then
return
end
if (bit.band(moreflags, GNF_BOOL)~=0) then
args[i] = (args[i] > 0)
end
end
func(unpack(args))
end
end
-- Replace chevrons (angle brackets!) with printext16 markup.
local function replchev(matchstr) return "^15"..matchstr:sub(2,-2).."^O" end
-- Replace ASCII code escapes like \XXX. We can't use these escapes in Lua [[ ... ]] strings.
local function replascii(matchstr) return string.char(tonumber(matchstr)) end
-- Format a whole string for the menu system:
local function formatHelp(str)
return str:gsub(
"(%b<>)", replchev):gsub(
"%*(.-)%*", "^014%1^O"):gsub(
"%\\(%d+)", replascii)
end
----------
engine.clearMenu()
engine.registerMenuFunc(
"Create shadexfog palset",
CreateMenuFunction{
[0] = shadexfog.create,
{ "Starting palnum", 100, MAXUSERPALOOKUP-31 },
{ "Red fog color [0-255]", 0, 255 },
{ "Green fog color [0-255]", 0, 255 },
{ "Blue fog color [0-255]", 0, 255 },
},
formatHelp
[[
<shadexfog.create(startpalnum, fogr, fogg, fogb)>
<_______________________________________________>
Creates 32 shade tables corresponding to different *shade levels*
of a fog palookup, together called a *shade-x-fog* palookup set.
Pals <startpalnum> to <startpalnum>+31 will be taken.
<fogr>, <fogg>, <fogb>: intensities of the fog color, [0 .. 255]
]]
)
engine.registerMenuFunc(
"Translate map for shxfog",
CreateMenuFunction{
[0] = shadexfog.translate,
{ "Starting palnum", 100, MAXUSERPALOOKUP-31 },
{ "Fog intensity [0-31]", 0, 31 },
{ "Change all sectors' visibility to (Esc: don't)", 0, 255, GNF.NEG_ALLOWED },
},
formatHelp
[[
<shadexfog.translate(startpalnum, fogintensity [, vis])>
<______________________________________________________>
Translates the whole map for use with a shade-x-fog palookup set.
.pal becomes the <startpalnum> + former .shade
.shade becomes the <fogintensity> [0 .. 31]
If <vis> is passed and >= 0, set all sector's visibility to that
value.
*Notes:*
- auto-detects when the translation has been applied with the *same*
<startpalnum> (if a different one is desired, must reload map).
- if shades > 31 or < 0 present, there is loss of information
]]
)
engine.registerMenuFunc(
"Change pal of everything",
CreateMenuFunction{
[0] = shadexfog.challpal,
{ "Pal to change to", 0, MAXUSERPALOOKUP },
},
formatHelp
[[
<shadexfog.challpal(palnum)>
<__________________________>
Changes the .pal member of all sector ceilings/floors, walls and
sprites to <palnum>.
]]
)
engine.registerMenuFunc(
"Create alpha trans. tabs",
CreateMenuFunction{
[0] = shadexfog.create_alpha_trans,
{ "Starting blendnum", 1, 255 },
{ "Number of blending tables", 32, 255 },
{ "Fullbright result colors OK?", 0, 1, GNF_BOOL },
},
formatHelp
[=[
<shadexfog.create_alpha_trans(startblend [, numtables [, fullbriOK]])>
<____________________________________________________________________>
Creates <numtables> blending tables of smooth alpha translucency,
starting with the blending number <startblend>, with values of alpha
1/(2\255<numtables>), 2/(2\255<numtables>) ... <numtables>/(2\255<numtables>).
<numtables> must be a power of two in [1 .. 128].
<fullbriOK>: should fullbright color indices (>= 240) be permitted as
the blending result of two color indices?
]=]
)
engine.registerMenuFunc(
"Create addtv. trans. tabs",
CreateMenuFunction{
[0] = shadexfog.create_additive_trans,
{ "Starting blendnum", 1, 255 },
{ "Number of blending tables", 32, 255 },
{ "Fullbright result colors OK?", 0, 1, GNF_BOOL },
},
formatHelp
[=[
<shadexfog.create_additive_trans(startbl [, numtables [, fullbriOK]])>
<____________________________________________________________________>
Creates <numtables> blending tables of smooth additive translucency,
starting with the blending number <startbl>, with factors for the
background color
1/<numtables>, 2/<numtables> ... <numtables>/<numtables>.
<numtables> must be a power of two in [1 .. 128].
<fullbriOK>: should fullbright color indices (>= 240) be permitted as
the blending result of two color indices?
]=]
)
engine.registerMenuFunc(
"Create bri.pass tr. tabs",
CreateMenuFunction{
[0] = shadexfog.create_brightpass_trans,
{ "Starting blendnum", 1, 255 },
{ "Number of blending tables", 32, 255 },
{ "Fullbright result colors OK?", 0, 1, GNF_BOOL },
},
formatHelp
[=[
<shadexfog.create_brightpass_trans(startbl [, numtabs [, fullbriOK]])>
<____________________________________________________________________>
Creates <numtabs> blending tables of "brightpass" translucency,
starting with the blending number <startbl>, with fractions
1/<numtables>, 2/<numtables> ... <numtables>/<numtables>.
<fullbriOK>: should fullbright color indices (>= 240) be permitted as
the blending result of two color indices?
]=]
)
engine.registerMenuFunc(
"Create base shade table",
CreateMenuFunction{
[0] = shadexfog.create0,
{ "Pal number", 100, MAXUSERPALOOKUP },
{ "Second attempt?", 1, 1, GNF_BOOL },
},
formatHelp
[[
<shadexfog.create0(palnum, secver)>
<_________________________________>
Creates our version of the base shade table at set it to palookup
number <palnum>.
<secver>: use second attempt instead of the first? This one is more
similar to the base shade table shipped with Duke3D, but still
shows significant differences.
]]
)
engine.registerMenuFunc(
"Create c.index remapping",
function()
local palnum = getnumber16("Pal number: ", 100, MAXUSERPALOOKUP)
if (palnum < 0) then return end
local remaptab = {}
while (true) do
local srchex = getnumber16("Source hexadecatuple (0: finish): ", 0, 14)
if (srchex < 0) then return end
local dsthex = getnumber16("Destn. hexadecatuple (0: finish): ", 0, 14)
if (dsthex < 0) then return end
if (srchex == 0 and dsthex == 0) then
break
end
remaptab[srchex] = dsthex
end
shadexfog.createremap(palnum, remaptab)
end,
formatHelp
[[
<shadexfog.createremap(palnum, remaptab)>
<_______________________________________>
Creates a color index remapping expressed as mappings of sexdeca-
tuples (16-tuples) of the base palette at pal <palnum>.
Duke3D's default base palette can be considered to consist of six
ramps of 32 colors each, three ramps of 16 colors each and a
remaining fullbright color set. The sexdecatuples are as follows:
< 0, 1>: gray ramp
< 2, 3>: skin color ramp
< 5, 4>: blue ramp (note that the 16-tuples are in reverse order)
< 6, 7>: nightvision yellow/green
< 8, 13>: red ramp
< 10, 11>: almost gray ramp, but with a slight red hue
< 9>: yellow (slightly more red than green)
< 12>: "dirty" orange
< 14>: blue-purple-red
]]
)
local function getNumberRange(what, whating)
local str = engine.getstring("Additional "..what.." numbers (e.g. '64,100-131,255'): ")
if (str == nil) then return end
if (str == "") then return {} end
if (not str:find("^[%d,%-]+$")) then
error("Additional "..whating.." numbers string must contain only digits or ',' or '-'", 2)
end
local moreblends = {}
local didnumstr = {}
for n1, n2 in str:gmatch("(%d+)%-(%d+)") do -- parse number ranges
moreblends[#moreblends+1] = { tonumber(n1), tonumber(n2) }
didnumstr[n1] = true
didnumstr[n2] = true
end
for n in str:gmatch("%d+") do -- parse single numbers
if (not didnumstr[n]) then
moreblends[#moreblends+1] = tonumber(n)
end
end
return moreblends
end
engine.registerMenuFunc(
"Save pal+sh+trans DAT f.",
function()
local filename = engine.getstring("File name: ")
if (filename == nil) then return end
local palnum = getnumber16("Pal number of base shade table: ", 0, MAXUSERPALOOKUP)
if (palnum < 0) then return end
local blendnum = getnumber16("Blendnum of base transluc. table: ", 0, 255)
if (blendnum < 0) then return end
local moreblends = getNumberRange("blend", "blending")
if (moreblends == nil) then return end
local lognumalphatabs
if (#moreblends > 0) then
lognumalphatabs = getnumber16("log2 of last alpha blending table index (1-7, 0: none): ", 0, 7)
if (lognumalphatabs < 0) then return end
if (lognumalphatabs == 0) then lognumalphatabs = nil end
end
shadexfog.save(filename, palnum, blendnum, moreblends, lognumalphatabs)
end,
formatHelp
[[
<shadexfog.save(filename, palnum, blendnum, moreblends, lognumalpha)>
<___________________________________________________________________>
Writes out a full PALETTE.DAT-formatted file named <filename> with the
base shade table numbered <palnum> and the base translucency table
numbered <blendnum>.
Finally, you are asked to specify additional blending tables that can
be stored in EDuke32's extended PALETTE.DAT format. If one or more
additional blending table is specified, you are also queried for the
log2 of the last alpha blending table index, <lognumalpha>. Since alpha
blending tables are assumed to be set up at indices 1 to
exp(2, <lognumalpha>), it is also the log2 of their total count.
]]
)
engine.registerMenuFunc(
"Save lookups DAT file",
function()
local filename = engine.getstring("File name: ")
if (filename ~= nil and filename ~= "") then
local lookups = {
-- Duke3D 1.5 LOOKUP.DAT order
1,2,6,7,8, 3,4,5,9,10,
12,13,15,16,18, 19,11,14,17,20,
21,22,23,24,25
}
local morelookups = getNumberRange("lookup", "lookup")
if (morelookups == nil) then return end
if (#morelookups > 0) then
for i=1,#morelookups do
lookups[#lookups+1] = morelookups[i]
end
end
shadexfog.saveLookupDat(filename, lookups)
end
end,
formatHelp
[[
<shadexfog.saveLookupDat(filename, lookups)>
<__________________________________________>
Saves the color index lookups (i.e. first 256 values of each shade
table) of the pal numbers which have lookups in Duke3D's unaltered
LOOKUP.DAT, plus optional ones provided by the user.
The default ones are, in this order:
1,2,6,7,8, 3,4,5,9,10, 12,13,15,16,18, 19,11,14,17,20, 21,22,23,24,25.
(All pal numbers from 1 to 25 are present.)
<filename>: the name of the LOOKUP.DAT-formatted file to create
]]
)
engine.registerMenuFunc("_________DEBUG_________", function() end
--[[
,
" \01\02\03\04\05\06\07\08\09\10\11\12\13\14\15\
\16\17\18\19\20\21\22\23\24\25\26\27\28\29\30\31\
\128\129\130\131\132\133\134\135\136\137\138\139\140\141\142\143\
\144\145\146\147\148\149\150\151\152\153\154\155\156\157\158\159\
\160\161\162\163\164\165\166\167\168\169\170\171\172\173\174\175\
\176\177\178\179\180\181\182\183\184\185\186\187\188\189\190\191\
\192\193\194\195\196\197\198\199\200\201\202\203\204\205\206\207\
\208\209\210\211\212\213\214\215\216\217\218\219\220\221\222\223\
\224\225\226\227\228\229\230\231\232\233\234\235\236\237\238\239\
\240\241\242\243\244\245\246\247\248\249\250\251\252\253\254\255"
--]]
)
engine.registerMenuFunc("Setup dbg. water basepal", engine.setupDebugBasePal,
formatHelp
[[
<engine.setupDebugBasePal()>
<__________________________>
Overwrites the water base palette with one where each 16-tuple
(except the fullbrights) consists of a single representative
color.
This can be used to get a quick glimpse about what ramps are present
in particular tiles. With this information, custom lookups can be
created more directedly with the <Create c.index remapping> menu
entry.
]]
)
engine.registerMenuFunc(
"Create depth shade tab",
CreateMenuFunction{
[0] = shadexfog.create_depth_shtab,
{ "Pal number", 100, MAXUSERPALOOKUP },
},
formatHelp
[[
<shadexfog.create_depth_shtab(palnum)>
<____________________________________>
Creates a shade table for debugging purposes at pal <palnum>.
For every color index, the shade table maps shade index <i> to
color index <i> (of the first ramp of gray colors, assuming Build
has loaded a base shade table with 32 shade levels).
]]
)
engine.registerMenuFunc(
"Create vismarker sh. tab",
CreateMenuFunction{
[0] = shadexfog.create_vismarker_shtab,
{ "Pal number", 100, MAXUSERPALOOKUP },
},
formatHelp
[[
<shadexfog.create_vismarker_shtab(palnum)>
<________________________________________>
Creates a shade table for debugging purposes at pal <palnum>.
For every color index, the shade table maps shade index 1 to
a ^14bright yellow^O color and shade index 30 to a ^13purple^O color.
Thus, it can be useful in visualizing the limits of the
fog/visibility attenuation.
]]
)
engine.registerMenuFunc("Linearize default basep.", engine.linearizeBasePal,
formatHelp
[[
<engine.linearizeBasePal()>
<_________________________>
Overwrites the default base palette with one where certain ramps have
their attenuation linearized. This is mainly useful for debugging
purposes as it excludes the effect of this nonlinearity for
comparison of fog/visibility between classic and OpenGL modes.
]]
)
do
return shadexfog
end

View file

@ -1,139 +0,0 @@
// Timing test for four 0..MAXSPRITES-1 access loops,
// fwd->back->fwd->back.
// Results with a LTO=1 RELEASE=1 C-CON build on x86: 42 ms for N=10.
// (Compare with Lunatic results in test.elua)
define N 10
gamevar n 0 0
gamevar i 0 0
gamevar t 0 0
gamevar t2 0 0
define MAXSPRITES 16384
define MAXSPRM1 16383 // MAXSPRITES-1
onevent EVENT_ENTERLEVEL
getticks t
setvar n 0
whilevarn n N
{
setvar i 0
whilevarvarn i MAXSPRITES
{
setactor[i].blend 1
addvar i 1
}
setvar i MAXSPRM1
whilevarvarn i -1
{
setactor[i].shade 1
subvar i 1
}
setvar i 0
whilevarvarn i MAXSPRITES
{
setactor[i].xoffset 0
addvar i 1
}
setvar i MAXSPRM1
whilevarvarn i -1
{
setactor[i].yoffset 0
subvar i 1
}
addvar n 1
}
getticks t2
subvarvar t2 t
// qsprintf test
redefinequote 116 0..MAXSPITES-1
redefinequote 117 in total
redefinequote 114 %d x four %s iterations took %d ms %s
qsprintf 115 /*<-*/ 114 /*args:*/ n 116 t2 117
echo 115
endevent
////////// Periodically alpha-faded liztroop //////////
gamevar alpha 0 0
gamevar tmp 0 0
onevent EVENT_GAME // XXX: better: in EVENT_EGS + EVENT_LOADACTOR
ifactor LIZTROOP
{
getactor[THISACTOR].mdflags tmp
orvar tmp 16
setactor[THISACTOR].mdflags tmp
}
endevent
define NUMALPHATABS 128
define C1 0 //must be log2(128/NUMALPHATABS)
define C2 257 // must be 2*NUMALPHATABS+1
define ALPHA_TABS_BY_EDUKE32 1 // assume alpha->blend handling by EDuke32
onevent EVENT_ANIMATESPRITES
setvarvar tmp totalclock
shiftvarl tmp 2
sin alpha tmp // alpha is now in [-2^14 .. 2^14]
shiftvarr alpha 7 // [-2^7 .. 2^7]
addvar alpha 128 // [0 .. 256]
shiftvarr alpha C1 // [0 .. 2*NUMALPHATABS]
ifvarn ALPHA_TABS_BY_EDUKE32 0
{
subvar alpha 1
ifvarl alpha 0 setvar alpha 0
setactor[THISACTOR].alpha alpha
break
}
ifvare alpha 0
{
// clear translucent bits, 0xfdfe == 0xffff-(1+512)
gettspr[THISACTOR].tsprcstat tmp
andvar tmp 0xfdfe
settspr[THISACTOR].tsprcstat tmp
}
else
{
gettspr[THISACTOR].tsprcstat tmp
orvar tmp 2
settspr[THISACTOR].tsprcstat tmp
// Assume blending tables [1 .. NUMALPHATABS] are installed, like
// generated by shadexfog.lua:create_alpha_trans(1, NUMALPHATABS).
ifvarg alpha NUMALPHATABS
{
setvarvar tmp C2
subvarvar tmp alpha
setvarvar alpha tmp
// Set "reverse translucent" cstat bit
gettspr[THISACTOR].tsprcstat tmp
orvar tmp 512
settspr[THISACTOR].tsprcstat tmp
}
else
{
// Clear cstat bit 512
gettspr[THISACTOR].tsprcstat tmp
andvar tmp 0xfdff
settspr[THISACTOR].tsprcstat tmp
}
settspr[THISACTOR].tsprblend alpha
}
endevent

View file

@ -1,31 +0,0 @@
gamevar tmp 0 0
gamevar cs 0 0
state setup_animatesprites
getactor[THISACTOR].mdflags tmp
orvar tmp 16
setactor[THISACTOR].mdflags tmp
ends
onevent EVENT_EGS
state setup_animatesprites
endevent
onevent EVENT_LOADACTOR
state setup_animatesprites
endevent
// Assuming a PALETTE.DAT with e.g.:
// blend 0: 50/50 alpha
// blend 1: factor 1.0 additive
//
// See discussion starting from
// http://forums.duke4.net/topic/775-eduke32-20-and-polymer/page__view__findpost__p__213408
onevent EVENT_ANIMATESPRITES
getactor[THISACTOR].cstat cs
ifvarand cs 2 ifvarand cs 512
setactor[THISACTOR].blend 1
else
setactor[THISACTOR].blend 0
endevent

View file

@ -1,128 +0,0 @@
gamevar j 0 0
gamevar k 0 0
definequote 400 QWE
definequote 500 ===
onevent EVENT_ENTERLEVEL
switch j
case 0
case -1
userquote 29
break
case 1
userquote 30
break
default
userquote 31
break
case 0
redefinequote 400 ASD
userquote 400
break
endswitch
addvar j 1
switch j
case 0
case -2
userquote 29
break
case 3
userquote 30
break
default
userquote 31
break
endswitch
// test "-" codegen (must not generate comment!).
subvar j -3
// result:
// ASD (from second case 0)
// BRIGHTNESS LEVEL: THREE (from default)
////////// test nested switch/while //////////
ifvare 0 1
break // outer level: "do return end"
setvar j 0
whilevarn j 3
{
addvar j 1
userquote 500
break // while is inner: "continue"
userquote 400
} // 3x "===", no quote 400
setvar j 0
whilevarn j 1
{
switch j
case 0
addvar j 1
// switch is inner: "do return end" (likewise with following
// "break" commands):
break
addvar j 1
break
case 1
nullop
break
default
nullop
break
endswitch
}
// NOTE: this is syntactically invalid, though may be useful:
// case X:
// default:
// <code...>
setvar j 0
switch j
case 0,
whilevarn j 1
{
addvar j 1
break // while is inner: "continue"
}
break
endswitch
/*
// LunaCON only: nested switches.
// Untested, but the generated code looks sane.
switch j
case 0,
switch k
default
userquote 29 // BRIGHTNESS LEVEL: ONE
endswitch
case 1,
userquote 30
endswitch
*/
// nested whilevar*n
setvar j -1
setvar k -1
whilevarn j 0
{
whilevarn k 0
{
setvar k 0
break
setvar k 1
}
setvar j 0
break
setvar j -1
}
endevent

View file

@ -1,115 +0,0 @@
#!/usr/bin/env luajit
local ffi = require "ffi"
local io = require "io"
local os = require "os"
local math = require "math"
local tablesfn = arg[1]
local taylor_n = tonumber(arg[2])
if (tablesfn==nil) then
-- taylor_n is a number: use that n for Taylor approximation of atan
-- taylor_n==nil: use math.atan
print("Usage: ./tables.lua ../path/to/TABLES.DAT [talyor_n]")
return 1
end
local fd, errmsg = io.open(tablesfn)
if (fd==nil) then
print("Failed opening "..tablesfn..": "..errmsg)
return 1 -- XXX: not working, neither os.exit(1) as of 20120901
end
local sintab = ffi.new("int16_t [?]", 2048)
local sinstr = fd:read(2048*2)
if (sinstr==nil or #sinstr<2048*2) then
print("Failed reading whole sine table")
fd:close()
return 1
end
ffi.copy(sintab, sinstr, 2048*2)
sinstr = nil
--== sintable
local oursin = ffi.new("int16_t [?]", 2048)
local BANG2RAD = math.pi/1024
for a=0,2047 do
local s = 16384*math.sin(a*BANG2RAD)
oursin[a] = s
-- if (a < 1024) then print(a..": "..s) end
end
--print("")
for a=0,2047 do
local dif = oursin[a]-sintab[a]
assert(dif == 0)
--[[
if (dif ~= 0) then
print(a..": "..dif)
end
--]]
if (a < 512) then
assert(sintab[a] == sintab[1024-a])
end
if (a >= 1024) then
assert(sintab[a] == -sintab[a-1024])
end
end
--== radarang
local ratab = ffi.new("int16_t [?]", 640)
local rastr = fd:read(640*2)
fd:close()
ffi.copy(ratab, rastr, 640*2)
rastr = nil
local function atan_taylor(x, n)
local absx = math.abs(x)
local at = 0
for i=0,n do
local twoip2 = 2*i+1
local xp = x^twoip2
local pmone = (-1)^i
if (absx < 1) then
at = at + (pmone * xp)/twoip2
else
at = at + pmone/(xp * twoip2)
end
end
if (absx > 1) then
at = math.pi/2 - at
end
return at
end
local atan
if (taylor_n==nil) then
atan = math.atan
else
atan = function (x) return atan_taylor(x, taylor_n) end
end
for i=0,639 do
local at = -64*atan((640-0.5-i)/160)/BANG2RAD
local ra = ratab[i]
at = math.ceil(at)
-- print(i ..": ".. ra .." ".. at .." ".. at/ra .." ".. at-ra)
end
--[[
local ourra = ffi.new("int16_t [?]", 640)
--]]

View file

@ -1,148 +0,0 @@
#!/usr/bin/env luajit
-- Usage: luajit bittest.lua <number or "x"> [-ffi] [-bchk]
local require = require
local string = require "string"
local math = require "math"
local bitar = require "bitar"
local print = print
local tonumber = tonumber
local getticks
if (string.dump) then
-- stand-alone
local os = require "os"
function getticks()
return os.clock()*1000
end
else
-- embedded
getticks = gv.gethiticks
module(...)
end
-- based on example from http://bitop.luajit.org/api.html
local m = string.dump and tonumber(arg[1]) or 1e7
local maxidx = math.floor(m/32)
local ffiar_p, boundchk_p = false, false
if (string.dump) then
if (arg[2]=="-ffi" or arg[3]=="-ffi") then
ffiar_p = true
end
if (arg[2]=="-bchk" or arg[3]=="-bchk") then
boundchk_p = true
end
end
function sieve()
local count = 0
local p = {}
if (ffiar_p) then
-- stand-alone using unchecked int32_t array: on x86_64 approx. 80 ms
-- for m = 1e7 (enabling bound checking makes it be around 100 ms)
local ffi = require "ffi"
local pp = ffi.new("int32_t [?]", maxidx + 1)
p = pp
if (boundchk_p) then
local mt = {
__index = function(tab,idx)
if (idx >= 0 and idx <= maxidx) then
return pp[idx]
end
end,
__newindex = function(tab,idx,val)
if (idx >= 0 and idx <= maxidx) then
pp[idx] = val
end
end,
}
p = setmetatable({}, mt)
end
for i=0,maxidx do p[i] = -1; end
else
p = bitar.new(m, 1)
end
local t = getticks()
if (ffiar_p) then
local bit = require "bit"
for i=2,m do
if (bit.band(p[bit.rshift(i, 5)], bit.lshift(1, i)) ~= 0) then
count = count + 1
for j=i+i,m,i do
local jx = bit.rshift(j, 5)
p[jx] = bit.band(p[jx], bit.rol(0xfffffffe, j));
end
end
end
else
for i=2,m do
if (p:isset(i)) then
count = count + 1
for j=i+i,m,i do p:set0(j); end
end
end
end
-- When using bitar module: x86_64: approx. 110 ms
print(string.format("[%s] Found %d primes up to %d (%.02f ms)",
ffiar_p and "ffi-ar"..(boundchk_p and ", bchk" or "") or "tab-ar",
count, m, getticks()-t))
return p, count
end
if (string.dump) then
local function printf(fmt, ...) print(string.format(fmt, ...)) end
local p, count = sieve()
local t = getticks()
if (ffiar_p) then
return
end
-- test serialization
local ser = tostring(p)
local maxbidx_str = string.match(ser, '%(([0-9]+),')
local p2 = bitar.new(tonumber(maxbidx_str), string.match(ser, "'(.*)'"))
printf("serialization + new: %.02f ms", tostring(getticks()-t))
assert(p==p2)
if (m >= 2) then
assert(#p == count+2) -- +2 is because 0 and 1 are set even though they're not primes
end
if (not ffiar_p) then
math.randomseed(os.time())
local maxbidx = math.random(0, 65536)
local p3 = bitar.new(maxbidx, 1)
assert(#p3 == maxbidx+1) -- bits 0 to maxbidx inclusive are set
end
--[[
print(p)
print(p-p) -- test set difference
print(-p)
--]]
-- Set difference of self with self is the same as set intersection of self
-- with complement of self:
assert(p-p == p*(-p))
end

View file

@ -1,89 +0,0 @@
#!/usr/bin/env luajit
local ffi = require "ffi"
local math = require "math"
local os = require "os"
local xmath = require "xmath"
local ldist = xmath.ldist
local sqrt = math.sqrt
local function printf(fmt, ...)
print(string.format(fmt, ...))
end
local function edist(p1, p2)
return sqrt(p1.x*p1.x + p2.x*p2.x)
end
-- z dummy is so that there's no error with xmath.rotate()
local vec2 = ffi.typeof("struct { int32_t x, y, z /* dummy */; }")
local numpoints = tonumber(arg[1]) or 1e4
local Nsq = numpoints*numpoints
printf("number of points: %d, testing %d distances", numpoints, Nsq)
local B = 8192
local pts = {}
for i=1,numpoints do
pts[i] = vec2(math.random(-B, B), math.random(B, -B))
end
-- test edist
local t = os.clock()
local sum = 0
for i=1,numpoints do
for j=1,numpoints do
sum = sum+edist(pts[i], pts[j])
end
end
t = os.clock()-t
printf("edist: %.03fns per call, mean=%.03f", (1e9*t)/Nsq, sum/Nsq)
-- test ldist
t = os.clock()
local sum = 0
for i=1,numpoints do
for j=1,numpoints do
sum = sum+ldist(pts[i], pts[j])
end
end
t = os.clock()-t
printf("ldist: %.03fns per call, mean=%.03f", (1e9*t)/Nsq, sum/Nsq)
-- test rotation
t = os.clock()
-- from control.lua (the CON version of rotatepoint)
local function _rotatepoint(pivotx, pivoty, posx, posy, ang)
local pos = xmath.ivec3(posx, posy)
local pivot = xmath.ivec3(pivotx, pivoty)
pos = xmath.rotate(pos, ang, pivot):toivec3()
return pos.x, pos.y
end
sum = 0
for i=1,numpoints do
for j=1,numpoints do
-- local p = xmath.rotate(pts[i], j, pts[j])
-- sum = sum+p.x
sum = sum + _rotatepoint(pts[j].x, pts[j].y, pts[i].x, pts[i].y, j)
end
end
t = os.clock()-t
printf("rotate: %.03fns per call", (1e9)/Nsq)
-- Results (helixhorned x86, x86_64)
-- number of points: 10000, testing 100000000 distances
-- edist: 6.300ns per call, mean=6286.597
-- ldist: 17.600ns per call, mean=8692.612
-- rotate: 10.000ns per call [even with _rotatepoint()!]

View file

@ -1,85 +0,0 @@
#!/usr/bin/env luajit
local os = require("os")
local xmath = require("xmath")
-- XXX: perf regression? (See below PERF_REGRESSION)
-- No, happens only with Clang build. (Why?)
local N = os.exit and (arg[1] and tostring(arg[1])) or 1e5 --1e6
local A,B = {}, {}
local V,W = {}, {}
local randvec
local args = { ... }
local ourname = args[1]
if (os.exit) then
local math = require("math")
randvec = function()
return xmath.vec2(math.random(), math.random())
end
print("Running stand-alone. ourname: "..tostring(ourname))
else
local randgen = require("randgen")
local s = randgen.new(true)
-- NOTE: factoring out the inner s:getdbl() into a separate function
-- reduces performance seriously (about an order of magnitude!)
randvec = function()
return xmath.vec2(s:getdbl(), s:getdbl())
end
-- Test optional arguments from our_require().
printf("Running %s embedded with opt arg %s", ourname, tostring(args[2]))
end
local t1 = os.clock()
if (os.exit == nil) then
local randgen = require("randgen")
local r = randgen.new(true)
for i=1,4*2*N do
-- This is to test the performance compared to a direct
-- ffiC.rand_jkiss_dbl() call in randgen.lua
r:getdbl()
end
end
local t2 = os.clock()
-- init random points and vectors
for i=1,N do
A[i] = randvec()
B[i] = randvec()
V[i] = randvec()
W[i] = randvec()
end
local t3 = os.clock()
local v = xmath.vec2(0, 0)
for i=1,N do
local intersp = xmath.intersect(A[i],V[i], B[i],W[i], true)
if (intersp ~= nil) then
v = v + intersp
end
end
local t4 = os.clock()
-- x86_64 (embedded): approx. 200 ms (vs. the 100 ms of direct
-- ffiC.rand_jkiss_dbl()):
-- x86: 170 ms
print("getdbl: ".. 1000*(t2-t1))
print("genpoints: ".. 1000*(t3-t2)) -- x86_64: 500 ms, x86: 700 ms
print("intersect: ".. 1000*(t4-t3)) -- x86_64, x86: about 35 ms <- thanks to allocation sinking (else, about 500 ms?)
print("result: ".. tostring(v))
-- PERF_REGRESSION: with N==1e6 getdbl, genpoints now about 1000ms from EDuke32!

View file

@ -1,81 +0,0 @@
local require = require
local con = require("con")
local bit = require("bit")
local math = require("math")
local printf = printf
local tostring = tostring
local rs = con.rotatesprite
local gameevent = gameevent
local gv = gv
local player = player
module(...) --====================
test_gamevar = 123
local a_local_gamevar = "yes, this one too"
test_gamevar2 = 'qwe'
a_table = { ELT1='ELT1!', ELT2=4444, ATAB={ q=333, [4]="!four!", w=444, ["true"]=false } }
local ref_to_a_table = a_table
ref_to_tabtab = ref_to_a_table.ATAB
local l_ref_to_tabtab = ref_to_tabtab
ref_to_a_table.selfref = a_table
a_table[false] = { 1,2,3,con.actorvar(512) }
require "end_gamevars" --==========
not_a_gamevar = "no"
printf("a_table.ATAB[4]=%s", tostring(a_table.ATAB[4]))
a_table.ATAB[4] = "!FOUR!"
local DOT1x5 = 3135
local BAR1x5 = 3163
local function draw_hline_dotted(x1, x2, y, pal,stat)
for x=x1,x2,2 do
local pl = player[0]
local tile = (x==x1 or x==x2) and BAR1x5 or DOT1x5
if (pl.curr_weapon==2) then
x = x + 16*math.sin(2*math.pi*gv.totalclock/120)
elseif (pl.curr_weapon==1) then
x = x + (pl.ang - 1024)/100
end
rs(x,y, 65536, 0, tile, 0,pal,stat,0, 0,0,gv.xdim-1,gv.ydim-1)
end
end
local function draw_two_hlines(y, stat)
if (player[0].curr_weapon ~= 3) then
stat = bit.bor(stat, 8)
end
draw_hline_dotted(0,160, y, 0, stat)
-- XXX: rotatesprite(*, 320, ...) with stat 1024 and classic draws out of
-- bounds (wraps to lower left corner). (Or is it the left one? In any case,
-- weird stuff happens.)
draw_hline_dotted(162,318, y, 2, stat)
end
local function rotatesprite_test()
local stats = { [0]=0, 256, 512, 1024 }
for i=0,3 do
local stat = stats[i]
draw_two_hlines(50+25*i, stats[i])
end
-- NUKEBUTTON
rs(30,170, 32768, 2047*((gv.totalclock/240)%1), 142, 0,0,8+1024,0, 0,0,gv.xdim-1,gv.ydim-1)
end
gameevent{gv.EVENT_DISPLAYREST, rotatesprite_test}

View file

@ -1,51 +0,0 @@
// This CON module provides 'state thisactor_getzrange', reimplementing
// VM_GetZRange() from the C source. This routine obtains actor[].ceilingz and
// actor[].floorz for the current actor (THISACTOR), using getzrange() with the
// initial z position displaced by -256.
//
// Original author: Helixhorned.
// In
gamevar tagz_x 0 0
gamevar tagz_y 0 0
gamevar tagz_z 0 0
gamevar tagz_sectnum 0 0
// Temp
gamevar tagz_cstat 0 0
// Out
gamevar tagz_ceilz 0 0
gamevar tagz_ceilhit 0 0
gamevar tagz_florz 0 0
gamevar tagz_florhit 0 0
// ZOFFSET in the EDuke32 source.
define TAGZ_ZOFFSET 256
// In the EDuke32 source, the <walldist> passed to VM_GetZRange().
define TAGZ_WALLDIST 127
// Updates the current actor's actor[].floorz and actor[].ceilngz members in
// the same way A_GetZRange() does.
state thisactor_getzrange
setvarvar tagz_x sprite[THISACTOR].x
setvarvar tagz_y sprite[THISACTOR].y
setvarvar tagz_z sprite[THISACTOR].z, subvar tagz_z TAGZ_ZOFFSET
setvarvar tagz_sectnum sprite[THISACTOR].sectnum
// Back up and clear cstat.
setvarvar tagz_cstat sprite[THISACTOR].cstat
setactor[THISACTOR].cstat 0
getzrange tagz_x tagz_y tagz_z tagz_sectnum
/*out:*/ tagz_ceilz tagz_ceilhit tagz_florz tagz_florhit
/*in:*/ TAGZ_WALLDIST CLIPMASK0
// Restore cstat.
setactor[THISACTOR].cstat tagz_cstat
// Set actor[] members for the current actor.
setactor[THISACTOR].htceilingz tagz_ceilz
setactor[THISACTOR].htfloorz tagz_florz
ends

View file

@ -1,5 +0,0 @@
onevent EVENT_DRAW3DSCREEN
rotatesprite 40 80 131072 0 2523 0 0 330 windowx1 windowy1 windowx2 windowy2
rotatesprite 280 80 131072 0 2523 0 0 522 windowx1 windowy1 windowx2 windowy2
endevent

View file

@ -1,60 +0,0 @@
gamevar snd 351 0 // thunder sound
gamevar tmp 0 0
gamevar tmp2 0 0
gamevar shoots 2605 2 // RPG
gamevar WEAPON1_SHOOTS 2605 0
define MORTER2 1653
// NOTE: the custom MORTER2 doesn't spawn FRAMEEFFECT like the MORTER, which
// has some hard-wired code running.
defineprojectile MORTER2 PROJ_WORKSLIKE 6150
defineprojectile MORTER2 PROJ_SPAWNS EXPLOSION2
defineprojectile MORTER2 PROJ_SOUND RPG_SHOOT
defineprojectile MORTER2 PROJ_VEL 600
defineprojectile MORTER2 PROJ_EXTRA 165
defineprojectile MORTER2 PROJ_EXTRA_RAND 10
defineprojectile MORTER2 PROJ_DROP -200 // tested in test.elua
defineprojectile MORTER2 PROJ_ISOUND PIPEBOMB_EXPLODE
defineprojectile MORTER2 PROJ_HITRADIUS 2800
defineprojectile MORTER2 PROJ_BOUNCES 4
defineprojectile MORTER2 PROJ_OFFSET 128
defineprojectile MORTER2 PROJ_CLIPDIST 24
defineprojectile MORTER2 PROJ_TRAIL -1 // overridden in test.elua
defineprojectile MORTER2 PROJ_TNUM 6
defineprojectile MORTER2 PROJ_USERDATA 32 // test setting initial value
onevent EVENT_GAME
setvarvar WEAPON1_FIRESOUND snd
// setvarvar WEAPON1_SHOOTS shoots
endevent
definequote 400 Failed setting projectile userdata
useractor notenemy 909 // tree trunk
ifcount 120
{
zshoot -4096 BOUNCEMINE
// userdata: bitwise-OR in 64
getprojectile[MORTER2].userdata tmp
orvar tmp 64
setprojectile[MORTER2].userdata tmp
ezshoot -4096 MORTER2
getthisprojectile[RETURN].vel tmp
randvar tmp2 500
subvarvar tmp tmp2
setthisprojectile[RETURN].vel tmp
resetcount
// userdata: must be 32+64 now
getthisprojectile[RETURN].userdata tmp
ifvarn tmp 96
quote 400
}
enda
onevent EVENT_JUMP
tossweapon
endevent

View file

@ -1,16 +0,0 @@
#!/usr/bin/env luajit
if (arg[1]==nil) then
print("Usage: "..arg[0].." ../path/to/*.ART")
return 1
end
B = require "build"
tile = B.loadarts(arg)
for i=0,B.MAX.TILES-1 do
if (tile.sizy[i] > 256) then
print(i..": "..tile.sizy[i])
end
end

View file

@ -1,636 +0,0 @@
-- Loaders for various BUILD structures for LuaJIT
local ffi = require "ffi"
local io = require "io"
local bit = require "bit"
local string = require "string"
local table = require "table"
local error = error
local assert = assert
local pairs = pairs
local print = print
local setmetatable = setmetatable
local tostring = tostring
local tonumber = tonumber
module(...)
local STRUCTDEF = {
sector = [[
int16_t wallptr, wallnum;
int32_t ceilingz, floorz;
uint16_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, fogpal;
int16_t lotag, hitag, extra;
]],
wall = [[
int32_t x, y;
int16_t point2, nextwall, nextsector;
uint16_t cstat;
int16_t picnum, overpicnum;
int8_t shade;
uint8_t pal, xrepeat, yrepeat, xpanning, ypanning;
int16_t lotag, hitag, extra;
]],
sprite = [[
int32_t x, y, z;
uint16_t cstat;
int16_t picnum;
int8_t shade;
uint8_t pal, clipdist, blend;
uint8_t xrepeat, yrepeat;
int8_t xoffset, yoffset;
int16_t sectnum, statnum;
int16_t ang, owner, xvel, yvel, zvel;
int16_t lotag, hitag, extra;
]],
}
ffi.cdef([[
typedef struct
{
]]..STRUCTDEF.sector..[[
} sectortype;
typedef struct
{
]]..STRUCTDEF.wall..[[
} walltype;
typedef struct
{
]]..STRUCTDEF.sprite..[[
} spritetype;
]])
ffi.cdef[[
size_t fread(void *ptr, size_t size, size_t nmemb, void *stream);
]]
local C = ffi.C
-- [<sector/wall/sprite>].<membername> = true
local is_member_tab = {
sector = {},
wall = {},
sprite = {},
}
do
for what, sdef in pairs(STRUCTDEF) do
for membname in string.gmatch(sdef, "([a-z0-9_]+)[,;]") do
is_member_tab[what][membname] = true
end
end
end
function ismember(what, membname)
return (is_member_tab[what][membname] ~= nil)
end
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 MAX = MAX
-- <dontclose>: if true, don't close file on error
local function doread(fh, basectype, numelts, dontclose)
assert(numelts > 0)
local typ = ffi.typeof("$ [?]", ffi.typeof(basectype))
local cd = ffi.new(typ, numelts)
if (C.fread(cd, ffi.sizeof(basectype), numelts, fh) ~= numelts) then
if (not dontclose) then
fh:close()
end
return nil, "Failed reading"
end
return cd
end
-- Read base palette (i.e. first 768 bytes as R,G,B triplets) from a PALETTE.DAT.
-- <noquad>: if true, don't multiply components by 4
-- Returns:
-- on success: <uint8_t [768]> cdata (palette values scaled by 4)
-- on failure: nil, <errmsg>
function read_basepal(filename, noquad)
local fh, errmsg = io.open(filename, "rb")
if (fh == nil) then
return nil, errmsg
end
local palette, errmsg = doread(fh, "uint8_t", 768, true)
fh:close()
local f = noquad and 1 or 4
for i=0,768-1 do
palette[i] = f*palette[i]
end
return palette, errmsg
end
local function set_secwalspr_mt(structar, maxidx)
local mt = {
__index = function(tab, idx)
if (not (idx >= 0 and 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
local function get_numyaxbunches(map)
if (map.version < 9) then
return 0
end
local numbunches = 0
local sectsperbunch = { [0]={}, [1]={} }
for i=0,map.numsectors-1 do
for cf=0,1 do
local sec = map.sector[i]
local stat = (cf==0) and sec.ceilingstat or sec.floorstat
local xpan = (cf==0) and sec.ceilingxpanning or sec.floorxpanning
if (bit.band(stat, 1024) ~= 0) then
if (xpan+1 > numbunches) then
numbunches = xpan+1
end
if (sectsperbunch[cf][xpan]==nil) then
sectsperbunch[cf][xpan] = 1
else
sectsperbunch[cf][xpan] = sectsperbunch[cf][xpan]+1
end
end
end
end
map.numbunches = numbunches
map.sectsperbunch = sectsperbunch
end
--== sprite canonicalizer ==--
local function sprite2str(s)
local FMT = "%+11d_"
-- NOTE: this canonicalization isn't very useful except for debugging
-- copy-paste in the editor.
-- tostring(s): make sort stable
return string.format(FMT:rep(4).."%s", s.x, s.y, s.z, s.ang, tostring(s))
end
local function canonicalize_sprite_order(map)
local numsprites = map.numsprites
map.spriten2o = {} -- mapping of new to old sprite index
if (numsprites == 0) then
return
end
local spriteidx = {}
for i=0,numsprites-1 do -- 0->1 based indexing
spriteidx[i+1] = i
end
table.sort(spriteidx,
function(i1, i2)
return sprite2str(map.sprite[i1]) < sprite2str(map.sprite[i2])
end)
-- deep-copied sprite structs
local spritedup = {}
for i=0,numsprites-1 do
-- save sorting permutation (0-based -> 0-based)
map.spriten2o[i] = assert(spriteidx[i+1])
-- back up struct
spritedup[i] = ffi.new("spritetype")
ffi.copy(spritedup[i], map.sprite[i], ffi.sizeof("spritetype"))
end
for i=0,numsprites-1 do -- do the actual rearrangement
map.sprite[i] = spritedup[spriteidx[i+1]]
end
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> },
-- numbunches = <num>,
-- sectsperbunch = {
-- [0] = { [<bunchnum>]=<number of ceilings> },
-- [1] = { [<bunchnum>]=<number of floors> }
-- }
-- }
function loadboard(filename, do_canonicalize_sprite)
local fh, errmsg = io.open(filename, "rb")
if (fh==nil) then
return nil, errmsg
end
local cd = doread(fh, "int32_t", 4)
if (cd==nil) then
return nil, "Couldn't read header"
end
-- The table we'll return on success
local map = {
version = cd[0],
start = { x=cd[1], y=cd[2], 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
if (map.numsprites ~= 0) then
map.sprite = doread(fh, "spritetype", map.numsprites)
if (map.sprite == nil) then
return nil, "Couldn't read sprites"
end
end
fh:close()
if (do_canonicalize_sprite) then
-- must do this before setting metatable
canonicalize_sprite_order(map)
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)
get_numyaxbunches(map)
-- done
return map
end
local picanm_t = ffi.typeof[[
struct {
uint8_t num; // animate number
int8_t xofs, yofs;
uint8_t sf; // anim. speed and flags
}
]]
local artfile_mt = {
__index = {
-- Global -> local tile index (-1 if gtile is not in this ART file)
toltile = function(self, gtile)
if (self.tbeg <= gtile and gtile <= self.tend) then
return gtile - self.tbeg
end
return -1
end,
_check_ltile = function(self, ltile)
if (not (ltile >= 0 and ltile < self.numtiles)) then
error("Invalid local tile number "..ltile, 3)
end
end,
_getofs = function(self, ltile)
return self.tiledataofs + self.offs[ltile]
end,
dims = function(self, ltile)
self:_check_ltile(ltile)
return self.sizx[ltile], self.sizy[ltile]
end,
getpic = function(self, ltile)
local sx, sy = self:dims(ltile)
if (sx == 0 or sy == 0) then
-- Tile nonexistent/empty in this ART file
return nil, "Empty tile"
end
assert(self.fh:seek("set", self:_getofs(ltile)))
return doread(self.fh, "uint8_t", sx*sy, self.grpfh ~= false) -- GRPFH_FALSE
end,
},
__metatable = true,
}
-- af, errmsg = artfile(filename [, grpfh, grpofs])
--
-- <filename>: File name of the file to get data from, expect if <grpfh>
-- passed. Always closed on error. Kept open on success.
-- <grpfh>: io.open() file handle to grouping file containing ART
-- uncompressed. Never closed, even on error.
-- <grpofs>: offset of the ART file in file given by <grpfh>
--
-- Returns:
-- * on error: nil, <errmsg>
-- * on success:
function artfile(filename, grpfh, grpofs)
local ogrpofs
local fh
if (grpfh) then
ogrpofs = grpfh:seek()
assert(grpfh:seek("set", grpofs))
fh = grpfh
else
local errmsg
fh, errmsg = io.open(filename, "rb")
if (fh == nil) then
return nil, errmsg
end
end
-- Close file on error?
local dontclose = (grpfh ~= nil)
-- Maybe close file handle and return error message <msg>
local function err(msg, ...)
if (not dontclose) then
fh:close()
end
return nil, string.format(msg, ...)
end
local hdr = doread(fh, "int32_t", 4, dontclose)
-- artversion, numtiles, localtilestart, localtileend
if (hdr == nil) then
return err("Couldn't read header")
end
local af = {
filename = filename,
fh = fh,
grpfh = grpfh or false, -- GRPFH_FALSE
tbeg = hdr[2],
tend = hdr[3],
numtiles = hdr[3]-hdr[2]+1,
-- Members inserted later:
-- sizx, sizy: picanm: arrays of length af.numtiles
-- tiledataofs: byte offset in `fh' to beginning of tile data
-- offs: local byte offsets of each tile, relative of af.tiledataofs
--
-- Thus,
-- af.tiledataofs + af.offs[localtilenum]
-- is the byte offset of global tile
-- af.tbeg + localtilenum
-- in `fh'.
}
if (af.numtiles <= 0) then
return err("Invalid tile start/end or empty ART file")
end
local lasttile = af.tbeg + af.numtiles
if (lasttile >= MAX.TILES) then
return err("Last tile %d beyond MAXTILES-1 (%d)", lasttile, MAX.TILES-1)
end
af.sizx = doread(fh, "int16_t", af.numtiles, dontclose)
af.sizy = doread(fh, "int16_t", af.numtiles, dontclose)
if (af.sizx==nil or af.sizy==nil) then
return err("Couldn't read sizx or sizy arrays")
end
af.picanm = doread(fh, picanm_t, af.numtiles, dontclose)
if (af.picanm == nil) then
return err("Couldn't read picanm array")
end
af.tiledataofs = assert(fh:seek())
af.offs = ffi.new("uint32_t [?]", af.numtiles)
local curofs = 0
for i=0,af.numtiles-1 do
local sx, sy = af.sizx[i], af.sizy[i]
if (sx > 0 and sy > 0) then
af.offs[i] = curofs
curofs = curofs + sx*sy
elseif (sx < 0 or sy < 0) then
-- Let's sanity-check stuff a little
return err("Local tile %d has negative x or y size", i)
else
af.offs[i] = -1
end
end
return setmetatable(af, artfile_mt)
end
local function set_sizarray_mt(sizar)
local mt = {
__index = function(tab, idx)
if (not (idx >= 0 and idx < MAX.TILES)) then
error("Invalid tile size array read access", 2)
end
return sizar[idx]
end,
__newindex = function(tab, idx, newval)
if (not (idx >= 0 and 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, "rb")
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+1
-- print(fn.. ": "..cd[2].. ", "..cd[3])
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
-- defs [, rdefs] = readdefs(fn [, alsoreverse])
function readdefs(fn, alsoreverse)
local fh, errmsg = io.open(fn)
if (fh==nil) then
return nil, errmsg
end
local defs, rdefs = {}, nil
for line in fh:lines() do
local defname, numstr = string.match(line, "#?%s*define%s+([%a_][%w_]+)%s+([0-9]+)")
if (defname) then
defs[defname] = tonumber(numstr)
end
end
if (alsoreverse) then
rdefs = {}
for defname, num in pairs(defs) do
rdefs[num] = defname
end
end
fh:close()
return defs, rdefs
end

View file

@ -1,100 +0,0 @@
#!/usr/bin/env luajit
local io = require("io")
local os = require("os")
local string = require("string")
local format = string.format
local table = require("table")
local B = require("build")
local ffi = require("ffi")
local C = ffi.C
ffi.cdef[[
int memcmp(const void *s1, const void *s2, size_t n);
]]
local fn1, fn2 = arg[1], arg[2]
local noOnlyLeft = (arg[3]=="-l") -- don't show diff lines where left has tile, right hasn't
local function err(str)
io.stderr:write(str, "\n")
os.exit(1)
end
local function printf(fmt, ...)
io.stdout:write(string.format(fmt, ...), "\n")
end
if (not fn1 or not fn2) then
err("Usage: "..arg[0].." </path/to/tilesXXX.ART> </path/to/tilesYYY.ART> [-l]")
end
local af1, errmsg = B.artfile(fn1)
if (errmsg) then
err(errmsg)
end
local af2, errmsg = B.artfile(fn2)
if (errmsg) then
err(errmsg)
end
-- Check for same start/end
if (af1.tbeg ~= af2.tbeg) then
err("tile starts differ: "..af1.tbeg.." "..af2.tbeg)
end
if (af1.tend ~= af2.tend) then
err("tile ends differ: "..af1.tend.." "..af2.tend)
end
-- Compare the two ART files
local SIZEOF_PICANM = ffi.sizeof(af1.picanm[0])
for i=0,af1.numtiles-1 do
local sx1, sx2 = af1.sizx[i], af2.sizx[i]
local sxdif = (sx1 ~= sx2)
local sy1, sy2 = af1.sizy[i], af2.sizy[i]
local sydif = (sy1 ~= sy2)
local sizedif = (sxdif or sydif)
local picanmdif = (C.memcmp(af1.picanm[i], af2.picanm[i], SIZEOF_PICANM) ~= 0)
local datadif = false
-- compare data
if (not sizedif) then
local pic1 = af1:getpic(i)
local pic2 = af2:getpic(i)
if (pic1 and pic2 and C.memcmp(pic1, pic2, sx1*sy1) ~= 0) then
datadif = true
end
end
if (sizedif or picanmdif or datadif) then
local strbuf = {}
local s1str = sx1*sy1==0 and "none" or format("(%d,%d)", sx1, sy1)
local s2str = sx2*sy2==0 and "none" or format("(%d,%d)", sx2, sy2)
if ((sxdif or sydif) and (not noOnlyLeft or s2str ~= "none")) then
strbuf[#strbuf+1] = format("size %s %s", s1str, s2str)
end
if (picanmdif) then
strbuf[#strbuf+1] = "picanm"
end
if (not sizedif and datadif) then
strbuf[#strbuf+1] = "data"
end
if (#strbuf > 0) then
io.stdout:write(i..": "..table.concat(strbuf, ", "), "\n")
end
end
end

View file

@ -1,104 +0,0 @@
-- Search for enemies with nonzero pal.
-- foreachmap module.
local build = require("build")
local D = build.readdefs("../../names.h") or error("Need ../../names.h")
local assert = assert
local print = print
local string = require("string")
local io = require("io")
local function printf(fmt, ...)
print(string.format(fmt, ...))
end
module(...)
local ENEMY_NAME = {
--[[
[D.BOSS1] = "BOSS1",
[D.BOSS1STAYPUT] = "BOSS1STAYPUT",
--]]
[D.BOSS2] = "BOSS2",
[D.BOSS3] = "BOSS3",
[D.BOSS4] = "BOSS4",
[D.BOSS4STAYPUT] = "BOSS4STAYPUT",
[D.COMMANDER] = "COMMANDER",
[D.COMMANDERSTAYPUT] = "COMMANDERSTAYPUT",
[D.DRONE] = "DRONE",
[D.GREENSLIME] = "GREENSLIME",
[D.LIZMAN] = "LIZMAN",
[D.LIZMANFEEDING] = "LIZMANFEEDING",
[D.LIZMANJUMP] = "LIZMANJUMP",
[D.LIZMANSPITTING] = "LIZMANSPITTING",
[D.LIZMANSTAYPUT] = "LIZMANSTAYPUT",
[D.LIZTROOP] = "LIZTROOP",
[D.LIZTROOPDUCKING] = "LIZTROOPDUCKING",
[D.LIZTROOPJETPACK] = "LIZTROOPJETPACK",
[D.LIZTROOPJUSTSIT] = "LIZTROOPJUSTSIT",
[D.LIZTROOPONTOILET] = "LIZTROOPONTOILET",
[D.LIZTROOPRUNNING] = "LIZTROOPRUNNING",
[D.LIZTROOPSHOOT] = "LIZTROOPSHOOT",
[D.LIZTROOPSTAYPUT] = "LIZTROOPSTAYPUT",
[D.OCTABRAIN] = "OCTABRAIN",
[D.OCTABRAINSTAYPUT] = "OCTABRAINSTAYPUT",
[D.ORGANTIC] = "ORGANTIC",
[D.PIGCOP] = "PIGCOP",
[D.PIGCOPDIVE] = "PIGCOPDIVE",
[D.PIGCOPSTAYPUT] = "PIGCOPSTAYPUT",
[D.RAT] = "RAT",
[D.ROTATEGUN] = "ROTATEGUN",
[D.SHARK] = "SHARK",
}
local uniq = false
function init(args)
if (#args == 1) then
local wr = function(s) io.stderr:write(s) end
wr("Usage: "..args[0].." "..args[1].." [-u] *.map ...\n")
wr(" -u: print only one pal-x-tilenum combination\n")
end
if (args[2]:sub(1,1)=="-") then
assert(args[2]=="-u", "Unknown option "..args[2])
uniq = true
return 3
end
end
function success(map, fn)
-- Have one at all?
local haveone = false
-- Have pal-x-tile combination?
-- [tile*256 + pal] = true
local havept = {}
for i=0,map.numsprites-1 do
local spr = map.sprite[i]
local picnum, pal = spr.picnum, spr.pal
local name = ENEMY_NAME[picnum]
if (name and pal~=0) then
if (not (name:match("^LIZTROOP") and pal==21)) then -- those are handled by CON
if (not uniq or not havept[picnum*256 + pal]) then
if (not haveone) then
printf("%s:", fn)
haveone = true
end
printf("%5d %3d %s", i, pal, name)
havept[picnum*256 + pal] = true
end
end
end
end
if (haveone) then
print("")
end
end

View file

@ -1,34 +0,0 @@
#!/bin/bash
ok=yes
if [ -z "$1" ]; then
ok=
fi
if [ -z "$2" ]; then
ok=
fi
if [ -z "$ok" ]; then
echo "Usage: $0 <dir> <some_foreachmap_module.lua | code for foreachmap.lua -e>"
exit 1
fi
LOPT=-L
idx=$(expr match `uname -s` '[mM][iI][nN][gG][wW]')
if [ "$idx" != 0 ]; then
LOPT=
fi
FN="$1"
ARG="$2"
idx=$(expr match "$ARG" '.*lua$')
if [ "$idx" == 0 ]; then
ARG="-e$ARG"
find $LOPT "$FN" -iname '*.map' -print0 | xargs -0 ./foreachmap.lua "$ARG"
else
shift
# So that you can e.g. do
# ./findmaps.sh ~/.eduke32 ./colenemy.lua -u
find $LOPT "$FN" -iname '*.map' -print0 | xargs -0 ./foreachmap.lua "$@"
fi

View file

@ -1,177 +0,0 @@
#!/usr/bin/env luajit
-- Generic map iterator.
-- The first cmdline arg is a name of a lua file (may be sans .lua) which must
-- be a module and is `require'd into this script, e.g. "stats" or "stats.lua".
-- First, a key named .init is looked up in the loaded module and if it exists,
-- it is run like .init(arg) (thus allowing it to parse the command-line
-- arguments and then potentially remove the ones it used).
-- If .init returns non-nil, this script aborts.
-- Otherwise, for each 2nd and following argument, if map loading succeeds,
-- .success(map, filename) is run, otherwise
-- .failure(filename, errmsg) is run if that key exists, or a standard error
-- message is printed to stderr.
-- Finally, if there is a .finish field in the module, it is run with no args.
-- forxcode example: print sprite numbers with lotag < -1 (lotag is signed for us),
-- and for each matching sprite also print its lotag and picnum:
-- $ ./findmaps.sh ~/.eduke32/ "sprite: .lotag < -1 :: io.write(', '.. .lotag .. ' ' .. .picnum)"
-- The local 'd' provides defs loaded from ../../names.h, example:
-- $ ./findmaps.sh ~/.eduke32/ "sprite: .picnum>=d.CRACK1 and .picnum<=d.CRACK4"
-- (Now: no space between "d." and "CRACK" is necessary ".xxx" is translated to
-- "sprite[<current>].xxx" only if it's the name of a sprite member.)
-- The local 'rdefs' provides the reverse mapping: from picnums to tile names
-- (or nil).
-- Print all V9 maps along with their number of bunches and max(ceilings of a bunch)
-- $ prog='if (map.version==9) then print(map.numbunches.." ".. math.max(unpack(map.sectsperbunch[0],0)) .." "..fn) end'
-- $ ./findmaps.sh ~/.eduke32 "$prog" |sort -n -k 2
-- Print all MUSICANDSFX sprites that play sounds with bit 1 set.
-- ./findmaps.sh /g/Games/Eduke32c/grp 'sprite: .picnum==5 and eq(.lotag, {170, 186, 187, 279, 382, 347}) :: io.write(" ".. tostring(.lotag))'
-- Print all maps that have floor-aligned blockable sprites in underwater sectors.
-- ./findmaps.sh ~/.eduke32 'sprite:: bit.band(.cstat,49)==33 and .sectnum>=0 and .sectnum < map.numsectors and sector[.sectnum].lotag==2
local B = require "build"
local string = require "string"
local io = require "io"
local os = require "os"
if (#arg < 1) then
local wr = function(s) io.stdout:write(s) end
wr("Usage: ./foreachmap.lua <module[.lua]> [init args...] <filename.map> ...\n")
wr(" ./foreachmap.lua -e\"some_lua_code ...\" <filename.map> ...\n")
wr(" ./foreachmap.lua -e\"[sector|wall|sprite]: <condition on .<field>>\" <fn.map> ...\n\n")
wr("In the second form, the code is run as body of a function(map, fn)\n")
wr("and num{sectors,walls,sprites} and {sector,wall,sprite} do not\n")
wr("need to be qualified with the \"map.\" prefix.\n")
wr("The third form is a shortcut for quickly finding sectors/walls/sprites\n")
wr("satisfying a certain condition (see example below)\n\n")
wr("Examples: ./foreachmap.lua -e\"if map.numbunches==1 then print(fn) end\" ~/.eduke32/*.map\n")
wr(" ./foreachmap.lua -e\"sprite: .picnum==10 and .lotag==2563\" *.map\n")
wr(" ./foreachmap.lua -e\"sprite:: ... -- (only prints the file names)\n")
wr("(See foreachmap.lua for an example of the \"forxcode\" feature.)\n\n")
return
end
local modname = string.gsub(arg[1], "%.lua$", "")
function sum(tab, initidx)
local s = 0
for i=(initidx or 1),#tab do
s = s + tab[i]
end
return s
end
local g_what
-- Maybe replace e.g. .nextwall --> wall[i].nextwall.
-- <what>: one of "sector", "wall" or "sprite"
-- <maybechar>: the char before <maybememb>, or ""
-- <maybememb>: a potential member name prefixed by "."
local function maybe_complete_member(maybechar, maybememb)
if (maybechar~="]" and B.ismember(g_what, maybememb:sub(2))) then
return maybechar..g_what.."[i]"..maybememb
else
return maybechar..maybememb
end
end
local mod
if (modname:sub(1,2) == "-e") then
local body = modname:sub(3)
-- sector/wall/sprite finder shortcut
local b, e, what = body:find("^([a-z]+)::?")
if (what) then
g_what = what
local onlyfiles = (body:sub(e-1,e)=="::") -- "::" means "only list files" (like grep -l)
body = body:sub(e+1) -- clip off "bla::"
body = body:gsub("(.?)(%.[a-z][a-z0-9]*)", maybe_complete_member) -- e.g. .lotag --> sprite[i].lotag
local perxcode
-- look for additional "print" code to be executed for each match
b, e, perxcode = body:find("::(.*)")
if (perxcode) then
body = body:sub(1,b-1)
if (onlyfiles) then
error("Per-x code can only be used with the ':' syntax (list each match)")
end
else
perxcode = ""
end
assert(what=="sector" or what=="wall" or what=="sprite")
body =
"for i=0,num"..what.."s-1 do\n"..
" if ("..body..") then\n"..
(onlyfiles and "io.write(fn); return\n" or "io.write(fn..': '..i)\n") ..
perxcode .. "io.write('\\n')\n"..
" end\n"..
"end\n"
end
local successfunc, errmsg = loadstring([[
local function eq(x, tab)
for i=1,#tab do
if (x==tab[i]) then return true end
end
end
local d,rdefs = require('build').readdefs('../../names.h',true)
if (not d) then error('Need ../../names.h') end -- XXX
local numsectors, numwalls, numsprites, sector, wall, sprite
return function (map, fn)
numsectors, numwalls, numsprites = map.numsectors, map.numwalls, map.numsprites
sector, wall, sprite = map.sector, map.wall, map.sprite
]]..body
.." end")
if (successfunc==nil) then
io.stderr:write("Error loading string: "..errmsg.."\n")
io.stderr:write("Function body:\n", body)
os.exit(1)
end
mod = { success=successfunc() }
else
mod = require(modname)
end
-- The return value from the module's .init().
local initret
if (mod.init) then
initret = mod.init(arg)
if (type(initret)=="number" and initret < 0) then
-- A negative return value from .init() is taken as a request to exit,
-- e.g. because an error occurred.
os.exit(1)
end
end
-- A positive return value from .init() is taken to start counting map names
-- from that 'arg' index.
local startargi = (type(initret)=="number" and initret > 0 and initret) or 2
for i=startargi,#arg do
local fn = arg[i]
local map, errmsg = B.loadboard(fn)
if (map ~= nil) then
mod.success(map, fn)
else
if (mod.failure) then
mod.failure(fn, errmsg)
else
io.stderr:write(string.format("--- %s: %s\n", fn, errmsg))
end
end
end
if (mod.finish) then
mod.finish()
end

View file

@ -1,110 +0,0 @@
#!/usr/bin/env luajit
local B = require("build")
local string = require("string")
local opt, fn
local do_canonicalize_sprite
if (arg[1] and arg[1]:sub(1,1)=="-") then
opt = arg[1]
fn = arg[2]
else
fn = arg[1]
end
if (opt) then
if (opt=="-c" or opt=="-C") then
-- -C means to canonicalize but without adding the new->old mapping
-- comment
do_canonicalize_sprite = opt
else
print("Error: unrecognized option "..opt)
return 1
end
end
if (fn==nil) then
print("Usage: map2text [-c/-C] <somefile.map>")
return 1
end
local function printf(fmt, ...)
print(string.format(fmt, ...))
end
local map, errmsg = B.loadboard(fn, do_canonicalize_sprite)
if (map == nil) then
printf("Couldn't load \"%s\": %s", fn, errmsg)
return 1
end
printf("numsectors = %d", map.numsectors)
printf("numwalls = %d", map.numwalls)
printf("numsprites = %d", map.numsprites)
printf("startpos = { %d, %d, %d }", map.start.x, map.start.y, map.start.z)
printf("startang = %d", map.start.ang)
printf("startsectnum = %d", map.start.sectnum)
local sector_members = {
"wallptr", "wallnum",
"ceilingz", "floorz",
"ceilingstat", "floorstat",
"ceilingpicnum", "ceilingheinum",
"ceilingshade",
"ceilingpal", "ceilingxpanning", "ceilingypanning",
"floorpicnum", "floorheinum",
"floorshade",
"floorpal", "floorxpanning", "floorypanning",
"visibility", "fogpal",
"lotag", "hitag", "extra",
}
local wall_members = {
"x", "y",
"point2", "nextwall", "nextsector",
"cstat",
"picnum", "overpicnum",
"shade",
"pal", "xrepeat", "yrepeat", "xpanning", "ypanning",
"lotag", "hitag", "extra",
}
local sprite_members = {
"x", "y", "z",
"cstat", "picnum",
"shade",
"pal", "clipdist", "blend",
"xrepeat", "yrepeat",
"xoffset", "yoffset",
"sectnum", "statnum",
"ang", "owner", "xvel", "yvel", "zvel",
"lotag", "hitag", "extra",
}
local function print_members(map, struct, members)
printf("%s = {", struct)
for i=0,map["num"..struct.."s"]-1 do
local comment = ""
if (struct=="sprite" and do_canonicalize_sprite=="-c") then
comment = " --"..tostring(map.spriten2o[i])
end
printf("[%d]={%s", i, comment)
for j=1,#members do
local member = members[j]
printf("%s = %d", member, map[struct][i][member])
end
print("}")
end
print("}")
end
print_members(map, "sector", sector_members)
print_members(map, "wall", wall_members)
print_members(map, "sprite", sprite_members)

View file

@ -1,46 +0,0 @@
-- Print out some aggregate statistics for passed BUILD maps,
-- foreachmap module.
local string = require "string"
local math = require "math"
local print = print
local type = type
local stat = require "stat"
module(...)
local function printf(fmt, ...)
print(string.format(fmt, ...))
end
local sumnumsectors = 0
local sumnumwalls = 0
local s = stat.new()
function success(map, fn)
local ns = map.numsectors
local nw = map.numwalls
s:add(nw/ns)
sumnumsectors = sumnumsectors+ns
sumnumwalls = sumnumwalls+nw
end
function finish()
res = s:getstats()
printf("%d maps\n", res.n)
printf("total sectors: %d", sumnumsectors)
printf("total walls: %d", sumnumwalls)
printf("total walls / total sectors: %.02f", sumnumwalls/sumnumsectors)
printf("")
printf("Walls/sector")
print(res)
end

View file

@ -1,46 +0,0 @@
#!/bin/sh
DIFF="git diff -U2 --no-index --color-words"
CMD="/usr/bin/env luajit ./map2text.lua"
opt=""
# Name of the 'tempfile' or 'mktemp' command (or full path).
tempfile_cmd=tempfile
tempfile_path=`which "$tempfile_cmd"`
if [ -z "$tempfile_path" ]; then
echo "Error: tempfile_cmd ($tempfile_cmd) must be the name of existing 'tempfile' or 'mktemp' executable."
exit 1
fi
if [ "$1" = "-c" -o "$1" = "-C" ]; then
opt="$1"
shift
fi
if [ -z "$1" -o -z "$2" ]; then
echo "Usage: ./mapdiff.sh [-c] <file.map> <file2.map>"
exit 1
fi
tf1=`"$tempfile_cmd"`
if [ -z "$tf1" ]; then
echo Failed creating temp file
exit 2
fi
tf2=`"$tempfile_cmd"`
if [ -z "$tf2" ]; then
rm "$tf1"
echo Failed creating temp file
exit 2
fi
$CMD $opt "$1" > "$tf1"
$CMD $opt "$2" > "$tf2"
$DIFF "$tf1" "$tf2"
rm "$tf1"
rm "$tf2"

View file

@ -1,25 +0,0 @@
-- Print out some statistics for a BUILD map,
-- foreachmap module.
local string = require "string"
local print = print
module(...)
local function printf(fmt, ...)
print(string.format(fmt, ...))
end
function success(map, fn)
printf("--- %s:", fn)
printf(" version: %d", map.version)
printf(" numsectors: %d\n numwalls: %d\n numsprites: %d",
map.numsectors, map.numwalls, map.numsprites)
printf(" walls/sector: %.02f\n sprites/sector: %.02f",
map.numwalls/map.numsectors, map.numsprites/map.numsectors)
printf("")
end

View file

@ -1,172 +0,0 @@
-- Display information about problematic wall ypannings,
-- foreachmap module.
local string = require "string"
local table = require "table"
local bit = require "bit"
local print = print
local pairs = pairs
local rawget = rawget
local setmetatable = setmetatable
local B = require "build"
module(...)
local function printf(fmt, ...)
print(string.format(fmt, ...))
end
local tile
function init(arg)
local artargend = nil
for i=2,#arg do
if (arg[i]=="--") then
artargend = i
break
end
end
if (artargend==nil or artargend==0) then
printf("Usage: luajit ./foreachmap.lua %s <tilesXXX.ART> [, ...] -- <filename1.map> ...\n", arg[1])
return -1
end
local artfns = {}
for i=2,artargend-1 do
artfns[#artfns+1] = arg[i]
end
local i = 2
local j = artargend+1
while (arg[j]) do
arg[i] = arg[j]
arg[j] = nil
i = i+1
j = j+1
end
local tile_, errmsg = B.loadarts(artfns)
if (tile_ == nil) then
printf("%s", errmsg)
return -2
end
tile = tile_ -- set 'tile' file-scope var
end
local table_default0_mt = {
__index = function(tab, idx)
if (rawget(tab, idx)==nil) then
return 0
end
return rawget(tab, idx)
end
}
local sector, wall, sprite
function success(map, fn)
-- set file-scope vars for convenience
sector = map.sector
wall = map.wall
sprite = map.sprite
-- counts of non-pow2 tiles, per tilenum
local np2tile = setmetatable({}, table_default0_mt)
-- walls/overwall indices with non-pow2 tiles, [walidx]=true
local np2wall = {}
-- [i]=wallidx
local np2walls = {}
local badoverpicnum = false
for i=0,map.numsectors-1 do
local startwall = sector[i].wallptr
local endwall = startwall+sector[i].wallnum-1
for w=startwall,endwall do
for n=1,2 do
local pic, ysiz
if (wall[w].nextwall < 0) then
-- We don't care for white walls
elseif (n==1) then
pic = wall[w].picnum
ysiz = tile.sizy[pic]
else
pic = wall[w].overpicnum
if (pic < 0 or pic > 30720) then -- MAXTILES
badoverpicnum = true
else
if (bit.band(wall[w].cstat, 16+32)==0) then
-- we don't care about non-masked/1-way walls
ysiz = nil
else
ysiz = tile.sizy[pic]
end
end
end
if (pic == 560) then -- Don't care for MIRROR
ysiz = nil
end
if (ysiz~=nil and ysiz > 0 and bit.band(ysiz, bit.bnot(ysiz-1))~=ysiz) then
-- non-pow2 ysize
np2tile[pic] = np2tile[pic]+1
if (not np2wall[w]) then
np2wall[w] = true
np2walls[#np2walls+1] = w
end
end
end
end
end
-- report our findings
if (#np2walls == 0) then
return
end
-- sort in wall index order
table.sort(np2walls)
printf("--- %s:", fn)
--[[
printf(" Walls:")
for i=1,#np2walls do
printf(" %d", np2walls[i])
end
printf("")
--]]
printf(" %d red walls with non-pow2 ysize tiles", #np2walls)
if (badoverpicnum) then
printf(" (some red walls have out-of-bounds overpicnums)")
end
local np2tiles = {}
for tilenum,_ in pairs(np2tile) do
np2tiles[#np2tiles+1] = tilenum
end
table.sort(np2tiles)
printf(" Tiles:")
for i=1,#np2tiles do
printf(" %d", np2tiles[i])
end
printf("")
end

View file

@ -1,146 +0,0 @@
-- Search for maps that have parallaxed ceilings with different tile numbers.
-- foreachmap module.
local bit = require "bit"
local string = require "string"
local table = require "table"
local io = require "io"
local print = print
local tostring = tostring
local type = type
local function printf(fmt, ...)
print(string.format(fmt, ...))
end
module(...)
local MULTI_PSKY_TILES = { 80, 84, 89 }
local IS_MULTI_PSKY_TILE = {
[80] = true, -- MOONSKY1
[84] = true, -- BIGORBIT1
[89] = true, -- LA
}
local tilecnt
-- foreachmap initialization
local opts = { p=false, s=false } -- pretty? simple?
function init(args)
if (#args == 1) then
local wr = function(s) io.stderr:write(s) end
wr("Usage: "..args[0].." "..args[1].." [-s] [-p] *.map ...\n")
wr(" -s: short format\n")
wr(" -p: pretty-print using ANSI escapes\n\n")
wr("The long format is as follows:\n")
wr(" <mapname.map>: <num-distinct-psky-tiles> (<of-those-multi-pskies>): <item1> <item2> ...\n")
wr("Each <item> reads\n")
wr(" #<tilenum>:<num-occurrences>(s<sectnum>)\n")
wr("where <sectnum> is the index of an arbitrary sector containing such a psky ceiling.\n")
return
end
for i=2,#args do
local arg = args[i]
if (arg:sub(1,1)=="-") then
local letter = arg:sub(2,2)
if (#arg==2 and type(opts[letter])=="boolean") then
opts[letter] = true
else
io.stderr:write("Unrecognized option "..arg.."\n")
end
else
return i
end
end
end
-- Sorting function for tiles, true if <tile1> "less-than" <tile2>.
-- "less-than" here means appearing earlier in the resulting table.
local function sortskytiles(tile1, tile2)
local ismulti1 = IS_MULTI_PSKY_TILE[tile1]
local ismulti2 = IS_MULTI_PSKY_TILE[tile2]
-- First, check if <tile1> is a multi-psky tile and <tile2> not.
if (ismulti1 and not ismulti2) then
return true
end
-- Next, check if <tile1> appears more often in the map.
if (tilecnt[tile1] > tilecnt[tile2]) then
return true
end
-- Now, (tilecnt[tile1] >= tilecnt[tile2]) and (not ismulti1 or ismulti2).
return false
end
local function bold(s)
return opts.p and ("\x1b[1m"..s.."\x1b[0m") or tostring(s)
end
function success(map, fn)
-- [<tilenum>] = <count of psky ceilings with that tile number>
tilecnt = {}
-- [<idx>] = <tilenum>
local tiles = {}
-- [<tilenum>] = <arbitrary sectnum with that psky ceiling tile>
local tilesect = {}
for i=0,map.numsectors-1 do
local sec = map.sector[i]
if (bit.band(sec.ceilingstat, 1) ~= 0) then
local tile = sec.ceilingpicnum
if (tilecnt[tile] == nil) then
tiles[#tiles+1] = tile
end
tilecnt[tile] = 1 + (tilecnt[tile] or 0)
tilesect[tile] = i
end
end
if (#tiles > 0) then
-- Do an in-place sort.
table.sort(tiles, sortskytiles)
local strbuf = {}
for i=1,#tiles do
local tile = tiles[i]
local ismulti = IS_MULTI_PSKY_TILE[tile]
if (i == 4) then
strbuf[#strbuf+1] = "..."
break
end
strbuf[#strbuf+1] = (ismulti and "*" or "#")..tile..
":"..tilecnt[tile]..
(opts.s and "" or ("(s"..tilesect[tile]..")"))
end
local nummultiskies = 0
for i=1,#MULTI_PSKY_TILES do
if (tilecnt[MULTI_PSKY_TILES[i]]) then
nummultiskies = nummultiskies+1
end
end
-- DCPT: distinct ceiling-psky tiles
printf("%s: %s%sDCPT: %s", fn, bold(#tiles),
opts.s and " " or bold(" ("..nummultiskies..") "),
table.concat(strbuf," "))
end
end

View file

@ -1,167 +0,0 @@
----------------------------------------------------------------------------
-- Verbose mode of the LuaJIT compiler.
--
-- Copyright (C) 2005-2015 Mike Pall. All rights reserved.
-- Released under the MIT license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
--
-- This module shows verbose information about the progress of the
-- JIT compiler. It prints one line for each generated trace. This module
-- is useful to see which code has been compiled or where the compiler
-- punts and falls back to the interpreter.
--
-- Example usage:
--
-- luajit -jv -e "for i=1,1000 do for j=1,1000 do end end"
-- luajit -jv=myapp.out myapp.lua
--
-- Default output is to stderr. To redirect the output to a file, pass a
-- filename as an argument (use '-' for stdout) or set the environment
-- variable LUAJIT_VERBOSEFILE. The file is overwritten every time the
-- module is started.
--
-- The output from the first example should look like this:
--
-- [TRACE 1 (command line):1 loop]
-- [TRACE 2 (1/3) (command line):1 -> 1]
--
-- The first number in each line is the internal trace number. Next are
-- the file name ('(command line)') and the line number (':1') where the
-- trace has started. Side traces also show the parent trace number and
-- the exit number where they are attached to in parentheses ('(1/3)').
-- An arrow at the end shows where the trace links to ('-> 1'), unless
-- it loops to itself.
--
-- In this case the inner loop gets hot and is traced first, generating
-- a root trace. Then the last exit from the 1st trace gets hot, too,
-- and triggers generation of the 2nd trace. The side trace follows the
-- path along the outer loop and *around* the inner loop, back to its
-- start, and then links to the 1st trace. Yes, this may seem unusual,
-- if you know how traditional compilers work. Trace compilers are full
-- of surprises like this -- have fun! :-)
--
-- Aborted traces are shown like this:
--
-- [TRACE --- foo.lua:44 -- leaving loop in root trace at foo:lua:50]
--
-- Don't worry -- trace aborts are quite common, even in programs which
-- can be fully compiled. The compiler may retry several times until it
-- finds a suitable trace.
--
-- Of course this doesn't work with features that are not-yet-implemented
-- (NYI error messages). The VM simply falls back to the interpreter. This
-- may not matter at all if the particular trace is not very high up in
-- the CPU usage profile. Oh, and the interpreter is quite fast, too.
--
-- Also check out the -jdump module, which prints all the gory details.
--
------------------------------------------------------------------------------
-- Cache some library functions and objects.
local jit = require("jit")
assert(jit.version_num == 20003, "LuaJIT core/library version mismatch")
local jutil = require("jit.util")
local vmdef = require("jit.vmdef")
local funcinfo, traceinfo = jutil.funcinfo, jutil.traceinfo
local type, format = type, string.format
local stdout, stderr = io.stdout, io.stderr
-- Active flag and output file handle.
local active, out
------------------------------------------------------------------------------
local startloc, startex
local function fmtfunc(func, pc)
local fi = funcinfo(func, pc)
if fi.loc then
return fi.loc
elseif fi.ffid then
return vmdef.ffnames[fi.ffid]
elseif fi.addr then
return format("C:%x", fi.addr)
else
return "(?)"
end
end
-- Format trace error message.
local function fmterr(err, info)
if type(err) == "number" then
if type(info) == "function" then info = fmtfunc(info) end
err = format(vmdef.traceerr[err], info)
end
return err
end
-- Dump trace states.
local function dump_trace(what, tr, func, pc, otr, oex)
if what == "start" then
startloc = fmtfunc(func, pc)
startex = otr and "("..otr.."/"..oex..") " or ""
else
if what == "abort" then
local loc = fmtfunc(func, pc)
if loc ~= startloc then
out:write(format("[TRACE --- %s%s -- %s at %s]\n",
startex, startloc, fmterr(otr, oex), loc))
else
out:write(format("[TRACE --- %s%s -- %s]\n",
startex, startloc, fmterr(otr, oex)))
end
elseif what == "stop" then
local info = traceinfo(tr)
local link, ltype = info.link, info.linktype
if ltype == "interpreter" then
out:write(format("[TRACE %3s %s%s -- fallback to interpreter]\n",
tr, startex, startloc))
elseif link == tr or link == 0 then
out:write(format("[TRACE %3s %s%s %s]\n",
tr, startex, startloc, ltype))
elseif ltype == "root" then
out:write(format("[TRACE %3s %s%s -> %d]\n",
tr, startex, startloc, link))
else
out:write(format("[TRACE %3s %s%s -> %d %s]\n",
tr, startex, startloc, link, ltype))
end
else
out:write(format("[TRACE %s]\n", what))
end
out:flush()
end
end
------------------------------------------------------------------------------
-- Detach dump handlers.
local function dumpoff()
if active then
active = false
jit.attach(dump_trace)
if out and out ~= stdout and out ~= stderr then out:close() end
out = nil
end
end
-- Open the output file and attach dump handlers.
local function dumpon(outfile)
if active then dumpoff() end
if not outfile then outfile = os.getenv("LUAJIT_VERBOSEFILE") end
if outfile then
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
else
out = stderr
end
jit.attach(dump_trace, "trace")
active = true
end
-- Public module functions.
module(...)
on = dumpon
off = dumpoff
start = dumpon -- For -j command line option.

View file

@ -1,357 +0,0 @@
-- "Extended" math module for Lunatic.
local ffi = require("ffi")
local bit = require("bit")
local math = require("math")
local arshift = bit.arshift
local abs, sqrt = math.abs, math.sqrt
local assert = assert
local error = error
local type = type
local OUR_REQUIRE_STRING = [[
local _xm=require'xmath'
local _v,_iv=_xm.vec3,_xm.ivec3
]]
local function our_get_require()
return OUR_REQUIRE_STRING
end
module(...)
---=== TRIGONOMETRY ===---
local BANG2RAD = math.pi/1024
local isintab = ffi.new("int16_t [?]", 2048)
local dsintab = ffi.new("double [?]", 2048)
for a=0,511 do
local s = math.sin(a*BANG2RAD)
isintab[a] = 16384*s
dsintab[a] = s
end
isintab[512] = 16384
dsintab[512] = 1
for i=513,1023 do
isintab[i] = isintab[1024-i];
dsintab[i] = dsintab[1024-i];
end
for i=1024,2047 do
isintab[i] = -isintab[i-1024];
dsintab[i] = -dsintab[i-1024];
end
local band = bit.band
local function ksc_common(ang)
ang = band(ang, 2047)
assert(ang >= 0 and ang < 2048) -- might have been passed NaN
return ang
end
-- k{sin,cos}: 16384-scaled output, 2048-based angle input
function ksin(ang)
return isintab[ksc_common(ang)]
end
function kcos(ang)
return isintab[ksc_common(ang+512)]
end
local sin, cos = math.sin, math.cos
-- {sin,cos}b: [-1..1] output, 2048-based angle input
function sinb(ang)
return dsintab[ksc_common(ang)]
end
function cosb(ang)
return dsintab[ksc_common(ang+512)]
end
local cosb, sinb = cosb, sinb
---=== Approximations to 2D and 3D Euclidean distances ===---
-- (also see common.c)
local function dist_common(pos1, pos2)
local x = abs(pos1.x - pos2.x)
local y = abs(pos1.y - pos2.y)
if (x < y) then
x, y = y, x
end
return x, y
end
function ldist(pos1, pos2)
local x, y = dist_common(pos1, pos2)
local t = y + arshift(y,1)
return x - arshift(x,5) - arshift(x,7) + arshift(t,2) + arshift(t,6)
end
function dist(pos1, pos2)
local x, y = dist_common(pos1, pos2)
local z = abs(arshift(pos1.z - pos2.z, 4))
if (x < z) then
x, z = z, x
end
local t = y + z
return x - arshift(x,4) + arshift(t,2) + arshift(t,3)
end
---=== VECTOR TYPES ===---
-- The integer 3-vector can be useful for calculations expecting integer
-- values, e.g. ivec3(x, y, z) is a reasonable way to round a vec3. It can also
-- be used as the RHS to the vec2/vec3 arithmetic methods.
-- NOTE: We must have a typedef with that exact name, because for Lunatic
-- (i.e. not stand-alone), the type was already declared in defs_common.lua.
ffi.cdef "typedef struct { int32_t x, y, z; } vec3_t;"
local ivec3_t = ffi.typeof("vec3_t")
local dvec2_t = ffi.typeof("struct { double x, y; }")
local dvec3_t = ffi.typeof("struct { double x, y, z; }")
local vec2_mt = {
__add = function(a, b) return dvec2_t(a.x+b.x, a.y+b.y) end,
__sub = function(a, b) return dvec2_t(a.x-b.x, a.y-b.y) end,
__unm = function(a) return dvec2_t(-a.x, -a.y) end,
__mul = function(a,b)
if (type(a)=="number") then
return dvec2_t(a*b.x, a*b.y)
end
if (type(b)~="number") then
error("number expected in vec2 multiplication", 2)
end
return dvec2_t(a.x*b, a.y*b)
end,
__div = function(a,b)
if (type(b)~="number") then
error("number expected in vec2 division", 2)
end
return dvec2_t(a.x/b, a.y/b)
end,
__tostring = function(a) return "vec2("..a.x..", "..a.y..")" end,
__index = {
lensq = function(a) return a.x*a.x + a.y*a.y end,
mhlen = function(a) return abs(a.x)+abs(a.y) end,
},
}
local l_rotate -- fwd-decl (XXX: could be the other way around)
-- The vec3 metatable is shared between the integer- and double-based 3-vector
-- types. However, some operations are slightly different.
local vec3_mt = {
-- Arithmetic operations. Note that they always return a dvec3.
__add = function(a, b) return dvec3_t(a.x+b.x, a.y+b.y, a.z+b.z) end,
__sub = function(a, b) return dvec3_t(a.x-b.x, a.y-b.y, a.z-b.z) end,
__unm = function(a) return dvec3_t(-a.x, -a.y, -a.z) end,
__mul = function(a,b)
if (type(a)=="number") then
return dvec3_t(a*b.x, a*b.y, a*b.z)
end
if (type(b)~="number") then
error("number expected in vec3 multiplication", 2)
end
return dvec3_t(a.x*b, a.y*b, a.z*b)
end,
__div = function(a,b)
if (type(b)~="number") then
error("number expected in vec3 division", 2)
end
return dvec3_t(a.x/b, a.y/b, a.z/b)
end,
-- '^' is the "translate upwards" operator, returns same-typed vector.
__pow = function(v, zofs)
return v:_ctor(v.x, v.y, v.z-zofs)
end,
-- Convenience for human-readable display.
__tostring = function(a)
return (a:_isi() and "i" or "").."vec3("..a.x..", "..a.y..", "..a.z..")"
end,
__index = {
-- Euclidean 3D length.
len = function(a) return sqrt(a.x*a.x + a.y*a.y + a.z*a.z) end,
-- Euclidean 3D squared length.
lensq = function(a) return a.x*a.x + a.y*a.y + a.z*a.z end,
-- Euclidean 2D length.
len2 = function(a) return sqrt(a.x*a.x + a.y*a.y) end,
-- Euclidean 2D squared length.
len2sq = function(a) return a.x*a.x + a.y*a.y end,
-- Manhattan-distance 3D length:
mhlen = function(a) return abs(a.x)+abs(a.y)+abs(a.z) end,
toivec3 = function(v) return ivec3_t(v.x, v.y, v.z) end,
-- BUILD-coordinate (z scaled by 16) <-> uniform conversions.
touniform = function(v)
return v:_isi()
and v:_ctor(v.x, v.y, arshift(v.z, 4))
or v:_ctor(v.x, v.y, v.z/16)
end,
tobuild = function(v) return v:_ctor(v.x, v.y, 16*v.z) end,
rotate = function(v, ang, pivot) return l_rotate(v, ang, pivot) end,
-- PRIVATE methods --
-- Get the type constructor for this vector.
_ctor = function(v, ...)
return v:_isi() and ivec3_t(...) or dvec3_t(...)
end,
-- Is <v> integer vec3? INTERNAL.
_isi = function(v)
return ffi.istype(ivec3_t, v)
end,
--- Serialization ---
_get_require = our_get_require,
_serialize = function(v)
return (v:_isi() and "_iv" or "_v").."("..v.x..","..v.y..","..v.z..")"
end,
},
}
ffi.metatype(dvec2_t, vec2_mt)
ffi.metatype(dvec3_t, vec3_mt)
ffi.metatype(ivec3_t, vec3_mt)
-- VEC2 user data constructor.
-- * vec2([x [, y]]), assuming that x and y are numbers. Vacant positions are
-- assumed to be 0.
-- * vec2(<compound>), <compound> can be anything indexable with "x" and "y"
function vec2(...)
local x, y = ...
if (type(x)=="number" or x==nil) then
return dvec2_t(...)
else
return dvec2_t(x.x, x.y)
end
end
-- VEC3 user data constructor.
-- Analogous to VEC2.
function vec3(...)
local x, y, z = ...
if (type(x)=="number" or x==nil) then
return dvec3_t(...)
else
return dvec3_t(x.x, x.y, x.z)
end
end
-- IVEC3 user data constructor.
function ivec3(...)
local x, y, z = ...
if (type(x)=="number" or x==nil) then
return ivec3_t(...)
else
return ivec3_t(x.x, x.y, x.z)
end
end
local vec2, vec3 = vec2, vec3
---=== MISCELLANEOUS MATH ===---
local intarg = ffi.new("int32_t [1]")
function bangvec(bang)
intarg[0] = bang -- round towards zero
return dvec3_t(cosb(intarg[0]), sinb(intarg[0]))
end
function kangvec(bang, z)
intarg[0] = bang -- round towards zero
return ivec3_t(kcos(intarg[0]), ksin(intarg[0]), z or 0)
end
function angvec(ang)
return dvec3_t(cos(ang), sin(ang))
end
local zerovec = vec3()
-- Point rotation. Note the different order of arguments from engine function.
-- XXX: passing mixed vec2/vec3 is problematic. Get rid of vec2?
-- <ang>: BUILD angle (0-2047 based)
function rotate(pos, ang, pivot)
pivot = pivot or zerovec
local p = vec3(pos)-pivot
local c, s = cosb(ang), sinb(ang)
local x, y = p.x, p.y
p.x = pivot.x + (c*x - s*y)
p.y = pivot.y + (c*y + s*x)
return p
end
l_rotate = rotate
-- Two-element vector cross product.
-- Anti-commutative, distributive.
local function cross2(v, w)
return v.y*w.x - v.x*w.y
end
-- Finds the intersection point of two lines given by
-- point a and vector v
-- and
-- point b and vector w
--
-- Returns:
-- if <TODO>, nil
-- if retpoint_p evaluates to a non-true value, coefficients cv and cw such that <TODO>
-- else, the intersection point
function intersect(a,v, b,w, retpoint_p)
local vxw = cross2(v,w)
if (vxw ~= 0) then
local btoa = vec2(a) - vec2(b)
local cv, cw = cross2(w, btoa)/vxw, cross2(v, btoa)/vxw
if (retpoint_p) then
return vec2(a) + cv*vec2(v)
else
return cv, cw
end
end
-- return nil if v and w parallel (or either of them is a point), or if
-- they contain NaNs
end

View file

@ -29,10 +29,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "vfs.h"
#ifdef LUNATIC
# include "lunatic_game.h"
#endif
static uint8_t precachehightile[2][(MAXTILES+7)>>3];
static int32_t g_precacheCount;

View file

@ -24,11 +24,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "premap.h"
#include "prlights.h"
#include "savegame.h"
#ifdef LUNATIC
# include "lunatic_game.h"
static int32_t g_savedOK;
const char *g_failedVarname;
#endif
#include "vfs.h"

View file

@ -1,55 +0,0 @@
-- INTERNAL
-- definitions of BUILD and game types for the Lunatic Interpreter
local ffi = require("ffi")
local ffiC = ffi.C
ffi.cdef[[
enum {
LUNATIC_CLIENT_MAPSTER32 = 0,
LUNATIC_CLIENT_EDUKE32 = 1,
LUNATIC_CLIENT = LUNATIC_CLIENT_MAPSTER32
}
]]
--== First, load the definitions common to the game's and editor's Lua interface.
decl = ffi.cdef
local defs_c = require("defs_common")
defs_c.finish_spritetype({})
defs_c.create_globals(_G)
-- TODO: provide access to only a subset, restict access to ffiC?
gv = ffiC
--== Mapster32-specific initialization
ffi.cdef "char *listsearchpath(int32_t initp);"
-- Add the search path directories to the Lua load path.
local initp = 1
while (true) do
local sp_c = ffiC.listsearchpath(initp)
if (sp_c == nil) then
break
end
local sp = ffi.string(sp_c)
assert(sp:sub(-1)=='/')
package.path = sp..'?.lua;'..package.path
initp = 0
end
-- Helper functions
local package = package
local require = require
function reload(modname)
package.loaded[modname] = nil
return require(modname)
end
--print('Lua load path: '..package.path)

File diff suppressed because it is too large Load diff

View file

@ -1,124 +0,0 @@
-- Implementation of a bound-checked array type factory for LuaJIT 2.0 or later.
--
-- Usage example:
--
-- > bcarray.new("int8_t", 3, "test", "three_pigs")
-- > a = ffi.new("struct { int32_t a; three_pigs p; int16_t b; }")
-- > =ffi.sizeof(a) --> 12
-- > b = ffi.new("__attribute__((packed)) struct { int32_t a; three_pigs p; int16_t b; }")
-- > =ffi.sizeof(b) --> 9
local ffi = require("ffi")
local string = require("string")
local table = require("table")
local assert = assert
local error = error
local pairs = pairs
local type = type
local bcarray = {}
-- Generate C decl for a sequence of <nelts> const struct members.
-- For example, for 4 elements,
-- "const $ _r1, _f2, _u3, _n4;"
local function flatten_array(nelts, rng)
local strtab = { "$ " }
if (rng and rng.getu32==nil) then
assert(type(rng)=="table")
for i=1,#rng do
strtab[i+1] = rng[i]..((i<#rng) and "," or ";")
end
else
for i=1,nelts do
local ch = 97 + (rng and (rng:getu32() % 25) or 0) -- 'a'..'z'
strtab[i+1] = string.format("_%c%x%s", ch, i, (i<nelts) and "," or ";")
end
end
return table.concat(strtab)
end
-- ctype = bcarray.new(basetype, numelts, showname [, typename] [, rng] [, mtadd])
-- (optional fields may be nil)
--
-- <numelts>: Number of elements in array (small number)
-- <showname>: The name to be shown for the derived type in error messages
-- <typename>: If non-nil, the name under which the derived type is typedef'd
-- <rng>: Random generator state + method :getu32(). If nil, then members are
-- named _a1, _a2, ...
-- It also may be a table containing member names at numeric indices 1..#rng.
-- <mtadd>: A table containing functions __index and/or __newindex. They are
-- called first and the bound-checking ones are tail-called then. If the
-- custom __index one returns something, it is returned by the composite one.
function bcarray.new(basetype, numelts, showname, typename, rng, mtadd)
local eltptr_t = ffi.typeof("$ *", ffi.typeof(basetype))
local mt = {
__index = function(ar, idx)
if (not (idx >= 0 and idx < numelts)) then
error("out-of-bounds "..showname.." read access", 2)
end
return ffi.cast(eltptr_t, ar)[idx]
end,
-- NOTE: this function will be dead code if the prefixed __newindex
-- errors out unconditionally or the bcarray is declared 'const'.
__newindex = function(ar, idx, val)
if (not (idx >= 0 and idx < numelts)) then
error("out-of-bounds "..showname.." write access", 2)
end
ffi.cast(eltptr_t, ar)[idx] = val
end,
}
if (mtadd ~= nil) then
local curindexf, curnewindexf = mt.__index, mt.__newindex
local addindexf, addnewindexf = mtadd.__index, mtadd.__newindex
if (addindexf) then
-- Additional __index metamethod given.
mt.__index = function(ar, idx)
local sth = addindexf(ar, idx)
if (sth ~= nil) then
return sth
end
return curindexf(ar, idx)
end
end
if (addnewindexf) then
-- Additional __newindex metamethod given.
mt.__newindex = function(ar, idx, val)
addnewindexf(ar, idx, val)
return curnewindexf(ar, idx, val)
end
end
end
local cdeclstr = "struct {"..flatten_array(numelts, rng).."}"
local bcarray_t = ffi.typeof(cdeclstr, ffi.typeof(basetype));
bcarray_t = ffi.metatype(bcarray_t, mt)
if (not (rng and rng.getu32==nil)) then
-- When passing a member name table, it is allowed to have a different
-- number of named members than array elements.
assert(ffi.sizeof(bcarray_t) == ffi.sizeof(basetype)*numelts)
end
if (typename ~= nil) then
-- Register the type name in the global namespace.
assert(type(typename)=="string")
ffi.cdef("typedef $ $;", bcarray_t, typename)
end
return bcarray_t
end
return bcarray

View file

@ -1,126 +0,0 @@
-- Bound-checking functions for engine and game "things".
local ffiC = require("ffi").C
local type = type
local error = error
local bcheck = {}
--== ENGINE ==--
function bcheck.sector_idx(sectnum)
if (not (sectnum >= 0 and sectnum < ffiC.numsectors)) then
error("invalid sector number "..sectnum, 3)
end
end
function bcheck.wall_idx(wallnum)
if (not (wallnum >= 0 and wallnum < ffiC.numwalls)) then
error("invalid wall number "..wallnum, 3)
end
end
-- TODO: Provide another function that also checks whether the sprite exists in
-- the game world (statnum != MAXSTATUS).
function bcheck.sprite_idx(spritenum)
-- if (not (spritenum >= 0 and spritenum < ffiC.MAXSPRITES)) then
if (not (spritenum >= 0 and spritenum < ffiC.MAXSPRITES)) then
error("invalid sprite number "..spritenum, 3)
end
end
function bcheck.tile_idx(tilenum)
if (not (tilenum >= 0 and tilenum < ffiC.MAXTILES)) then
error("invalid tile number "..tilenum, 3)
end
end
--== HELPERS ==--
function bcheck.number(val, errlev)
if (type(val)~="number" or val~=val) then
error("invalid argument: must be a non-NaN number", errlev or 3)
end
end
function bcheck.type(val, typestr, errlev)
if (type(val)~=typestr) then
error("invalid argument: must be a "..typestr, errlev or 3)
end
end
--== GAME ==--
if (ffiC.LUNATIC_CLIENT == ffiC.LUNATIC_CLIENT_MAPSTER32) then
return bcheck
end
local con_lang = require("con_lang")
function bcheck.player_idx(snum)
if (not (snum >= 0 and snum < ffiC.g_mostConcurrentPlayers)) then
error("invalid player number "..snum, 3)
end
end
function bcheck.sound_idx(sndidx)
if (not (sndidx >= 0 and sndidx < con_lang.MAXSOUNDS)) then
error("invalid sound number "..sndidx, 3)
end
end
function bcheck.weapon_idx(weap)
if (not (weap >= 0 and weap < ffiC.MAX_WEAPONS)) then
error("Invalid weapon ID "..weap, 3)
end
end
function bcheck.inventory_idx(inv)
if (not (inv >= 0 and inv < ffiC.GET_MAX)) then
error("Invalid inventory ID "..inv, 3)
end
end
function bcheck.volume_idx(volume)
if (not (volume >= 0 and volume < con_lang.MAXVOLUMES)) then
error("invalid volume number "..volume, 3)
end
end
function bcheck.level_idx(level)
if (not (level >= 0 and level < con_lang.MAXLEVELS)) then
error("invalid level number "..level, 3)
end
end
function bcheck.linear_map_idx(idx)
if (not (idx >= 0 and idx <= con_lang.MAXLEVELS * con_lang.MAXVOLUMES)) then
error("invalid linear map index "..idx, 3)
end
end
function bcheck.quote_idx(qnum, onlyidx)
if (not (qnum >= 0 and qnum < con_lang.MAXQUOTES)) then
error("invalid quote number "..qnum, 3)
end
local cstr = ffiC.apStrings[qnum]
if (onlyidx) then
return cstr
end
if (cstr == nil) then
error("null quote "..qnum, 3)
end
return cstr
end
function bcheck.top_level(funcname, errlev)
if (ffiC.g_elCallDepth > 0) then
error("Invalid use of "..funcname..": must be called from top level", errlev or 3)
end
end
return bcheck

View file

@ -1,293 +0,0 @@
-- "Bit array" module based on LuaJIT's BitOp.
local bit = require "bit"
local math = require "math"
local ffi = require "ffi"
local assert = assert
local error = error
local type = type
local tostring = tostring
module(...)
local bitar_ct = ffi.typeof[[
struct {
const double maxbidx; // last permissible bit index
const double maxidx; // last permissible int32_t index
const intptr_t arptr; // address of the int32_t array
}]]
local ptr_to_int = ffi.typeof("int32_t *")
local anchor = {}
-- population count of a nibble
local nibpop = ffi.new("double [?]", 16,
{ 0, 1, 1, 2, 1, 2, 2, 3,
1, 2, 2, 3, 2, 3, 3, 4 })
-- ...and of a byte
local bytepop = ffi.new("double [?]", 256)
for i=0,255 do
bytepop[i] = nibpop[bit.band(i, 15)] + nibpop[bit.rshift(i, 4)]
end
nibpop = nil
local function bitar_from_intar(maxbidx, maxidx, ar)
-- We need to have the int32_t[?] array be reachable so that it will not be
-- garbage collected
local ar_intptr = ffi.cast("intptr_t", ar)
anchor[tostring(ar_intptr)] = ar
-- Leaving the (potential) high trailing bits at 0 lets us not worry
-- about them in the population count calculation (__len metamethod).
-- Also, this is correct for maxbidx%32 == 0, since BitOp's shifts
-- mask the 5 lower bits of the counts.
local numremain = bit.band(maxbidx+1, 31)
ar[maxidx] = bit.band(ar[maxidx], bit.rshift(-1, 32-numremain))
return bitar_ct(maxbidx, maxidx, ar_intptr)
end
local function setop_common_rel(s1, s2)
if (s1.maxbidx ~= s2.maxbidx) then
error("bad arguments to bit array set op: must be of same length", 4)
end
local ar1 = ffi.cast(ptr_to_int, s1.arptr)
local ar2 = ffi.cast(ptr_to_int, s2.arptr)
return ar1, ar2
end
local function setop_common(s1, s2)
if (not ffi.istype(bitar_ct, s1) or not ffi.istype(bitar_ct, s2)) then
error("bad arguments to bit array set op: both must be 'bitar' types", 3)
end
local ar1, ar2 = setop_common_rel(s1, s2)
local ar = ffi.new("int32_t [?]", s1.maxidx+1)
return ar, ar1, ar2
end
local mt = {
--- Operational methods
__add = function(s1, s2) -- set union
local ar, ar1, ar2 = setop_common(s1, s2)
for i=0,s1.maxidx do
ar[i] = bit.bor(ar1[i], ar2[i])
end
return bitar_from_intar(s1.maxbidx, s1.maxidx, ar)
end,
__mul = function(s1, s2) -- set intersection
local ar, ar1, ar2 = setop_common(s1, s2)
for i=0,s1.maxidx do
ar[i] = bit.band(ar1[i], ar2[i])
end
return bitar_from_intar(s1.maxbidx, s1.maxidx, ar)
end,
__sub = function(s1, s2) -- set difference
local ar, ar1, ar2 = setop_common(s1, s2)
for i=0,s1.maxidx do
ar[i] = bit.band(ar1[i], bit.bnot(ar2[i]))
end
return bitar_from_intar(s1.maxbidx, s1.maxidx, ar)
end,
__unm = function(s) -- bitwise NOT
local newar = ffi.new("int32_t [?]", s.maxidx+1)
local oldar = ffi.cast(ptr_to_int, s.arptr)
for i=0,s.maxidx do
newar[i] = bit.bnot(oldar[i])
end
return bitar_from_intar(s.maxbidx, s.maxidx, newar)
end,
--- Additional operations
__index = {
-- TODO: Rename to 'testi', 'seti', 'cleari'; add 'flipi'?
-- Is bit i set?
isset = function(s, i)
if (not (i >= 0 and i<=s.maxbidx)) then
error("bad bit index for isset: must be in [0.."..s.maxbidx.."]", 2)
end
s = ffi.cast(ptr_to_int, s.arptr)
return (bit.band(s[bit.rshift(i, 5)], bit.lshift(1, i)) ~= 0)
end,
-- Clear bit i.
set0 = function(s, i)
if (not (i >= 0 and i<=s.maxbidx)) then
error("bad bit index for set0: must be in [0.."..s.maxbidx.."]", 2)
end
s = ffi.cast(ptr_to_int, s.arptr)
local jx = bit.rshift(i, 5)
s[jx] = bit.band(s[jx], bit.rol(0xfffffffe, i))
end,
-- Set bit i.
set1 = function(s, i)
if (not (i >= 0 and i<=s.maxbidx)) then
error("bad bit index for set1: must be in [0.."..s.maxbidx.."]", 2)
end
s = ffi.cast(ptr_to_int, s.arptr)
local jx = bit.rshift(i, 5)
s[jx] = bit.bor(s[jx], bit.rol(0x00000001, i))
end
},
--- Relational methods
__eq = function(s1, s2) -- set identity
local ar1, ar2 = setop_common_rel(s1, s2)
for i=0,s1.maxidx do
if (bit.bxor(ar1[i], ar2[i]) ~= 0) then
return false
end
end
return true
end,
__le = function(s1, s2)
local ar1, ar2 = setop_common_rel(s1, s2)
for i=0,s1.maxidx do
if (bit.band(ar1[i], bit.bnot(ar2[i])) ~= 0) then
return false
end
end
return true
end,
__lt = function(s1, s2)
return s1 <= s2 and not (s2 == s1)
end,
-- The length operator gets the population count of the bit array, i.e. the
-- number of set bits.
__len = function(s)
local ar = ffi.cast(ptr_to_int, s.arptr)
local popcnt = 0
for i=0,s.maxidx do
popcnt = popcnt + bytepop[bit.band(ar[i], 255)] +
bytepop[bit.band(bit.rshift(ar[i], 8), 255)] +
bytepop[bit.band(bit.rshift(ar[i], 16), 255)] +
bytepop[bit.rshift(ar[i], 24)]
end
return popcnt
end,
-- serialization
__tostring = function(s)
local size=s.maxidx+1
local ar = ffi.cast(ptr_to_int, s.arptr)
local hdr = "bitar.new("..s.maxbidx..", '"
local ofs = #hdr
local totalstrlen = ofs+8*size+2
local str = ffi.new("char [?]", totalstrlen)
ffi.copy(str, hdr, ofs)
for i=0,s.maxidx do
-- 'a' is ASCII 97
for nib=0,7 do
str[ofs + 8*i + nib] = 97 + bit.band(bit.rshift(ar[i], 4*nib), 0x0000000f)
end
end
ffi.copy(str+totalstrlen-2, "')", 2)
return ffi.string(str, totalstrlen)
end,
-- On garbage collection of the bitar, clear the array's anchor so that it
-- can be collected too.
__gc = function(s)
anchor[tostring(s.arptr)] = nil
end,
__metatable = true,
}
ffi.metatype(bitar_ct, mt)
-- Create new bit array.
function new(maxbidx, initval)
if (type(maxbidx) ~= "number" or not (maxbidx >= 0 and maxbidx <= (2^31)-2)) then
-- NOTE: Uh-oh, we can't write '2^31' because that would be interpreted
-- as color by OSD_Printf.
error("bad argument #1 to bitar.new (must be a number in [0..(2**31)-2])", 2)
end
if (math.floor(maxbidx) ~= maxbidx) then
error("bad argument #1 to bitar.new (must be an integral number)")
end
if (ffi.istype(ptr_to_int, initval)) then
-- Initialization from an array on the C side. INTERNAL.
-- Cannot be reached by user code since there's no access to the FFI
-- (and thus no way to create pointers).
return bitar_from_intar(maxbidx, (maxbidx+1)/32-1, initval)
elseif (type(initval)=="string") then
-- String containing hex digits (a..p) given, for INTERNAL use only.
-- XXX: Can be reached by user code.
local lstr = initval
local numnibs = #lstr
assert(numnibs%8 == 0)
local size = numnibs/8
local maxidx = size-1
local ar = ffi.new("int32_t [?]", size)
local str = ffi.new("char [?]", numnibs)
ffi.copy(str, lstr, numnibs)
for i=0,maxidx do
ar[i] = 0
for nib=0,7 do
local hexdig = str[8*i + nib]
assert(hexdig >= 97 and hexdig < 97+16)
ar[i] = bit.bor(ar[i], bit.lshift(hexdig-97, 4*nib))
end
end
-- NOTE: <maxbidx> cannot be extracted from the string, use the passed one.
return bitar_from_intar(maxbidx, maxidx, ar)
else
-- User-requested bitar creation.
if (initval ~= 0 and initval ~= 1) then
error("bad argument #2 to bitar.new (must be either 0 or 1)", 2)
end
local maxidx = math.floor(maxbidx/32)
local size = maxidx+1
local ar = ffi.new("int32_t [?]", size)
if (initval==1) then
ffi.fill(ar, size*4, -1)
end
return bitar_from_intar(maxbidx, maxidx, ar)
end
end

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more