diff --git a/polymer/eduke32/build/src/build.c b/polymer/eduke32/build/src/build.c index feacc55f9..530e85188 100644 --- a/polymer/eduke32/build/src/build.c +++ b/polymer/eduke32/build/src/build.c @@ -7652,15 +7652,16 @@ CANCEL: } else { - int32_t bakstat=-1; + int32_t bakstat=-1, ret; mapinfofull_t bakmap; if (highlightsectorcnt > 0) bakstat = backup_highlighted_map(&bakmap); -// __old_mapcopy_2__ - if (LoadBoard(NULL, 4)) + + ret = LoadBoard(NULL, 4); + if (ret) { - message("Invalid map format, nothing loaded."); + message("Invalid map format, nothing loaded (code %d).", ret); if (bakstat==0) mapinfofull_free(&bakmap); } diff --git a/polymer/eduke32/build/src/engine.c b/polymer/eduke32/build/src/engine.c index 79b7235d6..ea42de8f9 100644 --- a/polymer/eduke32/build/src/engine.c +++ b/polymer/eduke32/build/src/engine.c @@ -9657,6 +9657,11 @@ void drawmapview(int32_t dax, int32_t day, int32_t zoome, int16_t ang) //////////////////// LOADING AND SAVING ROUTINES //////////////////// +static inline int32_t have_maptext(void) +{ + return (mapversion >= 10); +} + static void prepare_loadboard(int32_t fil, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum) { initspritelists(); @@ -9665,11 +9670,14 @@ static void prepare_loadboard(int32_t fil, vec3_t *dapos, int16_t *daang, int16_ Bmemset(show2dsprite, 0, sizeof(show2dsprite)); Bmemset(show2dwall, 0, sizeof(show2dwall)); - kread(fil,&dapos->x,4); dapos->x = B_LITTLE32(dapos->x); - kread(fil,&dapos->y,4); dapos->y = B_LITTLE32(dapos->y); - kread(fil,&dapos->z,4); dapos->z = B_LITTLE32(dapos->z); - kread(fil,daang,2); *daang = B_LITTLE16(*daang) & 2047; - kread(fil,dacursectnum,2); *dacursectnum = B_LITTLE16(*dacursectnum); + if (!have_maptext()) + { + kread(fil,&dapos->x,4); dapos->x = B_LITTLE32(dapos->x); + kread(fil,&dapos->y,4); dapos->y = B_LITTLE32(dapos->y); + kread(fil,&dapos->z,4); dapos->z = B_LITTLE32(dapos->z); + kread(fil,daang,2); *daang = B_LITTLE16(*daang) & 2047; + kread(fil,dacursectnum,2); *dacursectnum = B_LITTLE16(*dacursectnum); + } } static int32_t finish_loadboard(const vec3_t *dapos, int16_t *dacursectnum, int16_t numsprites, char myflags) @@ -9797,11 +9805,17 @@ static void check_sprite(int32_t i) } } +#ifdef NEW_MAP_FORMAT +// Returns the number of sprites, or <0 on error. +int32_t (*loadboard_maptext)(int32_t fil, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum); +#endif // flags: 1, 2: former parameter "fromwhere" // 4: don't call polymer_loadboard // returns: -1: file not found // -2: invalid version +// -3: invalid number of sectors, walls or sprites +// <= -4: map-text error int32_t loadboard(char *filename, char flags, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum) { int32_t fil, i; @@ -9813,29 +9827,68 @@ int32_t loadboard(char *filename, char flags, vec3_t *dapos, int16_t *daang, int i = Bstrlen(filename)-1; if (filename[i] == 255) { filename[i] = 0; flags = 1; } // JBF 20040119: "compatibility" if ((fil = kopen4load(filename,flags)) == -1) - { mapversion = 7; return(-1); } + { mapversion = 7; return -1; } - kread(fil,&mapversion,4); mapversion = B_LITTLE32(mapversion); + if (kread(fil, &mapversion, 4) != 4) + return -2; { - int32_t ok = (mapversion==7); + int32_t ok = 0; + +#ifdef NEW_MAP_FORMAT + // Check for map-text first. + if (!Bmemcmp(&mapversion, "--ED", 4)) + { + mapversion = 10; + ok = 1; + } + else +#endif + { + // Not map-text. We expect a little-endian version int now. + mapversion = B_LITTLE32(mapversion); #ifdef YAX_ENABLE - ok |= (mapversion==9); + ok |= (mapversion==9); #endif #if MAXSECTORS==MAXSECTORSV8 - // v8 engine - ok |= (mapversion==8); + // v8 engine + ok |= (mapversion==8); #endif - if (!ok) { kclose(fil); return(-2); } + ok |= (mapversion==7); + } + + if (!ok) + { + kclose(fil); + return -2; + } } prepare_loadboard(fil, dapos, daang, dacursectnum); +#ifdef NEW_MAP_FORMAT + if (have_maptext()) + { + int32_t ret = klseek(fil, 0, SEEK_SET); + + if (ret == 0) + ret = loadboard_maptext(fil, dapos, daang, dacursectnum); + + if (ret < 0) + { + kclose(fil); + return ret; + } + + numsprites = ret; + goto skip_reading_mapbin; + } +#endif ////////// Read sectors ////////// kread(fil,&numsectors,2); numsectors = B_LITTLE16(numsectors); - if ((unsigned)numsectors >= MYMAXSECTORS()+1) { kclose(fil); return(-1); } + if ((unsigned)numsectors >= MYMAXSECTORS()+1) { kclose(fil); return -3; } kread(fil, sector, sizeof(sectortypev7)*numsectors); @@ -9867,7 +9920,7 @@ int32_t loadboard(char *filename, char flags, vec3_t *dapos, int16_t *daang, int ////////// Read walls ////////// kread(fil,&numwalls,2); numwalls = B_LITTLE16(numwalls); - if ((unsigned)numwalls >= MYMAXWALLS()+1) { kclose(fil); return(-1); } + if ((unsigned)numwalls >= MYMAXWALLS()+1) { kclose(fil); return -3; } kread(fil, wall, sizeof(walltypev7)*numwalls); @@ -9897,30 +9950,36 @@ int32_t loadboard(char *filename, char flags, vec3_t *dapos, int16_t *daang, int ////////// Read sprites ////////// kread(fil,&numsprites,2); numsprites = B_LITTLE16(numsprites); - if ((unsigned)numsprites >= MYMAXSPRITES()+1) { kclose(fil); return(-1); } + if ((unsigned)numsprites >= MYMAXSPRITES()+1) { kclose(fil); return -3; } kread(fil, sprite, sizeof(spritetype)*numsprites); +#ifdef NEW_MAP_FORMAT +skip_reading_mapbin: +#endif kclose(fil); // Done reading file. for (i=numsprites-1; i>=0; i--) { - sprite[i].x = B_LITTLE32(sprite[i].x); - sprite[i].y = B_LITTLE32(sprite[i].y); - sprite[i].z = B_LITTLE32(sprite[i].z); - sprite[i].cstat = B_LITTLE16(sprite[i].cstat); - sprite[i].picnum = B_LITTLE16(sprite[i].picnum); - sprite[i].sectnum = B_LITTLE16(sprite[i].sectnum); - sprite[i].statnum = B_LITTLE16(sprite[i].statnum); - sprite[i].ang = B_LITTLE16(sprite[i].ang); - sprite[i].owner = B_LITTLE16(sprite[i].owner); - sprite[i].xvel = B_LITTLE16(sprite[i].xvel); - sprite[i].yvel = B_LITTLE16(sprite[i].yvel); - sprite[i].zvel = B_LITTLE16(sprite[i].zvel); - sprite[i].lotag = B_LITTLE16(sprite[i].lotag); - sprite[i].hitag = B_LITTLE16(sprite[i].hitag); - sprite[i].extra = B_LITTLE16(sprite[i].extra); + if (!have_maptext()) + { + sprite[i].x = B_LITTLE32(sprite[i].x); + sprite[i].y = B_LITTLE32(sprite[i].y); + sprite[i].z = B_LITTLE32(sprite[i].z); + sprite[i].cstat = B_LITTLE16(sprite[i].cstat); + sprite[i].picnum = B_LITTLE16(sprite[i].picnum); + sprite[i].sectnum = B_LITTLE16(sprite[i].sectnum); + sprite[i].statnum = B_LITTLE16(sprite[i].statnum); + sprite[i].ang = B_LITTLE16(sprite[i].ang); + sprite[i].owner = B_LITTLE16(sprite[i].owner); + sprite[i].xvel = B_LITTLE16(sprite[i].xvel); + sprite[i].yvel = B_LITTLE16(sprite[i].yvel); + sprite[i].zvel = B_LITTLE16(sprite[i].zvel); + sprite[i].lotag = B_LITTLE16(sprite[i].lotag); + sprite[i].hitag = B_LITTLE16(sprite[i].hitag); + sprite[i].extra = B_LITTLE16(sprite[i].extra); + } check_sprite(i); } diff --git a/polymer/eduke32/source/lunatic/defs.ilua b/polymer/eduke32/source/lunatic/defs.ilua index 886e73fa9..e0e340a25 100644 --- a/polymer/eduke32/source/lunatic/defs.ilua +++ b/polymer/eduke32/source/lunatic/defs.ilua @@ -1285,7 +1285,13 @@ local function errorf(level, fmt, ...) end local function readintostr_mod(fn) - return defs_c.readintostr(fn, ffiC.kopen4loadfrommod) + -- TODO: g_loadFromGroupOnly? + local fd = ffiC.kopen4loadfrommod(fn, 0) + if (fd < 0) then + return nil + end + + return defs_c.readintostr(fd) end diff --git a/polymer/eduke32/source/lunatic/defs_common.lua b/polymer/eduke32/source/lunatic/defs_common.lua index e85da543e..d60e43bfa 100644 --- a/polymer/eduke32/source/lunatic/defs_common.lua +++ b/polymer/eduke32/source/lunatic/defs_common.lua @@ -319,34 +319,31 @@ enum { }; ]] +ffi.cdef(maybe_strip_const("const int16_t numsectors, numwalls;")) + ffi.cdef[[ -const int16_t numsectors, numwalls; const int32_t numyaxbunches; // XXX const int32_t totalclock; int32_t randomseed; // DEPRECATED const int32_t xdim, ydim; const int32_t windowx1, windowy1, windowx2, windowy2; +]] +decl[[ int32_t kopen4load(const char *filename, char searchfirst); int32_t kfilelength(int32_t handle); void kclose(int32_t handle); int32_t kread(int32_t handle, void *buffer, int32_t leng); int32_t klseek(int32_t handle, int32_t offset, int32_t whence); + +int32_t sectorofwall_noquick(int16_t theline); ]] -function readintostr(fn, kopen4load_func) +-- Reads the whole file given by the k* file descriptor into a Lua string. +-- Always closes the file descriptor. +function readintostr(fd, kopen4load_func) -- XXX: this is pretty much the same as the code in L_RunOnce() - if (kopen4load_func == nil) then - kopen4load_func = ffiC.kopen4load - end - - -- TODO: for game, g_loadFromGroupOnly? - local fd = kopen4load_func(fn, 0) - if (fd < 0) then - return nil - end - local sz = ffiC.kfilelength(fd) if (sz == 0) then ffiC.kclose(fd) diff --git a/polymer/eduke32/source/lunatic/dynsymlist b/polymer/eduke32/source/lunatic/dynsymlist index 02d293bd7..61b7252d7 100644 --- a/polymer/eduke32/source/lunatic/dynsymlist +++ b/polymer/eduke32/source/lunatic/dynsymlist @@ -29,6 +29,7 @@ viewingrange; yax_getbunch; +sectorofwall_noquick; getceilzofslopeptr; getflorzofslopeptr; getzsofslopeptr; diff --git a/polymer/eduke32/source/lunatic/dynsymlist_m32 b/polymer/eduke32/source/lunatic/dynsymlist_m32 index 2430e8efe..c1f183d0d 100644 --- a/polymer/eduke32/source/lunatic/dynsymlist_m32 +++ b/polymer/eduke32/source/lunatic/dynsymlist_m32 @@ -29,6 +29,7 @@ viewingrange; yax_getbunch; +sectorofwall_noquick; getceilzofslopeptr; getflorzofslopeptr; getzsofslopeptr; diff --git a/polymer/eduke32/source/lunatic/engine_maptext.lua b/polymer/eduke32/source/lunatic/engine_maptext.lua index 6a21fbe90..f42996f48 100644 --- a/polymer/eduke32/source/lunatic/engine_maptext.lua +++ b/polymer/eduke32/source/lunatic/engine_maptext.lua @@ -4,16 +4,22 @@ local ffi = require("ffi") local ffiC = ffi.C +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); ]] @@ -46,6 +52,9 @@ local sector_members = { -- a newline should appear in the output. local sector_ord = { mand="1 23 45 67 ", 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 @@ -61,10 +70,12 @@ local wall_members = { f = "cstat", m = "overpicnum", 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 m p oie" } +local wall_ord = { mand="1 23 4 5 6 78 90 ", opt="f m p wW oie" } +local wall_default = ffi.new("const walltype", { extra = -1, upwall=-1, dnwall=-1 }) local sprite_members = { @@ -89,6 +100,7 @@ local sprite_members = { } local sprite_ord = { mand="123 4 5 6 7 8 90 ", opt="p c _ xy s w XYZ oie" } +local sprite_default = ffi.new("const spritetype", { clipdist=32, owner=-1, extra=-1 }) --== SAVING ==-- @@ -127,15 +139,14 @@ local function write_struct(f, struct, members, ord) end end) - f:write(str.."},\n") + local neednl = (#str>0 and str:sub(-1)~="\n") + f:write(str..(neednl and "\n" or "").."},\n") end --- In map-text, instead of saving wall[].point2, we store whether a particular --- wall is the last one in its loop instead. -local function save_tweak_point2() +-- common +local function check_bad_point2() local lastloopstart = 0 - -- Check first. for i=0,ffiC.numwalls-1 do local p2 = ffiC.wall[i].point2 @@ -149,6 +160,15 @@ local function save_tweak_point2() lastloopstart = i+1 end end +end + +-- In map-text, instead of saving wall[].point2, we store whether a particular +-- wall is the last one in its loop instead. +local function save_tweak_point2() + -- Check first. + if (check_bad_point2()) then + return true + end -- Do it for real. lastloopstart = 0 @@ -165,7 +185,8 @@ local function save_tweak_point2() end end -local function save_restore_point2() +-- common +local function restore_point2() local lastloopstart = 0 for i=0,ffiC.numwalls-1 do @@ -193,7 +214,7 @@ local function saveboard_maptext(filename, pos, ang, cursectnum) if (f == nil) then print(string.format("Couldn't open \"%s\" for writing: %s\n", filename, msg)) - save_restore_point2() + restore_point2() return -1 end @@ -215,14 +236,14 @@ local function saveboard_maptext(filename, pos, ang, cursectnum) for i=0,ffiC.numsectors-1 do write_struct(f, ffiC.sector[i], sector_members, sector_ord) end - f:write("}\n\n") + 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") + f:write("},\n\n") -- Sprites. f:write("sprite={\n") @@ -231,16 +252,240 @@ local function saveboard_maptext(filename, pos, ang, cursectnum) write_struct(f, ffiC.sprite[i], sprite_members, sprite_ord) end end - f:write("}\n\n") + f:write("},\n\n") f:write("}\n"); -- Done. f:close() - save_restore_point2() + restore_point2() return 0 end +--== LOADING ==-- + +local function isnum(v) + return (type(v)=="number") +end + +local function istab(v) + return (type(v)=="table") +end + +-- Checks whether is a table all values of are of type . +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 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 into C struct , using the struct +-- description . +-- 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 + + 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-6 + end + + if (not tabofnumtabs(msector, sector_members) or + not tabofnumtabs(mwall, wall_members) or + not tabofnumtabs(msprite, sprite_members)) + then + return RETERR-7 + end + + local numsectors, numwalls, numsprites = #msector, #mwall, #msprite + local sector, wall, sprite = ffiC.sector, ffiC.wall, ffiC.sprite + + if (numsectors+0ULL > ffiC.MAXSECTORS or numwalls+0ULL > ffiC.MAXWALLS or + numsprites > ffiC.MAXSPRITES) + then + return RETERR-8 + 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-9 + end + end + + -- Walls. + for i=0,numwalls-1 do + if (read_struct(wall[i], mwall[i+1], wall_members, wall_default)) then + return RETERR-10 + end + end + + -- Sprites. + for i=0,numsprites-1 do + if (read_struct(sprite[i], msprite[i+1], sprite_members, sprite_default)) then + return RETERR-11 + 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 + + -- 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 + + -- .point2 in {0, 1} --> wall index + restore_point2() + + -- Check .point2 at least. + if (check_bad_point2()) then + return RETERR-12 + 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 diff --git a/polymer/eduke32/source/lunatic/lunacon.lua b/polymer/eduke32/source/lunatic/lunacon.lua index 562dff0fa..9b940db95 100644 --- a/polymer/eduke32/source/lunatic/lunacon.lua +++ b/polymer/eduke32/source/lunatic/lunacon.lua @@ -237,6 +237,7 @@ local function new_initial_gvartab() numsectors = RO "_gv.numsectors", NUMSECTORS = RO "_gv.numsectors", NUMWALLS = RO "_gv.numwalls", + -- TODO: Numsprites randomseed = RW "_gv.randomseed", totalclock = RO "_gv.totalclock",