// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
// Ken Silverman's official web site: "http://www.advsys.net/ken"
// See the included license file "BUILDLIC.TXT" for license info.
//
// This file has been modified from Ken Silverman's original release
// by Jonathon Fowler (jonof@edgenetwk.com)

#include "build.h"
#include "compat.h"
#include "pragmas.h"
#include "osd.h"
#include "cache1d.h"
#include "editor.h"

#include "baselayer.h"
#ifdef RENDERTYPEWIN
#include "winlayer.h"
#endif

#include "m32script.h"

#define TIMERINTSPERSECOND 120

#define updatecrc16(crc,dat) (crc = (((crc<<8)&65535)^crctable[((((uint16_t)crc)>>8)&65535)^dat]))
static int32_t crctable[256];
static char kensig[64];

extern const char *ExtGetVer(void);

char noclip=0;

// 0   1     2     3      4       5      6      7
// up, down, left, right, lshift, rctrl, lctrl, space
// 8  9  10    11    12   13
// a, z, pgdn, pgup, [,], [.]
// 14       15     16 17 18   19
// kpenter, enter, =, -, tab, `
uint8_t buildkeys[NUMBUILDKEYS] =
{
    0xc8,0xd0,0xcb,0xcd,0x2a,0x9d,0x1d,0x39,
    0x1e,0x2c,0xd1,0xc9,0x33,0x34,
    0x9c,0x1c,0xd,0xc,0xf,0x29
};

vec3_t pos;
int32_t horiz = 100;
int16_t ang, cursectnum;
int32_t hvel, vel, svel, angvel;

static int32_t mousexsurp = 0, mouseysurp = 0;

int32_t grponlymode = 0;
int32_t graphicsmode = 0;
extern int32_t totalclocklock;

int32_t synctics = 0, lockclock = 0;

// those ones save the respective 3d video vars while in 2d mode
// so that exiting from mapster32 in 2d mode saves the correct ones
double vid_gamma_3d=-1, vid_contrast_3d=-1, vid_brightness_3d=-1;

int32_t xdim2d = 640, ydim2d = 480, xdimgame = 640, ydimgame = 480, bppgame = 8;
int32_t forcesetup = 1;

int32_t g_maxCacheSize = 24<<20;
//extern int32_t cachesize, artsize;

static int16_t oldmousebstatus = 0;
char game_executable[BMAX_PATH] = DEFAULT_GAME_LOCAL_EXEC;
int32_t zlock = 0x7fffffff, zmode = 0, whitecol, kensplayerheight = 32;
//int16_t defaultspritecstat = 0;

int16_t localartfreq[MAXTILES];
int16_t localartlookup[MAXTILES], localartlookupnum;

char tempbuf[4096];

char names[MAXTILES][25];
const char *g_namesFileName = "NAMES.H";

int16_t asksave = 0;
int32_t osearchx, osearchy;                               //old search input

int32_t grid = 3, autogrid = 0, gridlock = 1, showtags = 2;
int32_t zoom = 768, gettilezoom = 1;
int32_t lastpm16time = 0;

int32_t numsprites;
extern int32_t mapversion;

int16_t highlight[MAXWALLS+MAXSPRITES];
int16_t highlightsector[MAXSECTORS], highlightsectorcnt = -1;
extern char textfont[128][8];

// only valid when highlightsectorcnt>0 and no structural
// modifications (deleting/inserting sectors or points, setting new firstwall)
// have been made
static int16_t onextwall[MAXWALLS];  // onextwall[i]>=0 implies wall[j].nextwall < 0
static void mkonwvalid(void) { chsecptr_onextwall = onextwall; }
static void mkonwinvalid(void) { chsecptr_onextwall = NULL; }
static int32_t onwisvalid(void) { return chsecptr_onextwall != NULL; }

int32_t temppicnum, tempcstat, templotag, temphitag, tempextra;
uint32_t temppal, tempvis, tempxrepeat, tempyrepeat, tempxpanning=0, tempypanning=0;
int32_t tempshade, tempxvel, tempyvel, tempzvel;
char somethingintab = 255;

int32_t mlook = 0, mskip=0;
int32_t revertCTRL=0,scrollamount=3;
int32_t unrealedlook=1, quickmapcycling=1; //PK

char program_origcwd[BMAX_PATH];
const char *mapster32_fullpath;
char *testplay_addparam = 0;

static char boardfilename[BMAX_PATH], selectedboardfilename[BMAX_PATH];

static CACHE1D_FIND_REC *finddirs=NULL, *findfiles=NULL, *finddirshigh=NULL, *findfileshigh=NULL;
static int32_t numdirs=0, numfiles=0;
static int32_t currentlist=0;

//static int32_t repeatcountx, repeatcounty;

static int32_t fillist[640];
// used for fillsector and point selection in side-view mode:
static int32_t tempxyar[MAXWALLS][2] ATTRIBUTE((aligned(8)));

static int32_t mousx, mousy;
int16_t prefixtiles[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
uint8_t hlsectorbitmap[MAXSECTORS>>3];  // show2dsector is already taken...
static int32_t minhlsectorfloorz = 0;

static uint8_t visited[MAXWALLS>>3];  // used for AlignWalls and trace_loop

typedef struct
{
    int16_t numsectors, numwalls, numsprites;
    sectortype *sector;
    walltype *wall;
    spritetype *sprite;
} mapinfofull_t;

static int32_t backup_highlighted_map(mapinfofull_t *mapinfo);
static int32_t restore_highlighted_map(mapinfofull_t *mapinfo);

/*
static char scantoasc[128] =
{
    0,0,'1','2','3','4','5','6','7','8','9','0','-','=',0,0,
    'q','w','e','r','t','y','u','i','o','p','[',']',0,0,'a','s',
    'd','f','g','h','j','k','l',';',39,'`',0,92,'z','x','c','v',
    'b','n','m',',','.','/',0,'*',0,32,0,0,0,0,0,0,
    0,0,0,0,0,0,0,'7','8','9','-','4','5','6','+','1',
    '2','3','0','.',0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};
static char scantoascwithshift[128] =
{
    0,0,'!','@','#','$','%','^','&','*','(',')','_','+',0,0,
    'Q','W','E','R','T','Y','U','I','O','P','{','}',0,0,'A','S',
    'D','F','G','H','J','K','L',':',34,'~',0,'|','Z','X','C','V',
    'B','N','M','<','>','?',0,'*',0,32,0,0,0,0,0,0,
    0,0,0,0,0,0,0,'7','8','9','-','4','5','6','+','1',
    '2','3','0','.',0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};
*/

#define eitherALT   (keystatus[0x38]|keystatus[0xb8])
#define eitherCTRL  (keystatus[0x1d]|keystatus[0x9d])
#define eitherSHIFT (keystatus[0x2a]|keystatus[0x36])

#define DOWN_BK(BuildKey) (keystatus[buildkeys[BK_##BuildKey]])

int32_t pk_turnaccel=16;
int32_t pk_turndecel=12;
int32_t pk_uedaccel=3;

int8_t sideview_reversehrot = 0;

char lastpm16buf[156];

//static int32_t checksectorpointer_warn = 0;
static int32_t saveboard_savedtags=0;

char changechar(char dachar, int32_t dadir, char smooshyalign, char boundcheck);
static int32_t adjustmark(int32_t *xplc, int32_t *yplc, int16_t danumwalls);
static void locktogrid(int32_t *dax, int32_t *day);
static int32_t checkautoinsert(int32_t dax, int32_t day, int16_t danumwalls);
void keytimerstuff(void);
static int32_t clockdir(int16_t wallstart);
static void flipwalls(int16_t numwalls, int16_t newnumwalls);
static void insertpoint(int16_t linehighlight, int32_t dax, int32_t day);
static const char *deletepoint(int16_t point, int32_t runi);
static int32_t deletesector(int16_t sucksect);
void fixrepeats(int16_t i);
static int16_t loopinside(int32_t x, int32_t y, int16_t startwall);
int32_t fillsector(int16_t sectnum, int32_t fillcolor);  // fillcolor == -1: default (pulsating)
static int16_t whitelinescan(int16_t sucksect, int16_t dalinehighlight);
void printcoords16(int32_t posxe, int32_t posye, int16_t ange);
static void copysector(int16_t soursector, int16_t destsector, int16_t deststartwall, char copystat, const int16_t *oldtonewsect);
int32_t drawtilescreen(int32_t pictopleft, int32_t picbox);
void overheadeditor(void);
static int32_t getlinehighlight(int32_t xplc, int32_t yplc, int32_t line);
void fixspritesectors(void);
static int32_t movewalls(int32_t start, int32_t offs);
int32_t loadnames(const char *namesfile);
void updatenumsprites(void);
static void getclosestpointonwall(int32_t x, int32_t y, int32_t dawall, int32_t *nx, int32_t *ny,
                                  int32_t maybe_screen_coord_p);
static void initcrc(void);
int32_t gettile(int32_t tilenum);

static int32_t menuselect(void);
static int32_t menuselect_auto(int32_t); //PK

static int32_t insert_sprite_common(int32_t sucksect, int32_t dax, int32_t day);
static void correct_ornamented_sprite(int32_t i, int32_t hitw);

static int32_t getfilenames(const char *path, const char *kind);
static void clearfilenames(void);
void loadmhk(int32_t domessage);

void clearkeys(void) { Bmemset(keystatus,0,sizeof(keystatus)); }

#ifdef USE_OPENGL
static int32_t osdcmd_restartvid(const osdfuncparm_t *parm)
{
    UNREFERENCED_PARAMETER(parm);

    if (qsetmode != 200) return OSDCMD_OK;

    resetvideomode();
    if (setgamemode(fullscreen,xdim,ydim,bpp))
        OSD_Printf("restartvid: Reset failed...\n");

    return OSDCMD_OK;
}

static int32_t osdcmd_vidmode(const osdfuncparm_t *parm)
{
    int32_t newx = xdim, newy = ydim, newbpp = bpp, newfullscreen = fullscreen, tmp;

    switch (parm->numparms)
    {
    case 1:	// bpp switch
        tmp = Batol(parm->parms[0]);
        if (tmp==8 || tmp==16 || tmp==32)
            newbpp = tmp;
        break;
    case 4:	// fs, res, bpp switch
        newfullscreen = (Batol(parm->parms[3]) != 0);
    case 3:	// res & bpp switch
        tmp = Batol(parm->parms[2]);
        if (tmp==8 || tmp==16 || tmp==32)
            newbpp = tmp;
    case 2: // res switch
        newx = Batol(parm->parms[0]);
        newy = Batol(parm->parms[1]);
        break;
    default:
        return OSDCMD_SHOWHELP;
    }

    if (qsetmode != 200)
    {
        qsetmodeany(newx,newy);
        xdim2d = xdim;
        ydim2d = ydim;

        begindrawing();	//{{{
        CLEARLINES2D(0, ydim16, 0);
        enddrawing();	//}}}

        ydim16 = ydim-STATUS2DSIZ2;

        return OSDCMD_OK;
    }

    if (setgamemode(newfullscreen,newx,newy,newbpp))
        OSD_Printf("vidmode: Mode change failed!\n");

    xdimgame = newx;
    ydimgame = newy;
    bppgame = newbpp;
    fullscreen = newfullscreen;

    return OSDCMD_OK;
}
#endif


#ifdef M32_SHOWDEBUG
char m32_debugstr[64][128];
int32_t m32_numdebuglines=0;

static void M32_drawdebug(void)
{
    int i;
    int x=4, y=8;

#if 0
    {
        static char tstr[128];
        Bsprintf(tstr, "search... stat=%d, sector=%d, wall=%d (%d), isbottom=%d, asksave=%d",
                 searchstat, searchsector, searchwall, searchbottomwall, searchisbottom, asksave);
        printext256(x,y,whitecol,0,tstr,xdimgame>640?0:1);
    }
#endif
    if (m32_numdebuglines>0)
    {
        begindrawing();
        for (i=0; i<m32_numdebuglines && y<ydim-8; i++, y+=8)
            printext256(x,y,whitecol,0,m32_debugstr[i],xdimgame>640?0:1);
        enddrawing();
    }
    m32_numdebuglines=0;
}
#endif

#ifdef YAX_ENABLE
// Check whether bunchnum has exactly one corresponding floor and ceiling
// and return it in this case. If not 1-to-1, return -1.
int32_t yax_is121(int16_t bunchnum, int16_t getfloor)
{
    int32_t i;
    i = headsectbunch[0][bunchnum];
    if (i<0 || nextsectbunch[0][i]>=0)
        return -1;
    i = headsectbunch[1][bunchnum];
    if (i<0 || nextsectbunch[1][i]>=0)
        return -1;

    return headsectbunch[getfloor][bunchnum];
}

static int32_t yax_numsectsinbunch(int16_t bunchnum, int16_t cf)
{
    int32_t i, n=0;

    if (bunchnum<0 || bunchnum>=numyaxbunches)
        return -1;

    for (SECTORS_OF_BUNCH(bunchnum, cf, i))
        n++;

    return n;
}

static void yax_fixreverselinks(int16_t oldwall, int16_t newwall)
{
    int32_t cf, ynw;
    for (cf=0; cf<2; cf++)
    {
        ynw = yax_getnextwall(oldwall, cf);
        if (ynw >= 0)
            yax_setnextwall(ynw, !cf, newwall);
    }
}

static void yax_tweakwalls(int16_t start, int16_t offs)
{
    int32_t i, nw, cf;
    for (i=0; i<numwalls; i++)
        for (cf=0; cf<2; cf++)
        {
            nw = yax_getnextwall(i, cf);
            if (nw >= start)
                yax_setnextwall(i, cf, nw+offs);
        }
}

static void yax_resetbunchnums(void)
{
    int32_t i;

    for (i=0; i<MAXSECTORS; i++)
        yax_setbunches(i, -1, -1);
    yax_update(1);
    yax_updategrays(pos.z);
}

// Whether a wall is constrained by sector extensions.
// If false, it's a wall that you can freely move around,
// attach points to, etc...
static int32_t yax_islockedwall(int16_t sec, int16_t line)
#if 1
{
    UNREFERENCED_PARAMETER(sec);
    return !!(wall[line].cstat&YAX_NEXTWALLBITS);
}
#else
{
    int16_t cb,fb, cbn,fbn;
    int16_t ns = wall[line].nextsector;

    yax_getbunches(sec, &cb, &fb);

    if (ns < 0)
        return (cb>=0 || fb>=0);

    yax_getbunches(ns, &cbn, &fbn);

    return (cb!=cbn || fb!=fbn);
}
#endif

# define DEFAULT_YAX_HEIGHT (2048<<4)
#endif

static void reset_default_mapstate(void)
{
    pos.x = 32768;          //new board!
    pos.y = 32768;
    pos.z = 0;
    ang = 1536;
    cursectnum = -1;

    numsectors = 0;
    numwalls = 0;
    numsprites = 0;

    editorzrange[0] = INT32_MIN;
    editorzrange[1] = INT32_MAX;

    initspritelists();
    taglab_init();

#ifdef YAX_ENABLE
    yax_resetbunchnums();
#endif
}

int32_t app_main(int32_t argc, const char **argv)
{
    char quitflag, cmdsetup = 0;
    int32_t i, j, k;

    pathsearchmode = 1;		// unrestrict findfrompath so that full access to the filesystem can be had

#ifdef USE_OPENGL
    OSD_RegisterFunction("restartvid","restartvid: reinitialize the video mode",osdcmd_restartvid);
    OSD_RegisterFunction("vidmode","vidmode <xdim> <ydim> <bpp> <fullscreen>: immediately change the video mode",osdcmd_vidmode);
#endif

    wm_setapptitle("Mapster32");

    editstatus = 1;
    newaspect_enable = 1;

    if ((i = ExtPreInit(argc,argv)) < 0) return -1;

#ifdef RENDERTYPEWIN
    backgroundidle = 1;
#endif

    boardfilename[0] = 0;
    for (i=1; i<argc; i++)
    {
        if (argv[i][0] == '-')
        {
            if (!Bstrcmp(argv[i], "-setup")) cmdsetup = 1;
            else if (!Bstrcmp(argv[i], "-help") || !Bstrcmp(argv[i], "--help") || !Bstrcmp(argv[i], "-?"))
            {
                char *s =
                    "Mapster32\n"
                    "Syntax: mapster32 [options] mapname\n"
                    "Options:\n"
                    "\t-grp\tUse an extra GRP or ZIP file.\n"
                    "\t-g\tSame as above.\n"
#if defined RENDERTYPEWIN || (defined RENDERTYPESDL && !defined __APPLE__ && defined HAVE_GTK2)
                    "\t-setup\tDisplays the configuration dialogue box before entering the editor.\n"
#endif
                    ;
#if defined RENDERTYPEWIN || (defined RENDERTYPESDL && !defined __APPLE__ && defined HAVE_GTK2)
                wm_msgbox("Mapster32","%s",s);
#else
                puts(s);
#endif
                return 0;
            }
            continue;
        }
        if (!boardfilename[0])
        {
            Bstrncpy(boardfilename, argv[i], BMAX_PATH);
            boardfilename[i-BMAX_PATH] = 0;
        }
    }
    if (boardfilename[0] == 0)
    {
        Bstrcpy(boardfilename,"newboard.map");
    }
    else if (Bstrchr(boardfilename,'.') == 0)
    {
        Bstrcat(boardfilename, ".map");
    }
    //Bcanonicalisefilename(boardfilename,0);

    if ((i = ExtInit()) < 0) return -1;
#if defined RENDERTYPEWIN || (defined RENDERTYPESDL && !defined __APPLE__ && defined HAVE_GTK2)
    if (i || forcesetup || cmdsetup)
    {
        extern int32_t startwin_run(void);

        if (quitevent || !startwin_run())
        {
            uninitengine();
            exit(0);
        }
    }
#endif

    loadnames(g_namesFileName);

    if (initinput()) return -1;

    initmouse();

    inittimer(TIMERINTSPERSECOND);
    installusertimercallback(keytimerstuff);

    loadpics("tiles000.art", g_maxCacheSize);

#ifdef YAX_ENABLE
    // init dummy texture for YAX
    // must be after loadpics(), which inits BUILD's cache

    i = MAXTILES-1;
    if (tilesizx[i]==0 && tilesizy[i]==0)
    {
        static char R[8*16] = { //
            0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0,
            0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0,
            0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0,
            0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0,
            0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0,
            0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0,
            0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0,
        };

        char *newtile;
        int32_t sx=32, sy=32, col, j;

        walock[i] = 255; // permanent tile
        picsiz[i] = 5 + (5<<4);
        tilesizx[i] = sx; tilesizy[i] = sy;
        allocache(&waloff[i], sx*sy, &walock[i]);
        newtile = (char *)waloff[i];

        col = getclosestcol(128>>2, 128>>2, 0);
        for (j=0; j<(signed)sizeof(R); j++)
            R[j] *= col;

        Bmemset(newtile, 0, sx*sy);
        for (j=0; j<8; j++)
            Bmemcpy(&newtile[32*j], &R[16*j], 16);
    }
#endif

    Bstrcpy(kensig,"Uses BUILD technology by Ken Silverman");
    initcrc();

    if (!loaddefinitionsfile(g_defNamePtr))
        initprintf("Definitions file loaded.\n");

    k = 0;
    for (i=0; i<256; i++)
    {
        j = ((int32_t)palette[i*3])+((int32_t)palette[i*3+1])+((int32_t)palette[i*3+2]);
        if (j > k) { k = j; whitecol = i; }
    }

    k = clipmapinfo_load("_clipshape0.map");
    if (k>0)
        initprintf("There was an error loading the sprite clipping map (status %d).\n", k);

    taglab_init();

    mkonwinvalid();

    if (LoadBoard(boardfilename, 1))
        reset_default_mapstate();

    totalclock = 0;

    updatesector(pos.x,pos.y,&cursectnum);

    if (cursectnum == -1)
    {
        vid_gamma_3d = vid_gamma;
        vid_brightness_3d = vid_brightness;
        vid_contrast_3d = vid_contrast;

        vid_gamma = vid_contrast = 1.0;
        vid_brightness = 0.0;

        setbrightness(0,0,0);
        if (setgamemode(fullscreen, xdim2d, ydim2d, 8) < 0)
        {
            ExtUnInit();
            uninitengine();
            Bprintf("%d * %d not supported in this graphics mode\n",xdim2d,ydim2d);
            exit(0);
        }

        // executed once per init, but after setgamemode so that OSD has the right width
        OSD_Exec("m32_autoexec.cfg");

        overheadeditor();
        keystatus[buildkeys[BK_MODE2D_3D]] = 0;

        vid_gamma = vid_gamma_3d;
        vid_contrast = vid_contrast_3d;
        vid_brightness = vid_brightness_3d;

        vid_gamma_3d = vid_contrast_3d = vid_brightness_3d = -1;

        setbrightness(GAMMA_CALC,0,0);
    }
    else
    {
        if (setgamemode(fullscreen, xdimgame, ydimgame, bppgame) < 0)
        {
            ExtUnInit();
            uninitengine();
            Bprintf("%d * %d not supported in this graphics mode\n",xdim,ydim);
            exit(0);
        }

        // executed once per init, but after setgamemode so that OSD has the right width
        OSD_Exec("m32_autoexec.cfg");

        setbrightness(GAMMA_CALC,0,0);
    }

CANCEL:
    quitflag = 0;
    while (quitflag == 0)
    {
        if (handleevents())
        {
            if (quitevent)
            {
                keystatus[1] = 1;
                quitevent = 0;
            }
        }

        OSD_DispatchQueued();

        nextpage();
        synctics = totalclock-lockclock;
        lockclock += synctics;

        ExtPreCheckKeys();

        yax_preparedrawrooms();
        drawrooms(pos.x,pos.y,pos.z,ang,horiz,cursectnum);
        yax_drawrooms(ExtAnalyzeSprites, horiz, cursectnum);

        ExtAnalyzeSprites();
        drawmasks();

#ifdef POLYMER
        if (rendmode == 4 && searchit == 2)
        {
            polymer_editorpick();
            drawrooms(pos.x,pos.y,pos.z,ang,horiz,cursectnum);
            ExtAnalyzeSprites();
            drawmasks();
        }
#endif

#ifdef M32_SHOWDEBUG
        if (searchstat>=0 && (searchwall<0 || searchsector<0))
        {
            if (m32_numdebuglines<64)
                Bsprintf(m32_debugstr[m32_numdebuglines++], "inconsistent search variables!");
            searchstat = -1;
        }

        M32_drawdebug();
#endif
        ExtCheckKeys();


        if (keystatus[1])
        {
            keystatus[1] = 0;

            printext256(0,0,whitecol,0,"Are you sure you want to quit?",0);

            showframe(1);
            synctics = totalclock-lockclock;
            lockclock += synctics;

            while ((keystatus[1]|keystatus[0x1c]|keystatus[0x39]|keystatus[0x31]) == 0)
            {
                idle_waitevent();
                if (handleevents())
                {
                    if (quitevent)
                    {
                        quitflag = 1;
                        break;
                    }
                }

                if (keystatus[0x15]||keystatus[0x1c]) // Y or ENTER
                {
                    keystatus[0x15] = 0;
                    keystatus[0x1c] = 0;
                    quitflag = 1; break;
                }
            }
            while (keystatus[1])
            {
                keystatus[1] = 0;
                quitevent = 0;
                goto CANCEL;
            }
        }
    }

    if (asksave)
    {
        i = CheckMapCorruption(4, 0);

        printext256(0,8,whitecol,0,i<4?"Save changes?":"Map is heavily corrupt. Save changes?",0);
        showframe(1);

        while ((keystatus[1]|keystatus[0x1c]|keystatus[0x39]|keystatus[0x31]|keystatus[0x2e]) == 0)
        {
            idle_waitevent();
            if (handleevents()) { if (quitevent) break;	} // like saying no

            if (keystatus[0x15] || keystatus[0x1c]) // Y or ENTER
            {
                keystatus[0x15] = keystatus[0x1c] = 0;

                SaveBoard(NULL, 0);

                break;
            }
        }
        while (keystatus[1]||keystatus[0x2e])
        {
            keystatus[1] = keystatus[0x2e] = 0;
            quitevent = 0;
            goto CANCEL;
        }
    }


//    clearfilenames();
    ExtUnInit();
//    uninitengine();
    uninitsystem();

// not reached...
//    Bprintf("Memory status: %d(%d) bytes\n",cachesize,artsize);
//    Bprintf("%s\n",kensig);
    return(0);
}

/*
void showmouse(void)
{
    int32_t i;

    for (i=1;i<=4;i++)
    {
        plotpixel(searchx+i,searchy,whitecol);
        plotpixel(searchx-i,searchy,whitecol);
        plotpixel(searchx,searchy-i,whitecol);
        plotpixel(searchx,searchy+i,whitecol);
    }
}
*/

static int32_t mhk=0;
void loadmhk(int32_t domessage)
{
    char *p; char levname[BMAX_PATH];

    if (!mhk)
        return;

    Bstrcpy(levname, boardfilename);
    p = Bstrrchr(levname,'.');
    if (!p)
        Bstrcat(levname,".mhk");
    else
    {
        p[1]='m';
        p[2]='h';
        p[3]='k';
        p[4]=0;
    }

    if (!loadmaphack(levname))
    {
        if (domessage)
            message("Loaded map hack file '%s'",levname);
        else
            initprintf("Loaded map hack file '%s'\n",levname);
    }
    else
    {
        mhk=2;
        if (domessage)
            message("No maphack found for map '%s'",boardfilename);
    }
}

// this is spriteon{ceiling,ground}z from astub.c packed into
// one convenient function
void spriteoncfz(int32_t i, int32_t *czptr, int32_t *fzptr)
{
    int32_t cz,fz, height, zofs;

    getzsofslope(sprite[i].sectnum, sprite[i].x,sprite[i].y, &cz, &fz);
    spriteheightofs(i, &height, &zofs);

    if (czptr)
        *czptr = cz + height - zofs;
    if (fzptr)
        *fzptr = fz - zofs;
}

static void move_and_update(int32_t xvect, int32_t yvect, int32_t addshr)
{
    if (noclip)
    {
        pos.x += xvect>>(14+addshr);
        pos.y += yvect>>(14+addshr);
        updatesector(pos.x,pos.y, &cursectnum);
    }
    else
        clipmove(&pos,&cursectnum, xvect>>addshr,yvect>>addshr,
                 128,4<<8,4<<8, CLIPMASK0);
}

static void mainloop_move(void)
{
    int32_t xvect, yvect, doubvel;

    if (angvel != 0)  //ang += angvel * constant
    {
        //ENGINE calculates angvel for you

        //Lt. shift makes turn velocity 50% faster
        doubvel = (synctics + DOWN_BK(RUN)*(synctics>>1));

        ang += ((angvel*doubvel)>>4);
        ang &= 2047;
    }
    if ((vel|svel) != 0)
    {
        //Lt. shift doubles forward velocity
        doubvel = (1+(DOWN_BK(RUN)))*synctics;

        xvect = 0;
        yvect = 0;

        if (vel != 0)
        {
            xvect += (vel*doubvel*(int32_t)sintable[(ang+2560)&2047])>>3;
            yvect += (vel*doubvel*(int32_t)sintable[(ang+2048)&2047])>>3;
        }
        if (svel != 0)
        {
            xvect += (svel*doubvel*(int32_t)sintable[(ang+2048)&2047])>>3;
            yvect += (svel*doubvel*(int32_t)sintable[(ang+1536)&2047])>>3;
        }

        move_and_update(xvect, yvect, 0);
    }
}

void editinput(void)
{
    int32_t mousz, bstatus;
    int32_t i, j, k, tempint=0;
    int32_t goalz, xvect, yvect, hiz, loz, oposz;
    int32_t dax, day, hihit, lohit, omlook=mlook;

// 3B  3C  3D  3E   3F  40  41  42   43  44  57  58          46
// F1  F2  F3  F4   F5  F6  F7  F8   F9 F10 F11 F12        SCROLL

    mousz = 0;
    getmousevalues(&mousx,&mousy,&bstatus);
    mousx = (mousx<<16) + mousexsurp;
    mousy = (mousy<<16) + mouseysurp;

    if (unrealedlook && !mskip)
    {
        if (mlook==0 && (bstatus&(1|2|4))==2)
            mlook = 3;
        else if ((bstatus&(1|2|4))==1)
            mlook = 3;
    }

    {
        ldiv_t ld;
        if (mlook)
        {
            ld = ldiv(mousx, (int32_t)((1<<16)/(msens*0.5f))); mousx = ld.quot; mousexsurp = ld.rem;
            ld = ldiv(mousy, (int32_t)((1<<16)/(msens*0.25f))); mousy = ld.quot; mouseysurp = ld.rem;
        }
        else
        {
            ld = ldiv(mousx, (int32_t)((1<<16)/msens)); mousx = ld.quot; mousexsurp = ld.rem;
            ld = ldiv(mousy, (int32_t)((1<<16)/msens)); mousy = ld.quot; mouseysurp = ld.rem;
        }
    }

    if (mlook == 3)
        mlook = omlook;

    // UnrealEd:
    // rmb: mouselook
    // lbm: x:turn y:fwd/back local x
    // lmb&rmb: x:strafe y:up/dn (move in local yz plane)
    // mmb: fwd/back in viewing vector

    if (unrealedlook && !mskip)    //PK
    {
        if ((bstatus&(1|2|4))==1)
        {
            ang += mousx;
            xvect = -((mousy*(int32_t)sintable[(ang+2560)&2047])<<(3+pk_uedaccel));
            yvect = -((mousy*(int32_t)sintable[(ang+2048)&2047])<<(3+pk_uedaccel));

            move_and_update(xvect, yvect, 0);
        }
        else if (!mlook && (bstatus&(1|2|4))==2)
        {
            mlook=2;
        }
        else if ((bstatus&(1|2|4))==(1|2))
        {
            zmode = 2;
            xvect = -((mousx*(int32_t)sintable[(ang+2048)&2047])<<pk_uedaccel);
            yvect = -((mousx*(int32_t)sintable[(ang+1536)&2047])<<pk_uedaccel);
            pos.z += mousy<<(4+pk_uedaccel);

            move_and_update(xvect, yvect, 0);
        }
        else if ((bstatus&(1|2|4))==4)
        {
            zmode = 2;

            // horiz-100 of 200 is viewing at 326.4 build angle units (=atan(200/128)) upward
            tempint = getangle(128, horiz-100);

            xvect = -((mousy*
                       ((int32_t)sintable[(ang+2560)&2047]>>6)*
                       ((int32_t)sintable[(tempint+512)&2047])>>6)
                      <<pk_uedaccel);
            yvect = -((mousy*
                       ((int32_t)sintable[(ang+2048)&2047]>>6)*
                       ((int32_t)sintable[(tempint+512)&2047])>>6)
                      <<pk_uedaccel);

            pos.z += mousy*(((int32_t)sintable[(tempint+2048)&2047])>>(10-pk_uedaccel));

            move_and_update(xvect, yvect, 2);
        }
    }

    if (mskip)
    {
        // mskip was set in astub.c to not trigger UEd mouse movements.
        // Reset now.
        mskip = 0;
    }
    else
    {
        if (mlook && (unrealedlook==0 || (bstatus&(1|4))==0))
        {
            ang += mousx;
            horiz -= mousy;

            /*
            if (mousy && !(mousy/4))
                horiz--;
            if (mousx && !(mousx/2))
                ang++;
            */

            inpclamp(&horiz, -99, 299);

            if (mlook == 1)
            {
                searchx = xdim>>1;
                searchy = ydim>>1;
            }
            osearchx = searchx-mousx;
            osearchy = searchy-mousy;
        }
        else if (unrealedlook==0 || (bstatus&(1|2|4))==0)
        {
            osearchx = searchx;
            osearchy = searchy;
            searchx += mousx;
            searchy += mousy;

            inpclamp(&searchx, 12, xdim-13);
            inpclamp(&searchy, 12, ydim-13);
        }
    }

//    showmouse();

    if (keystatus[0x43])  // F9
    {
        if (mhk)
        {
            Bmemset(spriteext, 0, sizeof(spriteext_t) * MAXSPRITES);
            Bmemset(spritesmooth, 0, sizeof(spritesmooth_t) * (MAXSPRITES+MAXUNIQHUDID));
            delete_maphack_lights();
            mhk = 0;
            message("Maphacks disabled");
        }
        else
        {
            mhk = 1;
            loadmhk(1);
        }

        keystatus[0x43] = 0;
    }

    mainloop_move();

    getzrange(&pos,cursectnum, &hiz,&hihit, &loz,&lohit, 128,CLIPMASK0);
/*
{
    int32_t his = !(hihit&32768), los = !(lohit&32768);
    if (m32_numdebuglines<64)
        Bsprintf(m32_debugstr[m32_numdebuglines++], "s%d: cf[%s%d, %s%d] z(%d, %d)", cursectnum,
                 his?"s":"w",hihit&16383, los?"s":"w",lohit&16383, hiz,loz);
}
*/
    oposz = pos.z;
    if (zmode == 0)
    {
        goalz = loz-(kensplayerheight<<8);  //playerheight pixels above floor
        if (goalz < hiz+(16<<8))  //ceiling&floor too close
            goalz = (loz+hiz)>>1;
        goalz += mousz;

        if (DOWN_BK(MOVEUP))  //A (stand high)
        {
            goalz -= (16<<8);
            if (DOWN_BK(RUN))
                goalz -= (24<<8);   
        }
        if (DOWN_BK(MOVEDOWN))  //Z (stand low)
        {
            goalz += (12<<8);
            if (DOWN_BK(RUN))
                goalz += (12<<8);
        }

        if (goalz != pos.z)
        {
            if (pos.z < goalz) hvel += 64;
            if (pos.z > goalz) hvel = ((goalz-pos.z)>>3);

            pos.z += hvel;
            if (pos.z > loz-(4<<8)) pos.z = loz-(4<<8), hvel = 0;
            if (pos.z < hiz+(4<<8)) pos.z = hiz+(4<<8), hvel = 0;
        }
    }
    else
    {
        goalz = pos.z;
        if (DOWN_BK(MOVEUP))  //A
        {
            if (eitherCTRL)
            {
                horiz = max(-100,horiz-((DOWN_BK(RUN)+1)*synctics*2));
            }
            else
            {
                if (zmode != 1)
                    goalz -= (8<<8);
                else
                {
                    zlock += (4<<8);
                    DOWN_BK(MOVEUP) = 0;
                }
            }
        }
        if (DOWN_BK(MOVEDOWN))  //Z (stand low)
        {
            if (eitherCTRL)
            {
                horiz = min(300,horiz+((DOWN_BK(RUN)+1)*synctics*2));
            }
            else
            {
                if (zmode != 1)
                    goalz += (8<<8);
                else if (zlock > 0)
                {
                    zlock -= (4<<8);
                    DOWN_BK(MOVEDOWN) = 0;
                }
            }
        }

        if (!noclip)
            inpclamp(&goalz, hiz+(4<<8), loz-(4<<8));

        if (zmode == 1) goalz = loz-zlock;
        if (!noclip && (goalz < hiz+(4<<8)))
            goalz = ((loz+hiz)>>1);  //ceiling&floor too close
        if (zmode == 1) pos.z = goalz;

        if (goalz != pos.z)
        {
            //if (pos.z < goalz) hvel += (32<<DOWN_BK(RUN));
            //if (pos.z > goalz) hvel -= (32<<DOWN_BK(RUN));
            if (pos.z < goalz)
                hvel = ((192*synctics)<<DOWN_BK(RUN));
            else
                hvel = -((192*synctics)<<DOWN_BK(RUN));

            pos.z += hvel;

            if (!noclip)
            {
                if (pos.z > loz-(4<<8)) pos.z = loz-(4<<8), hvel = 0;
                if (pos.z < hiz+(4<<8)) pos.z = hiz+(4<<8), hvel = 0;
            }
        }
        else
            hvel = 0;
    }

    {
        int16_t ocursectnum = cursectnum;
        updatesectorz(pos.x,pos.y,pos.z, &cursectnum);
        if (cursectnum<0)
        {
            if (zmode != 2)
                pos.z = oposz;  // don't allow to fall into infinity when in void space
            cursectnum = ocursectnum;
        }
    }

    searchit = 2;
    if (searchstat >= 0)
    {
        if ((bstatus&(1|2|4)) || keystatus[0x39])  // SPACE
            searchit = 0;

        if (keystatus[0x1f])  //S (insert sprite) (3D)
        {
            hitdata_t hitinfo;

            dax = 16384;
            day = divscale14(searchx-(xdim>>1), xdim>>1);
            rotatepoint(0,0, dax,day, ang, &dax,&day);

            hitscan((const vec3_t *)&pos,cursectnum,              //Start position
                    dax,day,(scale(searchy,200,ydim)-horiz)*2000, //vector of 3D ang
                    &hitinfo,CLIPMASK1);

            if (hitinfo.hitsect >= 0)
            {
                dax = hitinfo.pos.x;
                day = hitinfo.pos.y;
                if (gridlock && grid > 0)
                {
                    if (AIMING_AT_WALL || AIMING_AT_MASKWALL)
                        hitinfo.pos.z &= 0xfffffc00;
                    else
                        locktogrid(&dax, &day);
                }

                i = insert_sprite_common(hitinfo.hitsect, dax, day);

                if (i < 0)
                    message("Couldn't insert sprite.");
                else
                {
                    int32_t cz, fz;

                    if (somethingintab == 3)
                    {
                        sprite[i].picnum = temppicnum;
                        if (tilesizx[temppicnum] <= 0 || tilesizy[temppicnum] <= 0)
                        {
                            j = 0;
                            for (k=0; k<MAXTILES; k++)
                                if (tilesizx[k] > 0 && tilesizy[k] > 0)
                                {
                                    j = k;
                                    break;
                                }
                            sprite[i].picnum = j;
                        }

                        sprite[i].shade = tempshade;
                        sprite[i].pal = temppal;
                        sprite[i].xrepeat = max(tempxrepeat, 1);
                        sprite[i].yrepeat = max(tempyrepeat, 1);
                        sprite[i].cstat = tempcstat;
                    }

                    getzsofslope(hitinfo.hitsect, hitinfo.pos.x, hitinfo.pos.y, &cz, &fz);

                    spriteoncfz(i, &cz, &fz);
                    sprite[i].z = clamp(hitinfo.pos.z, cz, fz);

                    if (AIMING_AT_WALL || AIMING_AT_MASKWALL)
                    {
                        sprite[i].cstat &= ~48;
                        sprite[i].cstat |= (16+64);

                        correct_ornamented_sprite(i, hitinfo.hitwall);
                    }
                    else
                        sprite[i].cstat |= (tilesizy[sprite[i].picnum]>=32);

                    correct_sprite_yoffset(i);

                    updatenumsprites();
                    asksave = 1;

                    VM_OnEvent(EVENT_INSERTSPRITE3D, i);
                }
            }

            keystatus[0x1f] = 0;
        }

        if (keystatus[0x3f]||keystatus[0x40])  //F5,F6
        {
            switch (searchstat)
            {
            case SEARCH_CEILING:
            case SEARCH_FLOOR:
                ExtShowSectorData(searchsector); break;
            case SEARCH_WALL:
            case SEARCH_MASKWALL:
                ExtShowWallData(searchwall); break;
            case SEARCH_SPRITE:
                ExtShowSpriteData(searchwall); break;
            }

            keystatus[0x3f] = keystatus[0x40] = 0;
        }
        if (keystatus[0x41]||keystatus[0x42])  //F7,F8
        {
            switch (searchstat)
            {
            case SEARCH_CEILING:
            case SEARCH_FLOOR:
                ExtEditSectorData(searchsector); break;
            case SEARCH_WALL:
            case SEARCH_MASKWALL:
                ExtEditWallData(searchwall); break;
            case SEARCH_SPRITE:
                ExtEditSpriteData(searchwall); break;
            }

            keystatus[0x41] = keystatus[0x42] = 0;
        }

    }

    if (keystatus[buildkeys[BK_MODE2D_3D]])  // Enter
    {

        vid_gamma_3d = vid_gamma;
        vid_contrast_3d = vid_contrast;
        vid_brightness_3d = vid_brightness;

        vid_gamma = vid_contrast = 1.0;
        vid_brightness = 0.0;

        setbrightness(0,0,0);

        keystatus[buildkeys[BK_MODE2D_3D]] = 0;
        overheadeditor();
        keystatus[buildkeys[BK_MODE2D_3D]] = 0;

        vid_gamma = vid_gamma_3d;
        vid_contrast = vid_contrast_3d;
        vid_brightness = vid_brightness_3d;

        vid_gamma_3d = vid_contrast_3d = vid_brightness_3d = -1;

        setbrightness(GAMMA_CALC,0,0);
    }
}

char changechar(char dachar, int32_t dadir, char smooshyalign, char boundcheck)
{
    if (dadir < 0)
    {
        if ((dachar > 0) || (boundcheck == 0))
        {
            dachar--;
            if (smooshyalign > 0)
                dachar = (dachar&0xf8);
        }
    }
    else if (dadir > 0)
    {
        if ((dachar < 255) || (boundcheck == 0))
        {
            dachar++;
            if (smooshyalign > 0)
            {
                if (dachar >= 256-8) dachar = 255;
                else dachar = ((dachar+7)&0xf8);
            }
        }
    }
    return(dachar);
}


////////////////////// OVERHEADEDITOR //////////////////////

int32_t inside_editor_curpos(int16_t sectnum)
{
    // TODO: take care: mous[xy]plc global vs overheadeditor auto
    return inside_editor(&pos, searchx,searchy, zoom, mousxplc,mousyplc, sectnum);
}

int32_t inside_editor(const vec3_t *pos, int32_t searchx, int32_t searchy, int32_t zoom,
                      int32_t x, int32_t y, int16_t sectnum)
{
    if (!m32_sideview)
        return inside(x, y, sectnum);

    // if in side-view mode, use the screen coords instead
    {
        int32_t dst = MAXSECTORS+M32_FIXME_SECTORS-1, i, oi;
        int32_t srcw=sector[sectnum].wallptr, dstw=MAXWALLS;
        int32_t ret;

        if (sector[sectnum].wallnum > M32_FIXME_WALLS)
            return -1;

        Bmemcpy(&sector[dst], &sector[sectnum], sizeof(sectortype));
        sector[dst].wallptr = dstw;

        Bmemcpy(&wall[dstw], &wall[srcw], sector[dst].wallnum*sizeof(walltype));
        for (i=dstw, oi=srcw; i<dstw+sector[dst].wallnum; i++, oi++)
        {
            wall[i].point2 += dstw-srcw;

            screencoords(&wall[i].x, &wall[i].y, wall[i].x-pos->x, wall[i].y-pos->y, zoom);
            wall[i].y += getscreenvdisp(getflorzofslope(sectnum,wall[oi].x,wall[oi].y)-pos->z, zoom);
            wall[i].x += halfxdim16;
            wall[i].y += midydim16;
        }

        i = numsectors;
        numsectors = dst+1;
        ret = inside(searchx, searchy, dst);
        numsectors = i;
        return ret;
    }
}

static inline void drawline16base(int32_t bx, int32_t by, int32_t x1, int32_t y1, int32_t x2, int32_t y2, char col)
{
    drawline16(bx+x1, by+y1, bx+x2, by+y2, col);
}

void drawsmallabel(const char *text, char col, char backcol, int32_t dax, int32_t day, int32_t daz)
{
    int32_t x1, y1, x2, y2;

    screencoords(&dax,&day, dax-pos.x,day-pos.y, zoom);
    if (m32_sideview)
        day += getscreenvdisp(daz-pos.z, zoom);

    x1 = halfxdim16+dax-(Bstrlen(text)<<1);
    y1 = midydim16+day-4;
    x2 = x1 + (Bstrlen(text)<<2)+2;
    y2 = y1 + 7;

    if ((x1 > 3) && (x2 < xdim) && (y1 > 1) && (y2 < ydim16))
    {
        printext16(x1,y1, col,backcol, text,1);
        drawline16(x1-1,y1-1, x2-3,y1-1, backcol);
        drawline16(x1-1,y2+1, x2-3,y2+1, backcol);

        drawline16(x1-2,y1, x1-2,y2, backcol);
        drawline16(x2-2,y1, x2-2,y2, backcol);
        drawline16(x2-3,y1, x2-3,y2, backcol);
    }
}

// backup highlighted sectors with sprites as mapinfo for later restoration
// return values:
//  -1: highlightsectorcnt<=0
//  -2: out of mem
//   0: ok
static int32_t backup_highlighted_map(mapinfofull_t *mapinfo)
{
    int32_t i, j, k, m, tmpnumwalls=0, tmpnumsprites=0;

    if (highlightsectorcnt <= 0)
        return -1;

    // count walls & sprites
    for (i=0; i<highlightsectorcnt; i++)
    {
        tmpnumwalls += sector[highlightsector[i]].wallnum;

        m = headspritesect[highlightsector[i]];
        while (m != -1)
        {
            tmpnumsprites++;
            m = nextspritesect[m];
        }
    }

    // allocate temp storage
    mapinfo->sector = Bmalloc(highlightsectorcnt * sizeof(sectortype));
    if (!mapinfo->sector) return -2;
    mapinfo->wall = Bmalloc(tmpnumwalls * sizeof(walltype));
    if (!mapinfo->wall) { Bfree(mapinfo->sector); return -2; }
    if (tmpnumsprites>0)
    {
        mapinfo->sprite = Bmalloc(tmpnumsprites * sizeof(spritetype));
        if (!mapinfo->sprite)
        {
            Bfree(mapinfo->sector);
            Bfree(mapinfo->wall);
            return -2;
        }
    }


    // copy everything over
    tmpnumwalls = 0;
    tmpnumsprites = 0;
    for (i=0; i<highlightsectorcnt; i++)
    {
        k = highlightsector[i];
        Bmemcpy(&mapinfo->sector[i], &sector[k], sizeof(sectortype));
        mapinfo->sector[i].wallptr = tmpnumwalls;

        for (j=0; j<sector[k].wallnum; j++)
        {
            Bmemcpy(&mapinfo->wall[tmpnumwalls+j], &wall[sector[k].wallptr+j], sizeof(walltype));
            mapinfo->wall[tmpnumwalls+j].point2 += (tmpnumwalls-sector[k].wallptr);
            mapinfo->wall[tmpnumwalls+j].nextsector = -1;
            mapinfo->wall[tmpnumwalls+j].nextwall = -1;
        }
        tmpnumwalls += j;

        m = headspritesect[highlightsector[i]];
        while (m != -1)
        {
            Bmemcpy(&mapinfo->sprite[tmpnumsprites], &sprite[m], sizeof(spritetype));
            mapinfo->sprite[tmpnumsprites].sectnum = i;
            m = nextspritesect[m];
            tmpnumsprites++;
        }
    }


    mapinfo->numsectors = highlightsectorcnt;
    mapinfo->numwalls = tmpnumwalls;
    mapinfo->numsprites = tmpnumsprites;

    return 0;
}

static void mapinfofull_free(mapinfofull_t *mapinfo)
{
    Bfree(mapinfo->sector);
    Bfree(mapinfo->wall);
    if (mapinfo->numsprites>0)
        Bfree(mapinfo->sprite);
}

// restore map saved with backup_highlighted_map, also
// frees mapinfo's sector, wall, (sprite) in any case.
// return values:
//  -1: limits exceeded
//   0: ok
static int32_t restore_highlighted_map(mapinfofull_t *mapinfo)
{
    int32_t i, j, sect, onumsectors=numsectors, newnumsectors, newnumwalls;

    updatenumsprites();
    if (numsectors+mapinfo->numsectors>MAXSECTORS || numwalls+mapinfo->numwalls>MAXWALLS
            || numsprites+mapinfo->numsprites>MAXSPRITES)
    {
        mapinfofull_free(mapinfo);
        return -1;
    }

    newnumsectors = numsectors + mapinfo->numsectors;
    newnumwalls = numwalls + mapinfo->numwalls;

    // copy sectors & walls
    Bmemcpy(&sector[numsectors], mapinfo->sector, mapinfo->numsectors*sizeof(sectortype));
    Bmemcpy(&wall[numwalls], mapinfo->wall, mapinfo->numwalls*sizeof(walltype));

    // tweak index members
    for (i=numsectors; i<newnumsectors; i++)
        sector[i].wallptr += numwalls;
    for (i=numwalls; i<newnumwalls; i++)
    {
        wall[i].point2 += numwalls;
        wall[i].x += BXY_MAX*2;
    }

    // reconstruct wall connections
    numsectors=newnumsectors;  // needed now for checksectorpointer
//    checksectorpointer_warn = 0;
    Bmemset(hlsectorbitmap, 0, sizeof(hlsectorbitmap));
    for (i=onumsectors; i<newnumsectors; i++)
    {
        for (j=sector[i].wallptr; j<sector[i].wallptr+sector[i].wallnum; j++)
            checksectorpointer(j, i);
        hlsectorbitmap[i>>3] |= (1<<(i&7));
    }
    for (i=numwalls; i<newnumwalls; i++)
        wall[i].x -= BXY_MAX*2;
//    checksectorpointer_warn = 1;

    // insert sprites
    for (i=0; i<mapinfo->numsprites; i++)
    {
        sect = onumsectors+mapinfo->sprite[i].sectnum;
        j = insertsprite(sect, mapinfo->sprite[i].statnum);
        Bmemcpy(&sprite[j], &mapinfo->sprite[i], sizeof(spritetype));
        sprite[j].sectnum = sect;
    }

    numwalls = newnumwalls;
    updatenumsprites();

    update_highlightsector();

    mapinfofull_free(mapinfo);
    return 0;
}


static int32_t newnumwalls=-1;

static void ovh_whiteoutgrab(void)
{
    int32_t i, j, k, startwall, endwall;
#if 0
//def YAX_ENABLE
    int16_t cb, fb;
#endif

    for (i=0; i<MAXWALLS; i++)
        onextwall[i] = -1;

    //White out all bordering lines of grab that are
    //not highlighted on both sides
    for (i=highlightsectorcnt-1; i>=0; i--)
        for (WALLS_OF_SECTOR(highlightsector[i], j))
        {
            if (wall[j].nextwall < 0)
                continue;

            k = wall[j].nextsector;

            if (hlsectorbitmap[k>>3]&(1<<(k&7)))
                continue;
#if 0
//def YAX_ENABLE
            // internal red walls are kept red
            yax_getbunches(highlightsector[i], &cb, &fb);
            if (cb>=0 && yax_getbunch(k, YAX_CEILING)>=0)
                continue;
            if (fb>=0 && yax_getbunch(k, YAX_FLOOR)>=0)
                continue;
#endif
            onextwall[j] = wall[j].nextwall;

            NEXTWALL(j).nextwall = -1;
            NEXTWALL(j).nextsector = -1;
            wall[j].nextwall = -1;
            wall[j].nextsector = -1;
        }

    mkonwvalid();
}

static void duplicate_selected_sectors(void)
{
    int32_t i, j, startwall, endwall, newnumsectors, newwalls = 0;
    int32_t minx=INT32_MAX, maxx=INT32_MIN, miny=INT32_MAX, maxy=INT32_MIN, dx, dy;
#ifdef YAX_ENABLE
    int16_t cb, fb, hadextended=0;
#endif

    for (i=0; i<highlightsectorcnt; i++)
        newwalls += sector[highlightsector[i]].wallnum;

    if (highlightsectorcnt + numsectors <= MAXSECTORS && numwalls+newwalls <= MAXWALLS)
    {
        int16_t *oldtonewsect = Bmalloc(numsectors * sizeof(int16_t));

        if (oldtonewsect)
        {
            for (i=0; i<numsectors; i++)
                oldtonewsect[i] = -1;
            for (i=0; i<highlightsectorcnt; i++)
                oldtonewsect[highlightsector[i]] = numsectors+i;
        }
        else
            message("warning: out of memory!");

        newnumsectors = numsectors;
        newnumwalls = numwalls;
        for (i=0; i<highlightsectorcnt; i++)
        {
            copysector(highlightsector[i], newnumsectors, newnumwalls, 1, oldtonewsect);
#ifdef YAX_ENABLE
            yax_getbunches(highlightsector[i], &cb, &fb);
            if (cb>=0 || fb>=0)
            {
                hadextended = 1;
                yax_setbunches(newnumsectors, -1, -1);
                for (WALLS_OF_SECTOR(newnumsectors, j))
                {
                    yax_setnextwall(j, 0, -1);
                    yax_setnextwall(j, 1, -1);
                }
            }
#endif
            newnumsectors++;
            newnumwalls += sector[highlightsector[i]].wallnum;
        }

        if (oldtonewsect)
            Bfree(oldtonewsect);

        Bmemset(hlsectorbitmap, 0, sizeof(hlsectorbitmap));
        for (i=0; i<highlightsectorcnt; i++)
        {
            // first, make red lines of old selected sectors, effectively
            // restoring the original state
            for (WALLS_OF_SECTOR(highlightsector[i], j))
            {
                if (wall[j].nextwall >= 0)
                    checksectorpointer(wall[j].nextwall,wall[j].nextsector);
                checksectorpointer(j, highlightsector[i]);

                minx = min(minx, wall[j].x);
                maxx = max(maxx, wall[j].x);
                miny = min(miny, wall[j].y);
                maxy = max(maxy, wall[j].y);
            }

            // Then, highlight the ones just copied.
            // These will have all walls whited out.
            j = numsectors + i;
            hlsectorbitmap[j>>3] |= (1<<(j&7));
        }

        // displace walls of new sectors by a small amount
        if (grid>0 && grid<9)
            dx = max(2048>>grid, 128);
        else
            dx = 512;
        dy = -dx;
        if (maxx+dx >= editorgridextent) dx*=-1;
        if (minx+dx <= -editorgridextent) dx*=-1;
        if (maxy+dy >= editorgridextent) dy*=-1;
        if (miny+dy <= -editorgridextent) dy*=-1;

        for (i=numsectors; i<newnumsectors; i++)
        {
            for (j=sector[i].wallptr; j<sector[i].wallptr+sector[i].wallnum; j++)
            {
                wall[j].x += dx;
                wall[j].y += dy;
            }

            for (j=headspritesect[i]; j>=0; j=nextspritesect[j])
            {
                sprite[j].x += dx;
                sprite[j].y += dy;
            }
        }

        numsectors = newnumsectors;
        numwalls = newnumwalls;

        update_highlightsector();  // must be after numsectors = newnumsectors

        newnumwalls = -1;
        newnumsectors = -1;

        updatenumsprites();
#ifdef YAX_ENABLE
        if (hadextended)
            printmessage16("Sectors duplicated and stamped, clearing extensions.");
        else
#endif
            printmessage16("Sectors duplicated and stamped.");
        asksave = 1;
    }
    else
    {
        printmessage16("Copying sectors would exceed sector or wall limit.");
    }
}

static void duplicate_selected_sprites(void)
{
    int32_t i, j, k=0;

    for (i=0; i<highlightcnt; i++)
        k += ((highlight[i]&0xc000) == 16384);

    if (highlightcnt + k <= MAXSPRITES)
    {
        for (i=0; i<highlightcnt; i++)
            if ((highlight[i]&0xc000) == 16384)
            {
                //duplicate sprite
                k = (highlight[i]&16383);
                j = insertsprite(sprite[k].sectnum,sprite[k].statnum);
                Bmemcpy(&sprite[j],&sprite[k],sizeof(spritetype));
//                sprite[j].sectnum = sprite[k].sectnum;   //Don't let memcpy overwrite sector!
//                setsprite(j,(vec3_t *)&sprite[j]);
            }
        updatenumsprites();
        printmessage16("Sprites duplicated and stamped.");
        asksave = 1;
    }
    else
    {
        printmessage16("Copying sprites would exceed sprite limit.");
    }
}

static void correct_ornamented_sprite(int32_t i, int32_t hitw)
{
    int32_t j;

    if (hitw >= 0)
        sprite[i].ang = (getangle(POINT2(hitw).x-wall[hitw].x,
                                  POINT2(hitw).y-wall[hitw].y)+512)&2047;

    //Make sure sprite's in right sector
    if (inside(sprite[i].x, sprite[i].y, sprite[i].sectnum) == 0)
    {
        j = wall[hitw].point2;
        sprite[i].x -= ksgn(wall[j].y-wall[hitw].y);
        sprite[i].y += ksgn(wall[j].x-wall[hitw].x);
    }
}

void DoSpriteOrnament(int32_t i)
{
    hitdata_t hitinfo;

    hitscan((const vec3_t *)&sprite[i],sprite[i].sectnum,
            sintable[(sprite[i].ang+1536)&2047],
            sintable[(sprite[i].ang+1024)&2047],
            0,
            &hitinfo,CLIPMASK1);

    sprite[i].x = hitinfo.pos.x;
    sprite[i].y = hitinfo.pos.y;
    sprite[i].z = hitinfo.pos.z;
    changespritesect(i, hitinfo.hitsect);

    correct_ornamented_sprite(i, hitinfo.hitwall);
}

void update_highlight(void)
{
    int32_t i;

    highlightcnt = 0;
    for (i=0; i<numwalls; i++)
        if (show2dwall[i>>3]&(1<<(i&7)))
            highlight[highlightcnt++] = i;
    for (i=0; i<MAXSPRITES; i++)
        if (sprite[i].statnum < MAXSTATUS)
        {
            if (show2dsprite[i>>3]&(1<<(i&7)))
                highlight[highlightcnt++] = i+16384;
        }
        else
            show2dsprite[i>>3] &= ~(1<<(i&7));

    if (highlightcnt == 0)
        highlightcnt = -1;
}

void update_highlightsector(void)
{
    int32_t i;

    minhlsectorfloorz = INT32_MAX;

    highlightsectorcnt = 0;
    for (i=0; i<numsectors; i++)
        if (hlsectorbitmap[i>>3]&(1<<(i&7)))
        {
            highlightsector[highlightsectorcnt++] = i;
            minhlsectorfloorz = min(minhlsectorfloorz, sector[i].floorz);
        }

    if (highlightsectorcnt==0)
    {
        minhlsectorfloorz = 0;
        highlightsectorcnt = -1;
    }
}

// hook run after handleevents in side view
static void sideview_filter_keys(void)
{
    uint32_t i;

    for (i=0; i<sizeof(keystatus)/sizeof(keystatus[0]); i++)
    {
        switch (i)
        {
//        case 0xd2: case 0xd3:  // ins, del
        case 0x2e: case 0x39:  // c, space
//        case 0xb8:  // ralt
            keystatus[i] = 0;
            break;
        }
    }
}

void m32_setkeyfilter(int32_t on)
{
    if (m32_sideview && on)
    {
        after_handleevents_hook = &sideview_filter_keys;
        clearkeys();
    }
    else
        after_handleevents_hook = 0;
}

// Get average point of sectors
static void get_sectors_center(const int16_t *sectors, int32_t numsecs, int32_t *cx, int32_t *cy)
{
    int32_t i, j, k=0, dax = 0, day = 0;
    int32_t startwall, endwall;

    for (i=0; i<numsecs; i++)
    {
        for (WALLS_OF_SECTOR(sectors[i], j))
        {
            dax += wall[j].x;
            day += wall[j].y;
            k++;
        }
    }

    if (k > 0)
    {
        dax /= k;
        day /= k;
    }

    *cx = dax;
    *cy = day;
}

static int32_t insert_sprite_common(int32_t sucksect, int32_t dax, int32_t day)
{
    int32_t i, j, k;

    i = insertsprite(sucksect,0);
    if (i < 0)
        return -1;

    sprite[i].x = dax, sprite[i].y = day;
    sprite[i].cstat = DEFAULT_SPRITE_CSTAT;
    sprite[i].shade = 0;
    sprite[i].pal = 0;
    sprite[i].xrepeat = 64, sprite[i].yrepeat = 64;
    sprite[i].xoffset = 0, sprite[i].yoffset = 0;
    sprite[i].ang = 1536;
    sprite[i].xvel = 0; sprite[i].yvel = 0; sprite[i].zvel = 0;
    sprite[i].owner = -1;
    sprite[i].clipdist = 32;
    sprite[i].lotag = 0;
    sprite[i].hitag = 0;
    sprite[i].extra = -1;

    Bmemset(localartfreq, 0, sizeof(localartfreq));
    for (k=0; k<MAXSPRITES; k++)
        localartfreq[sprite[k].picnum] += (sprite[k].statnum < MAXSTATUS);

    j = 0;
    for (k=0; k<MAXTILES; k++)
        if (localartfreq[k] > localartfreq[j])
            j = k;

    if (localartfreq[j] > 0)
        sprite[i].picnum = j;
    else
        sprite[i].picnum = 0;

    return i;
}

void correct_sprite_yoffset(int32_t i)
{
    int32_t tileyofs = (int8_t)((picanm[sprite[i].picnum]>>16)&255);
    int32_t tileysiz = tilesizy[sprite[i].picnum];

    if (klabs(tileyofs) >= tileysiz)
    {
        tileyofs *= -1;
        if (tileyofs == 128)
            tileyofs = 127;

        sprite[i].yoffset = tileyofs;
    }
    else
        sprite[i].yoffset = 0;
}

// keepcol >= 0 && <256: keep that idx-color
// keepcol < 0: keep none
// keepcol >= 256: 0x00ffffff is mask for 3 colors
void fade_editor_screen(int32_t keepcol)
{
    char blackcol=0, greycol=whitecol-25, *cp;
    int32_t pix, i, threecols = (keepcol >= 256);
    char cols[3] = {keepcol&0xff, (keepcol>>8)&0xff, (keepcol>>16)&0xff};

    begindrawing();
    cp = (char *)frameplace;
    for (i=0; i<bytesperline*(ydim-STATUS2DSIZ2); i++, cp++)
    {
        pix = (uint8_t)(*cp);

        if (!threecols && pix == keepcol)
            continue;
        if (threecols)
            if (pix==cols[0] || pix==cols[1] || pix==cols[2])
                continue;

        if (*cp==greycol)
            *cp = blackcol;
        else if (*cp != blackcol)
            *cp = greycol;
    }
    enddrawing();
    showframe(1);
}

static void copy_some_wall_members(int16_t dst, int16_t src, int32_t reset_some)
{
    //                                 x  y  p2 nw ns cs p  op sh pl xr yr xp yp lo hi ex
    static const walltype nullwall = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, -1 };
    walltype *dstwal=&wall[dst];
    const walltype *srcwal = src >= 0 ? &wall[src] : &nullwall;

    if (reset_some)
    {
        dstwal->cstat = srcwal->cstat;
    }
    else
    {
        dstwal->cstat &= ~(4+8+256);
        dstwal->cstat |= (srcwal->cstat&(4+8+256));
    }
    dstwal->shade = srcwal->shade;
    dstwal->yrepeat = srcwal->yrepeat;
    fixrepeats(dst);  // xrepeat
    dstwal->picnum = srcwal->picnum;
    dstwal->overpicnum = srcwal->overpicnum;

    dstwal->pal = srcwal->pal;
    dstwal->xpanning = srcwal->xpanning;
    dstwal->ypanning = srcwal->ypanning;

    if (reset_some)
    {
        dstwal->nextwall = -1;
        dstwal->nextsector = -1;

        dstwal->lotag = 0; //srcwal->lotag;
        dstwal->hitag = 0; //srcwal->hitag;
        dstwal->extra = -1; //srcwal->extra;
#ifdef YAX_ENABLE
        yax_setnextwall(dst, YAX_CEILING, -1);
        yax_setnextwall(dst, YAX_FLOOR, -1);
#endif
    }
}

static void init_new_wall1(int16_t *suckwall, int32_t *mousxplc, int32_t *mousyplc)
{
    int32_t i;

    Bmemset(&wall[newnumwalls], 0, sizeof(walltype));
    wall[newnumwalls].extra = -1;

    wall[newnumwalls].x = *mousxplc;
    wall[newnumwalls].y = *mousyplc;
    wall[newnumwalls].nextsector = -1;
    wall[newnumwalls].nextwall = -1;

    for (i=0; i<numwalls; i++)
    {
        YAX_SKIPWALL(i);
        if (wall[i].nextwall >= 0)
            YAX_SKIPWALL(wall[i].nextwall);

        if (wall[i].x == *mousxplc && wall[i].y == *mousyplc)
            *suckwall = i;
    }

    wall[newnumwalls].point2 = newnumwalls+1;
    newnumwalls++;
}

// helpers for often needed ops:
static int32_t do_while_copyloop1(int16_t startwall, int16_t endwall,
                                  int16_t *danumwalls, int16_t lastpoint2)
{
    int32_t m = startwall;

    do
    {
        if (*danumwalls >= MAXWALLS + M32_FIXME_WALLS)
            return 1;

        Bmemcpy(&wall[*danumwalls], &wall[m], sizeof(walltype));
        wall[*danumwalls].point2 = *danumwalls+1;
        (*danumwalls)++;
        m = wall[m].point2;
    }
    while (m != endwall);

    if (lastpoint2 >= 0)
        wall[(*danumwalls)-1].point2 = lastpoint2;

    return 0;
}

static void updatesprite1(int16_t i)
{
    setsprite(i, (vec3_t *)&sprite[i]);

    if (sprite[i].sectnum>=0)
    {
        int32_t cz, fz;
        spriteoncfz(i, &cz, &fz);
        inpclamp(&sprite[i].z, cz, fz);
    }
}

#ifdef YAX_ENABLE
static int32_t ask_above_or_below(void);
#endif

// returns:
//  0: continue
// >0: newnumwalls
// <0: error
// ignore_ret and refsect_ret are for the 'auto-red-wall' feature
static int32_t trace_loop(int32_t j, uint8_t *visitedwall, int16_t *ignore_ret, int16_t *refsect_ret,
                          int16_t trace_loop_yaxcf)
{
    int16_t refsect, ignore;
    int32_t k, n, refwall;
#if 0
//def YAX_ENABLE
    int32_t yaxp = (ignore_ret==NULL);  // bleh
#else
    UNREFERENCED_PARAMETER(trace_loop_yaxcf);
#endif

    if (wall[j].nextwall>=0 || (visitedwall[j>>3]&(1<<(j&7))))
        return 0;

    n=2*MAXWALLS;  // simple inf loop check
    refwall = j;
    k = numwalls;

    ignore = 0;

    if (ignore_ret)
    {
        refsect = -1;
        updatesectorexclude(wall[j].x, wall[j].y, &refsect, hlsectorbitmap);
        if (refsect<0)
            return -1;
    }

    do
    {
        if (j!=refwall && visitedwall[j>>3]&(1<<(j&7)))
            ignore = 1;
        visitedwall[j>>3] |= (1<<(j&7));

        if (ignore_ret)
        {
            if (inside(wall[j].x, wall[j].y, refsect) != 1)
                ignore = 1;
        }

        if (!ignore)
        {
            if (k>=MAXWALLS)
            {
                message("Wall limits exceeded while tracing outer loop.");
                return -2;
            }

            if (ignore_ret)  // auto-red wall feature
                onextwall[k] = onextwall[j];

            Bmemcpy(&wall[k], &wall[j], sizeof(walltype));
            wall[k].point2 = k+1;
// TODO: protect lotag/extra; see also hl-sector copying stuff
            wall[k].nextsector = wall[k].nextwall = wall[k].extra = -1;
#ifdef YAX_ENABLE
            if (trace_loop_yaxcf >= 0)
                yax_setnextwall(k, trace_loop_yaxcf, j);
#endif
            k++;
        }

        j = wall[j].point2;
        n--;

        while (wall[j].nextwall>=0 && n>0)
        {
#if 0
//def YAX_ENABLE
            if (yaxp)
            {
                int32_t ns = wall[j].nextsector;
                if ((hlsectorbitmap[ns>>3]&(1<<(ns&7)))==0)
                    break;
            }
#endif
            j = wall[wall[j].nextwall].point2;
//            if (j!=refwall && (visitedwall[j>>3]&(1<<(j&7))))
//                ignore = 1;
//            visitedwall[j>>3] |= (1<<(j&7));
            n--;
        }
    }
    while (j!=refwall && n>0);

    if (j!=refwall)
    {
        message("internal error while tracing outer loop: didn't reach refwall");
        return -3;
    }

    if (ignore_ret)
    {
        *ignore_ret = ignore;
        if (refsect_ret)
            *refsect_ret = refsect;
    }

    return k;
}

static int32_t backup_drawn_walls(int32_t restore)
{
    static int32_t wallsdrawn = -1;
    static walltype *tmpwall;

    // back up
    if (restore==0)
    {
        if (newnumwalls == -1)
        {
            wallsdrawn = -1;
        }
        else
        {
            wallsdrawn = newnumwalls-numwalls;

            tmpwall = Bmalloc(wallsdrawn * sizeof(walltype));
            if (!tmpwall)
            {
                wallsdrawn = -1;
                return 1;
            }

            Bmemcpy(tmpwall, &wall[numwalls], wallsdrawn*sizeof(walltype));
            newnumwalls = -1;
        }

        return 0;
    }

    // restore
    if (wallsdrawn != -1)
    {
        int32_t i;

        Bmemcpy(&wall[numwalls], tmpwall, wallsdrawn*sizeof(walltype));
        newnumwalls = numwalls + wallsdrawn;
        for (i=numwalls; i<newnumwalls; i++)
            wall[i].point2 = i+1;

        Bfree(tmpwall);
        tmpwall = NULL;
    }

    return 0;
}

#if 0
#define GETWALCOORD(w) (*(int64_t *)&wall[*(const int32_t *)(w)].x)
static int32_t compare_wall_coords(const void *w1, const void *w2)
{
    if (GETWALCOORD(w1) == GETWALCOORD(w2))
        return 0;
    if (GETWALCOORD(w1) > GETWALCOORD(w2))
        return 1;
    return -1;
}
#undef GETWALCOORD
#endif

#define RESET_EDITOR_VARS() do { \
    sectorhighlightstat = -1; \
    newnumwalls = -1; \
    joinsector[0] = -1; \
    circlewall = -1; \
    circlepoints = 7; \
    } while (0)


// breadth-first search helpers
void bfirst_search_init(int16_t *list, uint8_t *bitmap, int32_t *eltnumptr, int32_t maxnum, int16_t firstelt)
{
    Bmemset(bitmap, 0, (maxnum+7)>>3);

    list[0] = firstelt;
    bitmap[firstelt>>3] |= (1<<(firstelt&7));
    *eltnumptr = 1;
}

void bfirst_search_try(int16_t *list, uint8_t *bitmap, int32_t *eltnumptr, int16_t elt)
{
    if (elt < 0)
        return;

    if ((bitmap[elt>>3]&(1<<(elt&7)))==0)
    {
        bitmap[elt>>3] |= (1<<(elt&7));
        list[*eltnumptr] = elt;
        (*eltnumptr)++;
    }
}

#ifdef YAX_ENABLE
static int32_t collnumsects[2];
static int16_t collsectlist[2][MAXSECTORS];
static uint8_t collsectbitmap[2][MAXSECTORS>>3];

static void collect_sectors1(int16_t *sectlist, uint8_t *sectbitmap, int32_t *numsectptr,
                             int16_t startsec, int32_t alsoyaxnext, int32_t alsoonw)
{
    int32_t j, startwall, endwall, sectcnt;

    bfirst_search_init(sectlist, sectbitmap, numsectptr, MAXSECTORS, startsec);

    for (sectcnt=0; sectcnt<*numsectptr; sectcnt++)
    {
        for (WALLS_OF_SECTOR(sectlist[sectcnt], j))
        {
            if (wall[j].nextsector >= 0)
                bfirst_search_try(sectlist, sectbitmap, numsectptr, wall[j].nextsector);
            else if (alsoonw && onextwall[j]>=0)
                bfirst_search_try(sectlist, sectbitmap, numsectptr, sectorofwall(onextwall[j]));
        }

        if (alsoyaxnext)
        {
            int16_t bn[2], cf;
            yax_getbunches(sectlist[sectcnt], &bn[0], &bn[1]);
            for (cf=0; cf<2; cf++)
                if (bn[cf]>=0)
                {
                    for (SECTORS_OF_BUNCH(bn[cf], !cf, j))
                        bfirst_search_try(sectlist, sectbitmap, numsectptr, j);
                }
        }
    }
}


static int32_t sectors_components(int16_t hlsectcnt, const int16_t *hlsectors, int32_t alsoyaxnext, int32_t alsoonw);
static int32_t highlighted_sectors_components(int32_t alsoyaxnext, int32_t alsoonw)
{
    return sectors_components(highlightsectorcnt, highlightsector, alsoyaxnext, alsoonw);
}

// whether all highlighted sectors are in one (returns 1), two (2)
// or more (>2) connected components wrt the nextsector relation
// -1 means error
//  alsoyaxnext: also consider "yax-nextsector" relation
//  alsoonw: also consider "old-nextwall" relation (must be valid)
static int32_t sectors_components(int16_t hlsectcnt, const int16_t *hlsector, int32_t alsoyaxnext, int32_t alsoonw)
{
    int32_t j, k, tmp;

    if (hlsectcnt<1)
        return 0;

    collect_sectors1(collsectlist[0], collsectbitmap[0], &collnumsects[0],
                     hlsector[0], alsoyaxnext, alsoonw);

    for (k=1; k<hlsectcnt; k++)
    {
        j = hlsector[k];
        if ((collsectbitmap[0][j>>3]&(1<<(j&7)))==0)
        {
            // sector j not collected --> more than 1 conn. comp.
            collect_sectors1(collsectlist[1], collsectbitmap[1], &collnumsects[1],
                             j, alsoyaxnext, alsoonw);
            break;
        }
    }

    if (k == hlsectcnt)
        return 1;

    for (k=0; k<hlsectcnt; k++)
    {
        j = hlsector[k];
        tmp = (((collsectbitmap[0][j>>3]&(1<<(j&7)))!=0) + (((collsectbitmap[1][j>>3]&(1<<(j&7)))!=0)<<1));

        if (tmp==3)
            return -1;  // components only weakly connected

        if (tmp==0)
            return 3;  // sector j not reached
    }

    return 2;
}

static int cmpgeomwal1(const int16_t *w1, const int16_t *w2)
{
    const walltype *wal1 = &wall[*w1];
    const walltype *wal2 = &wall[*w2];

    if (wal1->x == wal2->x)
        return wal1->y - wal2->y;

    return wal1->x - wal2->x;
}

static void sort_walls_geometrically(int16_t *wallist, int32_t nwalls)
{
    qsort(wallist, nwalls, sizeof(int16_t), (int(*)(const void *, const void *))&cmpgeomwal1);
}
#endif

void SetFirstWall(int32_t sectnum, int32_t wallnum)
{
#ifdef YAX_ENABLE
    int32_t i, j, k, startwall, endwall;
    int16_t cf, bunchnum, tempsect, tempwall;

    for (i=0; i<numwalls; i++)
        wall[i].cstat &= ~(1<<14);

    for (cf=0; cf<2; cf++)
    {
        tempsect = sectnum;
        tempwall = wallnum;

        while ((bunchnum = yax_getbunch(tempsect, cf)) >= 0 &&
                   (tempsect=yax_is121(bunchnum, cf)) >= 0)
        {
            tempwall = yax_getnextwall(tempwall, cf);
            if (tempwall < 0)
                break;  // corrupt!
            wall[tempwall].cstat |= (1<<14);
        }
    }

    k = 0;
    for (i=0; i<numsectors; i++)
        for (WALLS_OF_SECTOR(i, j))
        {
            if (wall[j].cstat & (1<<14))
            {
                setfirstwall(i, j);
                k++;
                break;
            }
        }

    if (k > 0)
        message("Set first walls (sector[].wallptr) for %d sectors", k+1);
    else
#endif
        message("This wall now sector %d's first wall (sector[].wallptr)", sectnum);

    setfirstwall(sectnum, wallnum);
    mkonwinvalid();
    asksave = 1;
}

static void handlesecthighlight1(int32_t i, int32_t sub, int32_t nograycheck)
{
    int32_t j;

    if (sub)
    {
        hlsectorbitmap[i>>3] &= ~(1<<(i&7));
        for (j=sector[i].wallptr; j<sector[i].wallptr+sector[i].wallnum; j++)
        {
            if (wall[j].nextwall >= 0)
                checksectorpointer(wall[j].nextwall,wall[j].nextsector);
            checksectorpointer(j, i);
        }
    }
    else
    {
        if (nograycheck || (graysectbitmap[i>>3]&(1<<(i&7)))==0)
            hlsectorbitmap[i>>3] |= (1<<(i&7));
    }
}

#ifdef YAX_ENABLE
// 1: good, 0: bad
static int32_t hl_all_bunch_sectors_p()
{
    uint8_t *const havebunch = visited;
    int16_t cf, cb, fb;
    int32_t i, j;

    if (numyaxbunches > 0)
    {
        Bmemset(havebunch, 0, (numyaxbunches+7)>>3);
        for (i=0; i<highlightsectorcnt; i++)
        {
            yax_getbunches(highlightsector[i], &cb, &fb);
            if (cb>=0)
                havebunch[cb>>3] |= (1<<(cb&7));
            if (fb>=0)
                havebunch[fb>>3] |= (1<<(fb&7));
        }

        for (i=0; i<numyaxbunches; i++)
        {
            if ((havebunch[i>>3] & (1<<(i&7)))==0)
                continue;

            for (cf=0; cf<2; cf++)
                for (SECTORS_OF_BUNCH(i,cf, j))
                    if ((hlsectorbitmap[j>>3]&(1<<(j&7)))==0)
                        return 0;
        }
    }

    return 1;
}
#endif

void overheadeditor(void)
{
    char buffer[80], ch;
    const char *dabuffer;
    int32_t i, j, k, m=0, mousxplc, mousyplc, firstx=0, firsty=0, oposz, col;
    int32_t tempint, tempint1, tempint2;
    int32_t startwall=0, endwall, dax, day, x1, y1, x2, y2, x3, y3, x4, y4;
    int32_t highlightx1, highlighty1, highlightx2, highlighty2;
    int16_t suckwall=0, sucksect, split=0, bad;
    int16_t splitsect=0, joinsector[2];
    int16_t splitstartwall=0;
    int32_t mousx, mousy, bstatus;
    int32_t centerx, centery, circlerad;
    int16_t circlepoints, circleang1, circleang2, circleangdir;
    int32_t sectorhighlightx=0, sectorhighlighty=0;
    int16_t cursectorhighlight, sectorhighlightstat;
    walltype *wal;
    int32_t prefixarg = 0;
    int32_t resetsynctics = 0, lasttick=getticks(), waitdelay=totalclock, lastdraw=getticks();
    int32_t tsign;
    int32_t olen[2]={0,0}, nlen[2]={0,0}, dragwall[2] = {-1, -1};

    m32_setkeyfilter(1);

    qsetmodeany(xdim2d,ydim2d);
    xdim2d = xdim;
    ydim2d = ydim;

    osearchx = searchx;
    osearchy = searchy;

    searchx = clamp(scale(searchx,xdim2d,xdimgame), 8, xdim2d-8-1);
    searchy = clamp(scale(searchy,ydim2d-STATUS2DSIZ2,ydimgame), 8, ydim2d-STATUS2DSIZ-8-1);
    oposz = pos.z;

    yax_updategrays(pos.z);

    begindrawing(); //{{{
    CLEARLINES2D(0, ydim, 0);
    enddrawing(); //}}}

    ydim16 = ydim-STATUS2DSIZ2;

    cursectorhighlight = -1;
    lastpm16time = -1;

    update_highlightsector();
    ovh_whiteoutgrab();

    highlightcnt = -1;
    Bmemset(show2dwall, 0, sizeof(show2dwall));  //Clear all highlights
    Bmemset(show2dsprite, 0, sizeof(show2dsprite));

    RESET_EDITOR_VARS();
    bstatus = 0;

    while ((keystatus[buildkeys[BK_MODE2D_3D]]>>1) == 0)
    {
        if (!((vel|angvel|svel) //DOWN_BK(MOVEFORWARD) || DOWN_BK(MOVEBACKWARD) || DOWN_BK(TURNLEFT) || DOWN_BK(TURNRIGHT)
                || DOWN_BK(MOVEUP) || DOWN_BK(MOVEDOWN) || keystatus[0x10] || keystatus[0x11]
                || keystatus[0x48] || keystatus[0x4b] || keystatus[0x4d] || keystatus[0x50]  // keypad keys
                || bstatus || OSD_IsMoving()))
        {
            if (totalclock > waitdelay)
            {
                uint32_t ms = (highlightsectorcnt>0) ? 75 : 200;
                // wait for event, timeout after 200 ms - (last loop time)
                idle_waitevent_timeout(ms - min(getticks()-lasttick, ms));
                // have synctics reset to 0 after we've slept to avoid zooming out to the max instantly
                resetsynctics = 1;
            }
        }
        else waitdelay = totalclock + 30; // should be 250 ms

        lasttick = getticks();

        if (handleevents())
        {
            if (quitevent)
            {
                keystatus[1] = 1;
                quitevent = 0;
            }
        }

        if (resetsynctics)
        {
            resetsynctics = 0;
            lockclock = totalclock;
            synctics = 0;
        }

        OSD_DispatchQueued();

        if (totalclock < 120*3)
            printmessage16("Uses BUILD technology by Ken Silverman.");
        else if (totalclock < 120*6)
        {
            printmessage16("Press F1 for help.  This is a test release; always keep backups of your maps.");
            //        printext16(8L,ydim-STATUS2DSIZ+32L,editorcolors[9],-1,kensig,0);
        }

        oldmousebstatus = bstatus;
        getmousevalues(&mousx,&mousy,&bstatus);
        mousx = (mousx<<16)+mousexsurp;
        mousy = (mousy<<16)+mouseysurp;
        {
            ldiv_t ld;
            ld = ldiv(mousx, 1<<16); mousx = ld.quot; mousexsurp = ld.rem;
            ld = ldiv(mousy, 1<<16); mousy = ld.quot; mouseysurp = ld.rem;
        }
        searchx += mousx;
        searchy += mousy;

        inpclamp(&searchx, 8, xdim-8-1);
        inpclamp(&searchy, 8, ydim-8-1);

        mainloop_move();

        getpoint(searchx,searchy,&mousxplc,&mousyplc);
        linehighlight = getlinehighlight(mousxplc, mousyplc, linehighlight);

        if (newnumwalls >= numwalls)
        {
            // if we're in the process of drawing a wall, set the end point's coordinates
            dax = mousxplc;
            day = mousyplc;
            adjustmark(&dax,&day,newnumwalls);
            wall[newnumwalls].x = dax;
            wall[newnumwalls].y = day;
        }

        ydim16 = ydim;// - STATUS2DSIZ2;
        midydim16 = ydim>>1;

        tempint = numwalls;
        numwalls = newnumwalls;
        if (numwalls < 0)
            numwalls = tempint;

        if ((getticks() - lastdraw) >= 5 || (vel|angvel|svel) || DOWN_BK(MOVEUP) || DOWN_BK(MOVEDOWN)
                || mousx || mousy || bstatus || keystatus[0x10] || keystatus[0x11]
                || newnumwalls>=0 || OSD_IsMoving())
        {
            lastdraw = getticks();

            clear2dscreen();

            setup_sideview_sincos();

            if (graphicsmode && !m32_sideview)
            {
                Bmemset(show2dsector, 0xff, sizeof(show2dsector));
                setview(0, 0, xdim-1, ydim16-1);

                if (graphicsmode == 2)
                    totalclocklock = totalclock;

                drawmapview(pos.x, pos.y, zoom, 1536);
            }

            draw2dgrid(pos.x,pos.y,pos.z,cursectnum,ang,zoom,grid);

            ExtPreCheckKeys();

            {
                int32_t cx, cy;

                // Draw brown arrow (start)
                screencoords(&x2, &y2, startposx-pos.x,startposy-pos.y, zoom);
                if (m32_sideview)
                    y2 += getscreenvdisp(startposz-pos.z, zoom);

                cx = halfxdim16+x2;
                cy = midydim16+y2;
                if ((cx >= 2 && cx <= xdim-3) && (cy >= 2 && cy <= ydim16-3))
                {
                    int16_t angofs = m32_sideview ? m32_sideang : 0;
                    x1 = mulscale11(sintable[(startang+angofs+2560)&2047],zoom) / 768;
                    y1 = mulscale11(sintable[(startang+angofs+2048)&2047],zoom) / 768;
                    i = scalescreeny(x1);
                    j = scalescreeny(y1);
                    begindrawing();	//{{{
                    drawline16base(cx,cy, x1,j, -x1,-j, editorcolors[2]);
                    drawline16base(cx,cy, x1,j, +y1,-i, editorcolors[2]);
                    drawline16base(cx,cy, x1,j, -y1,+i, editorcolors[2]);
                    enddrawing();	//}}}
                }
            }

            draw2dscreen(&pos,cursectnum,ang,zoom,grid);

            begindrawing();	//{{{
            if (showtags)
            {
                if (zoom >= 768)
                {
                    for (i=0; i<numsectors; i++)
                    {
                        int16_t secshort = i;

                        YAX_SKIPSECTOR(i);

                        dabuffer = ExtGetSectorCaption(i);
                        if (dabuffer[0] == 0)
                            continue;

                        get_sectors_center(&secshort, 1, &dax, &day);

                        drawsmallabel(dabuffer, editorcolors[0], editorcolors[7],
                                       dax, day, getflorzofslope(i,dax,day));
                    }
                }

                x3 = pos.x + divscale14(-halfxdim16,zoom);
                y3 = pos.y + divscale14(-(midydim16-4),zoom);
                x4 = pos.x + divscale14(halfxdim16,zoom);
                y4 = pos.y + divscale14(ydim16-(midydim16-4),zoom);

                if (newnumwalls >= 0)
                {
                    for (i=newnumwalls; i>=tempint; i--)
                        wall[i].cstat |= (1<<14);
                }

                i = numwalls-1;
                j = numsectors-1;
                if (newnumwalls >= 0)
                    i = newnumwalls-1;
                for (wal=&wall[i]; i>=0; i--,wal--)
                {
                    if (sector[j].wallptr > i)
                        j--;

                    if (zoom < 768 && !(wal->cstat & (1<<14)))
                        continue;

                    YAX_SKIPWALL(i);

                    //Get average point of wall
//                    if ((dax > x3) && (dax < x4) && (day > y3) && (day < y4))
                    {
                        dabuffer = ExtGetWallCaption(i);
                        if (dabuffer[0] == 0)
                            continue;

                        dax = (wal->x+wall[wal->point2].x)>>1;
                        day = (wal->y+wall[wal->point2].y)>>1;
                        drawsmallabel(dabuffer, editorcolors[0], editorcolors[31],
                                       dax, day, getflorzofslope(j, dax,day));
                    }
                }

                if (zoom >= 768)
                    for (i=0, k=0; (m32_sideview && k<m32_swcnt) || (!m32_sideview && i<MAXSPRITES); i++, k++)
                    {
                        if (m32_sideview)
                        {
                            i = m32_wallsprite[k];
                            if (i<MAXWALLS)
                                continue;
                            i = i-MAXWALLS;
                        }
                        else
                            if (sprite[i].statnum == MAXSTATUS)
                                continue;

                        if (!m32_sideview && sprite[i].sectnum >= 0)
                            YAX_SKIPSECTOR(sprite[i].sectnum);

                        dabuffer = ExtGetSpriteCaption(i);
                        if (dabuffer[0] != 0)
                        {
                            int32_t blocking = (sprite[i].cstat&1);

                            col = 3 + 2*blocking;
                            if (spritecol2d[sprite[i].picnum][blocking])
                                col = spritecol2d[sprite[i].picnum][blocking];

                            if ((i == pointhighlight-16384) && (totalclock & 32))
                                col += (2<<2);

                            drawsmallabel(dabuffer, editorcolors[0], editorcolors[col],
                                          sprite[i].x, sprite[i].y, sprite[i].z);
                        }
                    }
            }

            // stick this event right between begin- end enddrawing()...
            // also after the above label stuff so users can redefine them
            VM_OnEvent(EVENT_DRAW2DSCREEN, -1);

            printcoords16(pos.x,pos.y,ang);

            numwalls = tempint;

            if (highlightsectorcnt >= 0)
            {
                for (i=0; i<numsectors; i++)
                    if (hlsectorbitmap[i>>3]&(1<<(i&7)))
                        fillsector(i, -1);
            }

            if (keystatus[0x2a]) // FIXME
            {
                drawlinepat = 0x00ff00ff;
                drawline16(searchx,0, searchx,ydim2d-1, editorcolors[15]);
                drawline16(0,searchy, xdim2d-1,searchy, editorcolors[15]);
                drawlinepat = 0xffffffff;

                _printmessage16("(%d,%d)",mousxplc,mousyplc);
#if 0
                i = (Bstrlen(tempbuf)<<3)+6;
                if ((searchx+i) < (xdim2d-1))
                    i = 0;
                else i = (searchx+i)-(xdim2d-1);
                if ((searchy+16) < (ydim2d-STATUS2DSIZ2-1))
                    j = 0;
                else j = (searchy+16)-(ydim2d-STATUS2DSIZ2-1);
                printext16(searchx+6-i,searchy+6-j,editorcolors[11],-1,tempbuf,0);
#endif
            }
            drawline16(searchx,0, searchx,8, editorcolors[15]);
            drawline16(0,searchy, 8,searchy, editorcolors[15]);

            ////// draw mouse pointer
            col = editorcolors[15 - 3*gridlock];
            if (joinsector[0] >= 0)
                col = editorcolors[11];

            if (numcorruptthings>0)
            {
                static char cbuf[64];

                if ((pointhighlight&16384)==0)
                {
                    for (i=0; i<numcorruptthings; i++)
                        if ((corruptthings[i]&CORRUPT_MASK)==CORRUPT_WALL &&
                            (corruptthings[i]&(MAXWALLS-1))==pointhighlight)
                        {
                            col = editorcolors[13];
                            printext16(searchx+6,searchy-6-8,editorcolors[13],editorcolors[0],"corrupt wall",0);
                            break;
                        }
                }

                Bsprintf(cbuf, "Map corrupt (level %d): %s%d errors", corruptlevel,
                         numcorruptthings>=MAXCORRUPTTHINGS ? ">=":"", numcorruptthings);
                printext16(8,8, editorcolors[13],editorcolors[0],cbuf,0);
            }

            if (keystatus[0x36] || keystatus[0xb8] || keystatus[0x9d])  // RSHIFT || RALT || RCTRL
            {
                if (keystatus[0x27] || keystatus[0x28])  // ' and ;
                {
                    col = editorcolors[14];

                    drawline16base(searchx+16, searchy-16, -4,0, +4,0, col);
                    if (keystatus[0x28])
                        drawline16base(searchx+16, searchy-16, 0,-4, 0,+4, col);
                }
                else if (keystatus[0x36] && eitherCTRL)
                    printext16(searchx+6,searchy-6-8,editorcolors[12],-1,"S",0);
            }

            drawline16base(searchx,searchy, +0,-8, +0,-1, col);
            drawline16base(searchx,searchy, +1,-8, +1,-1, col);
            drawline16base(searchx,searchy, +0,+2, +0,+9, col);
            drawline16base(searchx,searchy, +1,+2, +1,+9, col);
            drawline16base(searchx,searchy, -8,+0, -1,+0, col);
            drawline16base(searchx,searchy, -8,+1, -1,+1, col);
            drawline16base(searchx,searchy, +2,+0, +9,+0, col);
            drawline16base(searchx,searchy, +2,+1, +9,+1, col);

            ////// Draw the white pixel closest to mouse cursor on linehighlight
            if (linehighlight>=0)
            {
                char col = wall[linehighlight].nextsector >= 0 ? editorcolors[15] : editorcolors[5];

                if (m32_sideview)
                {
                    getclosestpointonwall(searchx,searchy, linehighlight, &dax,&day, 1);
                    drawline16base(dax,day, 0,0, 0,0, col);
                }
                else
                {
                    getclosestpointonwall(mousxplc,mousyplc, linehighlight, &dax,&day, 0);
                    x2 = mulscale14(dax-pos.x,zoom);
                    y2 = mulscale14(day-pos.y,zoom);

                    drawline16base(halfxdim16+x2,midydim16+y2, 0,0, 0,0, col);
                }
            }

            enddrawing();	//}}}

            OSD_Draw();
        }

        VM_OnEvent(EVENT_PREKEYS2D, -1);
        ExtCheckKeys(); // TX 20050101, it makes more sense to have this here so keys can be overwritten with new functions in bstub.c

        // Flip/mirror sector Ed Coolidge
        if (keystatus[0x2d] || keystatus[0x15])  // X or Y (2D)
        {
            int32_t about_x=keystatus[0x2d];
            int32_t doMirror = eitherALT;  // mirror walls and wall/floor sprites

#ifdef YAX_ENABLE
            if (highlightsectorcnt > 0 && !hl_all_bunch_sectors_p())
            {
                printmessage16("To flip extended sectors, all sectors of a bunch must be selected");
            }
            else
#endif
            if (highlightsectorcnt > 0)
            {
                keystatus[0x2d] = keystatus[0x15] = 0;

                get_sectors_center(highlightsector, highlightsectorcnt, &dax, &day);

                if (gridlock && grid > 0)
                    locktogrid(&dax, &day);

                for (i=0; i<highlightsectorcnt; i++)
                {
                    int32_t startofloop, endofloop;
                    int32_t numtoswap = -1;
                    int32_t w=0;
                    walltype tempwall;

                    startofloop = startwall = sector[highlightsector[i]].wallptr;
                    endofloop = endwall = startwall+sector[highlightsector[i]].wallnum-1;
#if 0
                    if (doMirror)
                    {
                        //mirror sector textures
                        sector[highlightsector[i]].ceilingstat ^= 0x10;
                        sector[highlightsector[i]].floorstat ^= 0x10;
                    }
#endif
                    //save position of wall at start of loop
                    x3 = wall[startofloop].x;
                    y3 = wall[startofloop].y;

                    for (j=startwall; j<=endwall; j++)
                    {
                        //fix position of walls
                        if (about_x)
                        {
                            wall[j].x = dax-POINT2(j).x+dax; //flip wall.x about dax
                            wall[j].y = POINT2(j).y;
                        }
                        else
                        {
                            wall[j].x = POINT2(j).x;
                            wall[j].y = day-POINT2(j).y+day; //flip wall.y about day
                        }

                        if (doMirror)
                            wall[j].cstat ^= 8;  //mirror walls about dax/day

                        if (wall[j].point2==startofloop) //check if j is end of loop
                        {
                            endofloop = j;
                            if (about_x)
                            {
                                wall[endofloop].x = dax-x3+dax; //flip wall.x about dax
                                wall[endofloop].y = y3;
                            }
                            else
                            {
                                wall[endofloop].x = x3;
                                wall[endofloop].y = day-y3+day; //flip wall.y about dax
                            }

                            //correct order of walls in loop to maintain player space (right-hand rule)
                            numtoswap = (endofloop-startofloop)>>1;
                            for (w=1; w<=numtoswap; w++)
                            {
                                Bmemcpy(&tempwall, &wall[startofloop+w], sizeof(walltype));
                                Bmemcpy(&wall[startofloop+w], &wall[endofloop-w+1], sizeof(walltype));
                                Bmemcpy(&wall[endofloop-w+1], &tempwall, sizeof(walltype));
                            }

                            //make point2 point to next wall in loop
                            for (w=startofloop; w<endofloop; w++)
                                wall[w].point2 = w+1;
                            wall[endofloop].point2 = startofloop;

                            startofloop = endofloop+1; //set first wall of next loop
                            //save position of wall at start of loop
                            x3 = wall[startofloop].x;
                            y3 = wall[startofloop].y;
                        }
                    }

                    j = headspritesect[highlightsector[i]];
                    while (j != -1)
                    {
                        if (about_x)
                        {
                            x3 = sprite[j].x;
                            sprite[j].x = dax-x3+dax; //flip sprite.x about dax
                            sprite[j].ang = (1024+2048-sprite[j].ang)&2047; //flip ang about 512
                        }
                        else
                        {
                            y3 = sprite[j].y;
                            sprite[j].y = day-y3+day; //flip sprite.y about day
                            sprite[j].ang = (2048-sprite[j].ang)&2047; //flip ang about 512
                        }

                        if (doMirror && (sprite[j].cstat & 0x30))
                            sprite[j].cstat ^= 4;  // mirror sprites about dax/day (don't mirror monsters)

                        j = nextspritesect[j];
                    }
                }

                printmessage16("Selected sector(s) flipped");
                asksave = 1;
            }
        }
        // end edit for sector flip

        if (keystatus[88])   //F12
        {
            keystatus[88] = 0;
//__clearscreen_beforecapture__

            Bsprintf(tempbuf, "Mapster32 %s", ExtGetVer());
            screencapture("captxxxx.tga", eitherSHIFT, tempbuf);

            showframe(1);
        }
        if (keystatus[0x30])  // B (clip Blocking xor) (2D)
        {
            pointhighlight = getpointhighlight(mousxplc, mousyplc, pointhighlight);
            linehighlight = getlinehighlight(mousxplc, mousyplc, linehighlight);

            if ((pointhighlight&0xc000) == 16384)
            {
                sprite[pointhighlight&16383].cstat ^= 1;
                sprite[pointhighlight&16383].cstat &= ~256;
                sprite[pointhighlight&16383].cstat |= ((sprite[pointhighlight&16383].cstat&1)<<8);
                asksave = 1;
            }
            else if (linehighlight >= 0)
            {
                wall[linehighlight].cstat ^= 1;
                wall[linehighlight].cstat &= ~64;
                if ((wall[linehighlight].nextwall >= 0) && !eitherSHIFT)
                {
                    NEXTWALL(linehighlight).cstat &= ~(1+64);
                    NEXTWALL(linehighlight).cstat |= (wall[linehighlight].cstat&1);
                }
                asksave = 1;
            }
            keystatus[0x30] = 0;
        }
        if (keystatus[0x21])  //F (F alone does nothing in 2D right now)
        {
            keystatus[0x21] = 0;
            if (eitherALT)  //ALT-F (relative alignmment flip)
            {
                linehighlight = getlinehighlight(mousxplc, mousyplc, linehighlight);
                if (linehighlight >= 0)
                    SetFirstWall(sectorofwall(linehighlight), linehighlight);
            }
        }

        if (keystatus[0x18])  // O (ornament onto wall) (2D)
        {
            keystatus[0x18] = 0;
            if ((pointhighlight&0xc000) == 16384)
            {
                asksave = 1;
                DoSpriteOrnament(pointhighlight&16383);
            }
        }


        tsign = 0;
        if (keystatus[0x33] || (bstatus&33)==33)  // , (2D)
            tsign = +1;
        if (keystatus[0x34] || (bstatus&17)==17)  // . (2D)
            tsign = -1;

        if (tsign)
        {
#ifdef YAX_ENABLE
            if (highlightsectorcnt > 0 && !hl_all_bunch_sectors_p())
            {
                printmessage16("To rotate ext. sectors, all sectors of a bunch must be selected");
            }
            else
#endif
            if (highlightsectorcnt > 0)
            {
                int32_t smoothRotation = !eitherSHIFT;

                get_sectors_center(highlightsector, highlightsectorcnt, &dax, &day);

                if (smoothRotation)
                {
                    if (gridlock && grid > 0)
                        locktogrid(&dax, &day);
                }

                for (i=0; i<highlightsectorcnt; i++)
                {
                    for (WALLS_OF_SECTOR(highlightsector[i], j))
                    {
                        if (smoothRotation)
                        {
                            x3 = wall[j].x;
                            y3 = wall[j].y;
                            wall[j].x = dax + tsign*(day-y3);
                            wall[j].y = day + tsign*(x3-dax);
                        }
                        else
                            rotatepoint(dax,day, wall[j].x,wall[j].y, tsign&2047, &wall[j].x,&wall[j].y);
                    }

                    for (j=headspritesect[highlightsector[i]]; j != -1; j=nextspritesect[j])
                    {
                        if (smoothRotation)
                        {
                            x3 = sprite[j].x;
                            y3 = sprite[j].y;
                            sprite[j].x = dax + tsign*(day-y3);
                            sprite[j].y = day + tsign*(x3-dax);
                            sprite[j].ang = (sprite[j].ang+tsign*512)&2047;
                        }
                        else
                        {
                            rotatepoint(dax,day, sprite[j].x,sprite[j].y, tsign&2047, &sprite[j].x,&sprite[j].y);
                            sprite[j].ang = (sprite[j].ang+tsign)&2047;
                        }
                    }
                }

                if (smoothRotation)
                    keystatus[0x33] = keystatus[0x34] = 0;

                mouseb &= ~(16|32);
                bstatus &= ~(16|32);
                asksave = 1;
            }
            else
            {
                if (pointhighlight >= 16384)
                {
                    i = pointhighlight-16384;
                    if (eitherSHIFT)
                        sprite[i].ang = (sprite[i].ang-tsign)&2047;
                    else
                    {
                        sprite[i].ang = (sprite[i].ang-128*tsign)&2047;
                        keystatus[0x33] = keystatus[0x34] = 0;
                    }

                    mouseb &= ~(16|32);
                    bstatus &= ~(16|32);
                }
            }
        }

        if (keystatus[0x46])  //Scroll lock (set starting position)
        {
            startposx = pos.x;
            startposy = pos.y;
            startposz = pos.z;
            startang = ang;
            startsectnum = cursectnum;
            keystatus[0x46] = 0;
            asksave = 1;
        }
#if 1
        if (keystatus[0x3f])  //F5
        {
            ExtShowSectorData(0);
        }
        if (keystatus[0x40])  //F6
        {
            if (pointhighlight >= 16384)
                ExtShowSpriteData(pointhighlight-16384);
            else if (linehighlight >= 0)
                ExtShowWallData(linehighlight);
            else
                ExtShowWallData(0);
        }
        if (keystatus[0x41])  //F7
        {
            keystatus[0x41] = 0;

            for (i=0; i<numsectors; i++)
                if (inside_editor_curpos(i) == 1)
                {
                    ExtEditSectorData(i);
                    break;
                }
        }
        if (keystatus[0x42])  //F8
        {
            keystatus[0x42] = 0;

            if (pointhighlight >= 16384)
                ExtEditSpriteData(pointhighlight-16384);
            else if (linehighlight >= 0)
                ExtEditWallData(linehighlight);
        }
#endif

        if (keystatus[0x23])  //H (Hi 16 bits of tag)
        {
            keystatus[0x23] = 0;
            if (eitherCTRL)  //Ctrl-H
            {
                pointhighlight = getpointhighlight(mousxplc, mousyplc, pointhighlight);
                linehighlight = getlinehighlight(mousxplc, mousyplc, linehighlight);

                if ((pointhighlight&0xc000) == 16384)
                {
                    sprite[pointhighlight&16383].cstat ^= 256;
                    asksave = 1;
                }
                else if (linehighlight >= 0)
                {
                    wall[linehighlight].cstat ^= 64;
                    if ((wall[linehighlight].nextwall >= 0) && !eitherSHIFT)
                    {
                        NEXTWALL(linehighlight).cstat &= ~64;
                        NEXTWALL(linehighlight).cstat |= (wall[linehighlight].cstat&64);
                    }
                    asksave = 1;
                }
            }
            else if (eitherALT)  //ALT
            {
                if (pointhighlight >= 16384)
                {
                    i = pointhighlight-16384;
                    j = taglab_linktags(1, i);
                    j = 2*(j&2);
                    Bsprintf(buffer, "Sprite (%d) Hi-tag: ", i);
                    sprite[i].hitag = getnumber16(buffer, sprite[i].hitag, BTAG_MAX, 0+j);
                }
                else if (linehighlight >= 0)
                {
                    i = linehighlight;
                    j = taglab_linktags(1, i);
                    j = 2*(j&2);
                    Bsprintf(buffer, "Wall (%d) Hi-tag: ", i);
                    wall[i].hitag = getnumber16(buffer, wall[i].hitag, BTAG_MAX, 0+j);
                }
            }
            else
            {
                for (i=0; i<numsectors; i++)
                    if (inside_editor_curpos(i) == 1)
                    {
                        Bsprintf(buffer, "Sector (%d) Hi-tag: ", i);
                        sector[i].hitag = getnumber16(buffer, sector[i].hitag, BTAG_MAX, 0);
                        break;
                    }
            }
            // printmessage16("");
        }
        if (keystatus[0x19])  // P (palookup #)
        {
            keystatus[0x19] = 0;

            for (i=0; i<numsectors; i++)
                if (inside_editor_curpos(i) == 1)
                {
                    Bsprintf(buffer, "Sector (%d) Ceilingpal: ", i);
                    sector[i].ceilingpal = getnumber16(buffer, sector[i].ceilingpal, M32_MAXPALOOKUPS, 0);

                    Bsprintf(buffer, "Sector (%d) Floorpal: ", i);
                    sector[i].floorpal = getnumber16(buffer, sector[i].floorpal, M32_MAXPALOOKUPS, 0);
                    break;
                }
        }
        if (keystatus[0x12])  // E (status list)
        {
            keystatus[0x12] = 0;

            if (!eitherCTRL)
            {
                if (pointhighlight >= 16384)
                {
                    i = pointhighlight-16384;
                    Bsprintf(buffer, "Sprite (%d) Status list: ", i);
                    changespritestat(i, getnumber16(buffer, sprite[i].statnum, MAXSTATUS-1, 0));
                }
            }
#ifdef YAX_ENABLE
            else if (highlightsectorcnt > 0 && newnumwalls < 0)
            {
                ////////// YAX //////////
                static const char *cfs[2] = {"ceiling", "floor"};

                int32_t cf, thez, ulz[2]={0,0};
                int16_t bn, sandwichbunch=-1;

                if (numyaxbunches==YAX_MAXBUNCHES)
                {
                    message("Bunch limit of %d reached, cannot extend", YAX_MAXBUNCHES);
                    goto end_yax;
                }

                if (highlighted_sectors_components(0,0) != 1)
                {
                    message("Sectors to extend must be in one connected component");
                    goto end_yax;
                }

                cf = ask_above_or_below();
                if (cf==-1)
                    goto end_yax;

                thez = SECTORFLD(highlightsector[0],z, cf);
                for (i=0; i<highlightsectorcnt; i++)
                {
                    bn = yax_getbunch(highlightsector[i], cf);

                    if (sandwichbunch >= 0 && bn!=sandwichbunch)
                    {
                        message("When sandwiching extension, must select only sectors of one bunch");
                        goto end_yax;
                    }

                    if (bn >= 0)
                    {
                        if (cf==YAX_FLOOR)
                        {
                            if (sandwichbunch < 0 && i!=0)
                            {
                                message("When sandwiching extension, must select only sectors of the bunch");
                                goto end_yax;
                            }
                            sandwichbunch = bn;
                        }
                        else
                        {
                            message("Sector %d's %s is already extended", highlightsector[i], cfs[cf]);
                            goto end_yax;
                        }
                    }

                    if (SECTORFLD(highlightsector[i],z, cf) != thez)
                    {
                        message("All sectors must have the same %s height", cfs[cf]);
                        goto end_yax;
                    }

                    if ((sandwichbunch>=0 || highlightsectorcnt>1) && SECTORFLD(highlightsector[i],stat, cf)&2)
                    {
                        message("Sector %ss must not be sloped%s", cfs[cf],
                                sandwichbunch>=0 ? "" : "if extending more than one");
                        goto end_yax;
                    }
                }

                if (sandwichbunch >= 0)
                {
                    // cf==YAX_FLOOR here

                    int32_t tempz, oldfz, swsecheight = DEFAULT_YAX_HEIGHT/4;
                    // highest floor z of lower sectors, lowest ceiling z of these sectors
                    int32_t minfloorz = INT32_MAX, maxceilz = INT32_MIN;

                    // some preparation for making the sandwich
                    if (highlightsectorcnt != yax_numsectsinbunch(sandwichbunch, YAX_FLOOR))
                    {
                        message("When sandwiching extension, must select all sectors of the bunch");
                        goto end_yax;
                    }

                    // "for i in sectors of sandwichbunch(floor)" is now the same as
                    // "for i in highlighted sectors"

                    oldfz = sector[highlightsector[0]].floorz;

                    // check if enough room in z
                    for (SECTORS_OF_BUNCH(sandwichbunch, YAX_CEILING, i))
                        for (WALLS_OF_SECTOR(i, j))
                        {
                            tempz = getflorzofslope(i, wall[j].x, wall[j].y);
                            minfloorz = min(minfloorz, tempz);
                        }
                    for (SECTORS_OF_BUNCH(sandwichbunch, YAX_FLOOR, i))
                        for (WALLS_OF_SECTOR(i, j))
                        {
                            tempz = getceilzofslope(i, wall[j].x, wall[j].y);
                            maxceilz = max(maxceilz, tempz);
                        }

                    if (minfloorz - maxceilz < 2*swsecheight)
                    {
                        message("Too little z headroom for sandwiching, need at least %d",
                                2*swsecheight);
                        goto end_yax;
                    }

                    if (maxceilz >= oldfz || oldfz >= minfloorz)
                    {
                        message("Internal error while sandwiching: oldfz out of bounds");
                        goto end_yax;
                    }

                    // maxceilz   ---|
                    //   ^           |
                    // ulz[0]        ^
                    //   ^          oldfz
                    // ulz[1]        ^
                    //   ^           |
                    // minfloorz  ---|

                    ulz[0] = oldfz - swsecheight*((double)(oldfz-maxceilz)/(minfloorz-maxceilz));
                    ulz[0] &= ~255;
                    ulz[1] = ulz[0] + swsecheight;

                    if (maxceilz >= ulz[0] || ulz[1] >= minfloorz)
                    {
                        message("Too little z headroom for sandwiching");
                        goto end_yax;
                    }
                }

                m = numwalls;
                Bmemset(visited, 0, sizeof(visited));
                // construct!
                for (i=0; i<highlightsectorcnt; i++)
                    for (WALLS_OF_SECTOR(highlightsector[i], j))
                    {
                        k = trace_loop(j, visited, NULL, NULL, !cf);
                        if (k == 0)
                            continue;
                        else if (k < 0)
                        {
                            numwalls = m;
                            goto end_yax;
                        }
//message("loop");
                        wall[k-1].point2 = numwalls;
                        numwalls = k;
                    }

                for (i=m; i<numwalls; i++)  // try
                {
                    j = YAX_NEXTWALL(i, !cf);
                    if (j < 0)
                    {
                        message("Internal error while constructing sector: "
                                "YAX_NEXTWALL(%d, %d)<0!", i, !cf);
                        numwalls = m;
                        goto end_yax;
                    }
                    if (sandwichbunch >= 0)
                    {
                        if (YAX_NEXTWALL(j, cf) < 0)
                        {
                            message("Internal error while sandwiching (2): "
                                    "YAX_NEXTWALL(%d, %d)<0!", j, cf);
                            numwalls = m;
                            goto end_yax;
                        }
                    }
                }
                for (i=m; i<numwalls; i++)  // do!
                {
                    j = YAX_NEXTWALL(i, !cf);

                    if (sandwichbunch >= 0)
                    {
                        int16_t oynw = YAX_NEXTWALL(j, cf);
                        yax_setnextwall(j, cf, i);
                        yax_setnextwall(i, cf, oynw);
                        yax_setnextwall(oynw, !cf, i);
                    }
                    else
                    {
                        yax_setnextwall(j, cf, i);
                    }
                }

                // create new sector based on first highlighted one
                i = highlightsector[0];
                Bmemcpy(&sector[numsectors], &sector[i], sizeof(sectortype));
                sector[numsectors].wallptr = m;
                sector[numsectors].wallnum = numwalls-m;

                if (sandwichbunch < 0)
                {
                    if (SECTORFLD(i,stat, cf)&2)
                        setslope(numsectors, !cf, SECTORFLD(i,heinum, cf));
                    else
                        setslope(numsectors, !cf, 0);
                    setslope(numsectors, cf, 0);

                    SECTORFLD(numsectors,z, !cf) = SECTORFLD(i,z, cf);
                    SECTORFLD(numsectors,z, cf) = SECTORFLD(i,z, cf) - (1-2*cf)*DEFAULT_YAX_HEIGHT;
                }
                else
                {
                    for (SECTORS_OF_BUNCH(sandwichbunch, cf, i))
                        sector[i].floorz = ulz[0];
                    sector[numsectors].ceilingz = ulz[0];
                    sector[numsectors].floorz = ulz[1];
                    for (SECTORS_OF_BUNCH(sandwichbunch, !cf, i))
                        sector[i].ceilingz = ulz[1];
                }

                newnumwalls = numwalls;
                numwalls = m;

                SECTORFLD(numsectors,stat, !cf) &= ~1;  // no plax

                // restore red walls of the selected sectors
                for (i=0; i<highlightsectorcnt; i++)
                {
                    SECTORFLD(highlightsector[i],stat, cf) &= ~1;  // no plax

                    for (WALLS_OF_SECTOR(highlightsector[i], j))
                        if (wall[j].nextwall < 0)
                            checksectorpointer(j, highlightsector[i]);
                }

                // link
                if (sandwichbunch < 0)
                {
                    yax_setbunch(numsectors, !cf, numyaxbunches);
                    for (i=0; i<highlightsectorcnt; i++)
                        yax_setbunch(highlightsector[i], cf, numyaxbunches);
                }
                else
                {
                    yax_setbunch(numsectors, !cf, sandwichbunch);
                    // also relink
                    yax_setbunch(numsectors, cf, numyaxbunches);
                    for (SECTORS_OF_BUNCH(sandwichbunch, !cf, i))
                        yax_setbunch(i, !cf, numyaxbunches);
                }

                numwalls = newnumwalls;
                newnumwalls = -1;

                numsectors++;
                yax_update(0);
                yax_updategrays(pos.z);

                Bmemset(hlsectorbitmap, 0, sizeof(hlsectorbitmap));
                update_highlightsector();

                if (sandwichbunch < 0)
                    message("Extended %ss of highlighted sectors, creating bunch %d",
                            cfs[cf], numyaxbunches-1);
                else
                    message("Sandwiched bunch %d, creating bunch %d",
                            sandwichbunch, numyaxbunches-1);
                asksave = 1;
end_yax: ;
            }
#endif
        }

        if (highlightsectorcnt < 0)
        {
            if (keystatus[0x36])  //Right shift (point highlighting)
            {
                if (highlightcnt == 0)
                {
                    int32_t xx[] = { highlightx1, highlightx1, searchx, searchx, highlightx1 };
                    int32_t yy[] = { highlighty1, searchy, searchy, highlighty1, highlighty1 };

                    highlightx2 = searchx;
                    highlighty2 = searchy;
                    ydim16 = ydim-STATUS2DSIZ2;

                    plotlines2d(xx, yy, 5, editorcolors[5]);
                }
                else
                {
                    highlightcnt = 0;

                    highlightx1 = searchx;
                    highlighty1 = searchy;
                    highlightx2 = searchx;
                    highlighty2 = searchy;
                }
            }
            else
            {
                if (highlightcnt == 0)
                {
                    int32_t add=keystatus[0x28], sub=(!add && keystatus[0x27]), setop=(add||sub);

                    if (!m32_sideview)
                    {
                        getpoint(highlightx1,highlighty1, &highlightx1,&highlighty1);
                        getpoint(highlightx2,highlighty2, &highlightx2,&highlighty2);
                    }

                    if (highlightx1 > highlightx2)
                        swaplong(&highlightx1, &highlightx2);
                    if (highlighty1 > highlighty2)
                        swaplong(&highlighty1, &highlighty2);

                    // Ctrl+RShift: select all wall-points of highlighted wall's loop:
                    if (eitherCTRL && highlightx1==highlightx2 && highlighty1==highlighty2)
                    {
                        if (!setop)
                        {
                            Bmemset(show2dwall, 0, sizeof(show2dwall));
                            Bmemset(show2dsprite, 0, sizeof(show2dsprite));
                        }

                        if (linehighlight >= 0 && linehighlight < MAXWALLS)
                        {
                            i = linehighlight;
                            do
                            {
                                if (!sub)
                                    show2dwall[i>>3] |= (1<<(i&7));
                                else
                                    show2dwall[i>>3] &= ~(1<<(i&7));

                                for (j=0; j<numwalls; j++)
                                    if (j!=i && wall[j].x==wall[i].x && wall[j].y==wall[i].y)
                                    {
                                        if (!sub)
                                            show2dwall[j>>3] |= (1<<(j&7));
                                        else
                                            show2dwall[j>>3] &= ~(1<<(j&7));
                                    }

                                i = wall[i].point2;
                            }
                            while (i != linehighlight);
                        }

                        update_highlight();
                    }
                    else
                    {
                        int32_t tx, ty, onlySprites=eitherCTRL;

                        if (!setop)
                        {
                            Bmemset(show2dwall, 0, sizeof(show2dwall));
                            Bmemset(show2dsprite, 0, sizeof(show2dsprite));
                        }

                        for (i=0; i<numwalls; i++)
                            wall[i].cstat &= ~(1<<14);

                        for (i=0; i<numwalls; i++)
                        {
                            if (onlySprites)
                                break;

                            YAX_SKIPWALL(i);

                            if (!m32_sideview)
                            {
                                tx = wall[i].x;
                                ty = wall[i].y;
//                                wall[i].cstat &= ~(1<<14);
                            }
                            else
                            {
                                screencoords(&tx,&ty, wall[i].x-pos.x,wall[i].y-pos.y, zoom);
                                ty += getscreenvdisp(
                                    getflorzofslope(sectorofwall(i), wall[i].x,wall[i].y)-pos.z, zoom);
                                tx += halfxdim16;
                                ty += midydim16;
                            }

                            if (tx >= highlightx1 && tx <= highlightx2 &&
                                    ty >= highlighty1 && ty <= highlighty2)
                            {
                                if (!sub)
                                {
                                    if (numgraysects > 0 || m32_sideview)
                                    {
                                        dragpoint(i, wall[i].x, wall[i].y);
                                        dragpoint_noreset = 1;  // vvv
                                    }
                                    else
                                        show2dwall[i>>3] |= (1<<(i&7));
                                }
                                else
                                    show2dwall[i>>3] &= ~(1<<(i&7));
                            }
                        }
                        dragpoint_noreset = 0;  // ^^^

                        if (!sub && (numgraysects > 0 || m32_sideview))
                        {
                            for (i=0; i<numwalls; i++)
                                if (wall[i].cstat&(1<<14))
                                    show2dwall[i>>3] |= (1<<(i&7));
                        }
#if 0
                        if (m32_sideview && numwalls>0 && !onlySprites)
                        {
                            int64_t curcoord;
                            int32_t begwall=0, mustselect=0;

                            // also select walls that would be dragged but
                            // maybe were missed

                            for (i=0; i<numwalls; i++)
                                tempxyar[i][0] = i;
                            qsort(tempxyar, numwalls, sizeof(tempxyar[0]), compare_wall_coords);

                            i = 0;
                            k = tempxyar[i][0];  // the actual wallnum...
                            curcoord = *(int64_t *)&wall[k].x;
                            while (i<numwalls)
                            {
                                //OSD_Printf("%5d: wall %5d: %8d,%8d\n", i, k, wall[k].x, wall[k].y);
                                mustselect |= (show2dwall[k>>3]&(1<<(k&7)));
                                i++;
                                if (i==numwalls || ((k=tempxyar[i][0]), (*(int64_t *)&wall[k].x != curcoord)))
                                {
                                    if (mustselect)
                                    {
                                        // select all from begwall to i-1
                                        for (j=begwall; j<i; j++)
                                        {
                                            //OSD_Printf("sel %5d: wall %5d: %8d,%8d\n", j, tempxyar[j][0],
                                            //wall[tempxyar[j][0]].x, wall[tempxyar[j][0]].y);
                                            show2dwall[tempxyar[j][0]>>3] |= (1<<(tempxyar[j][0]&7));
                                        }

                                        mustselect = 0;
                                    }

                                    if (i==numwalls)
                                        break;

                                    begwall = i;
                                    curcoord = *(int64_t *)&wall[tempxyar[i][0]].x;
                                }
                            }
                        }
#endif
                        for (i=0; i<MAXSPRITES; i++)
                        {
                            if (sprite[i].statnum == MAXSTATUS)
                                continue;

                            if ((unsigned)sprite[i].sectnum < MAXSECTORS)
                                YAX_SKIPSECTOR(sprite[i].sectnum);

                            if (!m32_sideview)
                            {
                                tx = sprite[i].x;
                                ty = sprite[i].y;
                            }
                            else
                            {
                                screencoords(&tx,&ty, sprite[i].x-pos.x,sprite[i].y-pos.y, zoom);
                                ty += getscreenvdisp(sprite[i].z-pos.z, zoom);
                                tx += halfxdim16;
                                ty += midydim16;
                            }

                            if (tx >= highlightx1 && tx <= highlightx2 &&
                                    ty >= highlighty1 && ty <= highlighty2)
                            {
                                if (!sub)
                                {
                                    if (sprite[i].sectnum >= 0)  // don't allow to select sprites in null space
                                        show2dsprite[i>>3] |= (1<<(i&7));
                                }
                                else
                                    show2dsprite[i>>3] &= ~(1<<(i&7));
                            }
                        }

                        update_highlight();
                    }
                }
            }
        }

        if (highlightcnt < 0)
        {
            if (keystatus[0xb8])  //Right alt (sector highlighting)
            {
                if (highlightsectorcnt == 0)
                {
                    if (!eitherCTRL)
                    {
                        int32_t xx[] = { highlightx1, highlightx1, searchx, searchx, highlightx1 };
                        int32_t yy[] = { highlighty1, searchy, searchy, highlighty1, highlighty1 };

                        highlightx2 = searchx;
                        highlighty2 = searchy;
                        ydim16 = ydim-STATUS2DSIZ2;

                        plotlines2d(xx, yy, 5, editorcolors[10]);
                    }
                }
                else
                {
                    int32_t didmakered = (highlightsectorcnt<0), hadouterpoint=0;
                    int16_t tmprefsect = -1;

                    for (i=0; i<highlightsectorcnt; i++)
                    {
                        for (WALLS_OF_SECTOR(highlightsector[i], j))
                        {
//                            if (wall[j].nextwall >= 0)
//                                checksectorpointer(wall[j].nextwall,wall[j].nextsector);
                            if (wall[j].nextwall < 0)
                                didmakered |= !!checksectorpointer(j, highlightsector[i]);

                            if (!didmakered)
                            {
                                updatesectorexclude(wall[j].x, wall[j].y, &tmprefsect, hlsectorbitmap);
                                if (tmprefsect<0)
                                    hadouterpoint = 1;
                            }
                        }
                    }

                    if (!didmakered && !hadouterpoint && newnumwalls<0)
                    {
#ifdef YAX_ENABLE
                        int16_t cb, fb;

                        yax_getbunches(tmprefsect, &cb, &fb);
                        if (cb>=0 || fb>=0)
                            didmakered = 1;
                        else
#endif
                        {
                            // fade the screen to have the user's attention
                            fade_editor_screen(-1);

                            didmakered |= !ask_if_sure("Insert outer loop and make red walls? (Y/N)", 0);
                            clearkeys();
                        }
                    }

                    if (!didmakered && !hadouterpoint && newnumwalls<0)
                    {
                        int16_t ignore, refsect;
                        int32_t n;

                        Bmemset(visited, 0, sizeof(visited));

                        for (i=0; i<highlightsectorcnt; i++)
                        {
                            for (WALLS_OF_SECTOR(highlightsector[i], j))
                            {
                                k = trace_loop(j, visited, &ignore, &refsect, -1);
                                if (k == 0)
                                    continue;
                                else if (k < 0)
                                {
                                    i = highlightsectorcnt;
                                    break;  // outer loop too
                                }

                                if (ignore)
                                    continue;
                                
                                wall[k-1].point2 = numwalls;  // close the loop
                                newnumwalls = k;
                                n = (newnumwalls-numwalls);  // number of walls in just constructed loop

                                if (clockdir(numwalls)==0)
                                {
                                    int16_t begwalltomove = sector[refsect].wallptr+sector[refsect].wallnum;
                                    int32_t onwwasvalid = onwisvalid();

                                    flipwalls(numwalls, newnumwalls);

                                    sector[refsect].wallnum += n;
                                    if (refsect != numsectors-1)
                                    {
                                        walltype *tmpwall = Bmalloc(n * sizeof(walltype));
                                        int16_t *tmponw = Bmalloc(n * sizeof(int16_t));

                                        if (!tmpwall || !tmponw)
                                        {
                                            message("out of memory!");
                                            ExtUnInit();
                                            uninitsystem();
                                            exit(1);
                                        }

                                        for (m=0; m<numwalls; m++)
                                        {
                                            if (wall[m].nextwall >= begwalltomove)
                                                wall[m].nextwall += n;
                                        }
#ifdef YAX_ENABLE
                                        yax_tweakwalls(begwalltomove, n);
#endif
                                        for (m=refsect+1; m<numsectors; m++)
                                            sector[m].wallptr += n;
                                        for (m=begwalltomove; m<numwalls; m++)
                                            wall[m].point2 += n;
                                        for (m=numwalls; m<newnumwalls; m++)
                                            wall[m].point2 += (begwalltomove-numwalls);

                                        Bmemcpy(tmponw, &onextwall[numwalls], n*sizeof(int16_t));
                                        Bmemmove(&onextwall[begwalltomove+n], &onextwall[begwalltomove],
                                                 (numwalls-begwalltomove)*sizeof(int16_t));
                                        Bmemcpy(&onextwall[begwalltomove], tmponw, n*sizeof(int16_t));

                                        Bmemcpy(tmpwall, &wall[numwalls], n*sizeof(walltype));
                                        Bmemmove(&wall[begwalltomove+n], &wall[begwalltomove],
                                                 (numwalls-begwalltomove)*sizeof(walltype));
                                        Bmemcpy(&wall[begwalltomove], tmpwall, n*sizeof(walltype));

                                        Bfree(tmpwall);
                                        Bfree(tmponw);
                                    }
                                    numwalls = newnumwalls;
                                    newnumwalls = -1;

                                    mkonwinvalid();

                                    for (m=begwalltomove; m<begwalltomove+n; m++)
                                        if (checksectorpointer(m, refsect) > 0)
                                            if (onwwasvalid && onextwall[wall[m].nextwall]>=0)
                                            {
                                                initprintf("%d %d\n", m, onextwall[wall[m].nextwall]);
                                                copy_some_wall_members(m, onextwall[wall[m].nextwall], 0);
                                            }

                                    message("Attached new inner loop to sector %d", refsect);
                                }
                                
                            }
                        }

                        newnumwalls = -1;
                    }

                    highlightx1 = searchx;
                    highlighty1 = searchy;
                    highlightx2 = searchx;
                    highlighty2 = searchy;
                    highlightsectorcnt = 0;
                }
            }
            else
            {
                if (highlightsectorcnt == 0)
                {
                    int32_t add=keystatus[0x28], sub=(!add && keystatus[0x27]), setop=(add||sub);
                    int32_t tx,ty, pointsel = eitherCTRL;
#ifdef YAX_ENABLE
                    // home: ceilings, end: floors
                    int32_t fb, bunchsel = keystatus[0xcf] ? 1 : (keystatus[0xc7] ? 0 : -1);
                    uint8_t bunchbitmap[YAX_MAXBUNCHES>>3];
                    Bmemset(bunchbitmap, 0, sizeof(bunchbitmap));
#endif
                    if (!m32_sideview)
                    {
                        getpoint(highlightx1,highlighty1, &highlightx1,&highlighty1);
                        getpoint(highlightx2,highlighty2, &highlightx2,&highlighty2);
                    }

                    if (!pointsel)
                    {
                        if (highlightx1 > highlightx2)
                            swaplong(&highlightx1, &highlightx2);
                        if (highlighty1 > highlighty2)
                            swaplong(&highlighty1, &highlighty2);
                    }

                    if (!setop)
                        Bmemset(hlsectorbitmap, 0, sizeof(hlsectorbitmap));

                    for (i=0; i<numsectors; i++)
                    {
                        if (pointsel)
                        {
                            bad = (inside_editor(&pos, searchx,searchy, zoom, highlightx2, highlighty2, i)!=1);
                        }
                        else
                        {
                            bad = 0;
                            for (WALLS_OF_SECTOR(i, j))
                            {
                                if (!m32_sideview)
                                {
                                    tx = wall[j].x;
                                    ty = wall[j].y;
                                }
                                else
                                {
                                    screencoords(&tx,&ty, wall[j].x-pos.x,wall[j].y-pos.y, zoom);
                                    ty += getscreenvdisp(getflorzofslope(i, wall[j].x,wall[j].y)-pos.z, zoom);
                                    tx += halfxdim16;
                                    ty += midydim16;
                                }

                                if (tx < highlightx1 || tx > highlightx2) bad = 1;
                                if (ty < highlighty1 || ty > highlighty2) bad = 1;
                                if (bad == 1) break;
                            }
                        }

                        if (bad == 0)
                        {
#ifdef YAX_ENABLE
                            if (bunchsel!=-1 && (fb = yax_getbunch(i, YAX_FLOOR))>=0)
                            {
                                if ((sub || (graysectbitmap[i>>3]&(1<<(i&7)))==0) &&
                                        (bunchbitmap[fb>>3]&(1<<(fb&7)))==0)
                                {
                                    bunchbitmap[fb>>3] |= (1<<(fb&7));
                                    for (SECTORS_OF_BUNCH(fb, bunchsel, j))
                                        handlesecthighlight1(j, sub, 1);
                                }
                            }
                            else
#endif
                                handlesecthighlight1(i, sub, 0);
                        }
                    }

                    update_highlightsector();
                    ovh_whiteoutgrab();
                }
            }
        }

        if (((bstatus&1) < (oldmousebstatus&1)) && highlightsectorcnt < 0)  //after dragging
        {
            int32_t runi, numdelpoints=0;
            const char *errmsg;

            if (backup_drawn_walls(0))
                goto end_after_dragging;

            j = 1;
            for (i=0; i<highlightcnt; i++)
                if (pointhighlight == highlight[i])
                {
                    j = 0;
                    break;
                }

            if (j == 0)
            {
                for (i=0; i<highlightcnt; i++)
                    if ((highlight[i]&0xc000) == 16384)
                        updatesprite1(highlight[i]&16383);
            }
            else if ((pointhighlight&0xc000) == 16384)
            {
                updatesprite1(pointhighlight&16383);
            }

            if ((pointhighlight&0xc000) == 0)
            {
                dax = wall[pointhighlight].x;
                day = wall[pointhighlight].y;

                for (i=0; i<2; i++)
                {
                    if (dragwall[i] < 0)
                        break;

                    nlen[i] = wallength(dragwall[i]);

                    if (olen[i] != 0 && nlen[i] != 0)
                    {
                        int32_t nw = wall[dragwall[i]].nextwall;

                        j = divscale10(nlen[i], olen[i]);

                        k = getlenbyrep(olen[i], wall[dragwall[i]].xrepeat);
                        fixxrepeat(dragwall[i], k);
                        if (nw >= 0)
                        {
                            k = getlenbyrep(olen[i], wall[nw].xrepeat);
                            fixxrepeat(nw, k);
                        }
                    }
                }
            }
            else if ((pointhighlight&0xc000) == 16384)
            {
                dax = sprite[pointhighlight&16383].x;
                day = sprite[pointhighlight&16383].y;
            }

            dragwall[0] = dragwall[1] = -1;

            for (runi=0; runi<3; runi++)
                for (i=numwalls-1; i>=0; i--)  //delete points
                {
                    if (runi==0)
                        wall[i].cstat &= ~(1<<14);

                    if (wall[i].x == POINT2(i).x && wall[i].y == POINT2(i).y
                            && sector[sectorofwall(i)].wallnum > 3)
                    {
                        errmsg = deletepoint(i, runi);
                        if (errmsg)
                        {
                            message("%s", errmsg);
                            goto end_after_dragging;
                        }
                        else if (runi==2)
                            numdelpoints++;
                    }
                }

            if (numdelpoints)
            {
                if (numdelpoints > 1)
                    message("Deleted %d points.", numdelpoints);
                else
                    printmessage16("Point deleted.");
                asksave = 1;
            }
            else
            {
                for (i=0; i<numwalls; i++)     //make new red lines?
                {
                    if ((wall[i].x == dax && wall[i].y == day)
                        || (POINT2(i).x == dax && POINT2(i).y == day))
                    {
                        checksectorpointer(i, sectorofwall(i));
//                    fixrepeats(i);
                        asksave = 1;
                    }
                }
            }
#ifdef YAX_ENABLE
            yax_update(0);
            yax_updategrays(pos.z);
#endif
end_after_dragging:
            backup_drawn_walls(1);
        }

        if ((bstatus&1) > 0)                //drag points
        {
            if (highlightsectorcnt > 0)
            {
                if ((bstatus&1) > (oldmousebstatus&1))
                {
                    newnumwalls = -1;
                    sectorhighlightstat = -1;

//                    updatesector(mousxplc,mousyplc,&cursectorhighlight);
                    cursectorhighlight = -1;
                    for (i=0; i<highlightsectorcnt; i++)
                        if (inside_editor_curpos(highlightsector[i])==1)
                        {
                            cursectorhighlight = highlightsector[i];
                            break;
                        }

                    if (cursectorhighlight >= 0 && cursectorhighlight < numsectors)
                    {
                        //You clicked inside one of the flashing sectors!
                        sectorhighlightstat = 1;

                        dax = mousxplc;
                        day = mousyplc;
                        if (gridlock && grid > 0)
                            locktogrid(&dax, &day);

                        sectorhighlightx = dax;
                        sectorhighlighty = day;
                    }
                }
                else if (sectorhighlightstat == 1)
                {
                    dax = mousxplc;
                    day = mousyplc;
                    if (gridlock && grid > 0)
                        locktogrid(&dax, &day);

                    dax -= sectorhighlightx;
                    day -= sectorhighlighty;
                    sectorhighlightx += dax;
                    sectorhighlighty += day;
#ifdef YAX_ENABLE
                    if (!hl_all_bunch_sectors_p())
                        printmessage16("To drag extended sectors, all sectors of a bunch must be selected");
                    else
#endif
                    for (i=0; i<highlightsectorcnt; i++)
                    {
                        for (WALLS_OF_SECTOR(highlightsector[i], j))
                            { wall[j].x += dax; wall[j].y += day; }

                        for (j=headspritesect[highlightsector[i]]; j>=0; j=nextspritesect[j])
                            { sprite[j].x += dax; sprite[j].y += day; }
                    }

                    //for(i=0;i<highlightsectorcnt;i++)
                    //{
                    //   startwall = sector[highlightsector[i]].wallptr;
                    //   endwall = startwall+sector[highlightsector[i]].wallnum-1;
                    //   for(j=startwall;j<=endwall;j++)
                    //   {
                    //      if (wall[j].nextwall >= 0)
                    //         checksectorpointer(wall[j].nextwall,wall[j].nextsector);
                    //     checksectorpointer((short)j,highlightsector[i]);
                    //   }
                    //}
                    asksave = 1;
                }

            }
            else  //if (highlightsectorcnt <= 0)
            {
                if ((bstatus&1) > (oldmousebstatus&1))
                {
                    pointhighlight = getpointhighlight(mousxplc, mousyplc, pointhighlight);

                    if (pointhighlight >= 0 && (pointhighlight&0xc000)==0)
                    {
                        dragwall[0] = lastwall(pointhighlight);
                        dragwall[1] = pointhighlight;
                        olen[0] = wallength(dragwall[0]);
                        olen[1] = wallength(dragwall[1]);
                    }
                }

                if (pointhighlight >= 0 && (!m32_sideview || m32_sideelev>=32))
                {
                    if (m32_sideview)
                    {
                        int32_t dz;
                        if (pointhighlight>=16384)
                            dz = sprite[pointhighlight&16383].z - pos.z;
                        else
                            dz = getflorzofslope(sectorofwall(pointhighlight),
                                                 wall[pointhighlight].x, wall[pointhighlight].y) - pos.z;
                        getinvdisplacement(&dax,&day, -dz);
                        dax += mousxplc;
                        day += mousyplc;
                    }
                    else
                    {
                        dax = mousxplc;
                        day = mousyplc;
                    }

                    if (gridlock && grid > 0)
                        locktogrid(&dax, &day);

                    j = 1;
                    if (highlightcnt > 0)
                        for (i=0; i<highlightcnt; i++)
                            if (pointhighlight == highlight[i])
                                { j = 0; break; }

                    if (j == 0)
                    {
                        if ((pointhighlight&0xc000) == 0)
                        {
                            dax -= wall[pointhighlight].x;
                            day -= wall[pointhighlight].y;
                        }
                        else
                        {
                            dax -= sprite[pointhighlight&16383].x;
                            day -= sprite[pointhighlight&16383].y;
                        }

                        for (i=0; i<highlightcnt; i++)
                        {
                            if ((highlight[i]&0xc000) == 0)
                            {
                                wall[highlight[i]].x += dax;
                                wall[highlight[i]].y += day;
                            }
                            else
                            {
                                sprite[highlight[i]&16383].x += dax;
                                sprite[highlight[i]&16383].y += day;
                            }
                        }
                    }
                    else
                    {
                        if ((pointhighlight&0xc000) == 0)
                        {
                            if (newnumwalls >= numwalls &&
                                    wall[pointhighlight].x==firstx && wall[pointhighlight].y==firsty)
                            {
                                printmessage16("Can't drag point where drawing started.");
                                goto end_point_dragging;
                            }

                            dragpoint(pointhighlight,dax,day);
                            if ((unsigned)linehighlight < MAXWALLS)
                                wall[linehighlight].cstat |= (1<<14);
                            wall[lastwall(pointhighlight)].cstat |= (1<<14);
                        }
                        else if ((pointhighlight&0xc000) == 16384)
                        {
                            int32_t daspr=pointhighlight&16383, osec=sprite[daspr].sectnum;
                            vec3_t vec, ovec;

                            Bmemcpy(&ovec, (vec3_t *)&sprite[daspr], sizeof(vec3_t));
                            vec.x = dax;
                            vec.y = day;
                            vec.z = sprite[daspr].z;
                            if (setsprite(daspr, &vec) == -1 && osec>=0)
                                Bmemcpy(&sprite[daspr], &ovec, sizeof(vec3_t));
#if 0
                            daz = spriteheight(daspr, NULL);

                            for (i=0; i<numsectors; i++)
                                if (inside(dax,day,i) == 1)
                                    if (sprite[daspr].z >= getceilzofslope(i,dax,day))
                                        if (sprite[daspr].z-daz <= getflorzofslope(i,dax,day))
                                        {
                                            sprite[daspr].x = dax;
                                            sprite[daspr].y = day;
                                            if (sprite[daspr].sectnum != i)
                                                changespritesect(daspr,(int16_t)i);
                                            break;
                                        }
#endif
                        }
                    }
                    asksave = 1;
                }
            }
        }
        else //if ((bstatus&1) == 0)
        {
            pointhighlight = getpointhighlight(mousxplc, mousyplc, pointhighlight);
            sectorhighlightstat = -1;
        }
end_point_dragging:

        if (bstatus&(2|4))  // change arrow position
        {
            if (eitherCTRL)
            {
                int16_t cursectornum;

                for (cursectornum=0; cursectornum<numsectors; cursectornum++)
                    if (inside_editor_curpos(cursectornum) == 1)
                        break;

                if (cursectornum < numsectors)
                {
                    if (pointhighlight >= 16384)
                        ExtEditSpriteData(pointhighlight-16384);
                    else if ((linehighlight >= 0) && (bstatus&1 || sectorofwall(linehighlight) == cursectornum))
                        ExtEditWallData(linehighlight);
                    else if (cursectornum >= 0)
                        ExtEditSectorData(cursectornum);
                }

                bstatus &= ~6;
            }
            else
            {
                if (m32_sideview && (bstatus&4))
                {
                    pos.z += divscale18(searchy-midydim16,zoom);
                    getpoint(searchx,midydim16, &pos.x, &pos.y);
#ifdef YAX_ENABLE
                    yax_updategrays(pos.z);
#endif
                }
                else
                {
                    pos.x = mousxplc;
                    pos.y = mousyplc;
                }

                if (m32_sideview)
                {
                    int32_t opat=drawlinepat;

                    y1 = INT32_MAX;

                    for (i=0; i<numsectors; i++)
                    {
                        if (inside(pos.x, pos.y, i)==1)
                        {
                            day = getscreenvdisp(getflorzofslope(i, pos.x, pos.y)-pos.z, zoom);

                            x2 = max(4, mulscale14(64,zoom));
                            y2 = scalescreeny(x2);

                            if (klabs(day) < y1)
                                y1 = day;

                            drawline16base(halfxdim16, midydim16+day, -x2,-y2, x2,y2, editorcolors[14]);
                            drawline16base(halfxdim16, midydim16+day, -x2,y2, x2,-y2, editorcolors[14]);
                        }
                    }

                    drawlinepat = 0x11111111;
                    if (y1 != INT32_MAX)
                        drawline16base(halfxdim16,midydim16, 0,0, 0,y1, editorcolors[14]);
//                    else
//                        drawline16base(halfxdim16,midydim16, 0,0, 0,getscreenvdisp(-pos.z, zoom), editorcolors[14]);
                    drawlinepat = opat;
                }

                searchx = halfxdim16;
                searchy = midydim16;
            }
        }
        else if ((oldmousebstatus&6) > 0)
            updatesectorz(pos.x,pos.y,pos.z,&cursectnum);

        if (circlewall != -1 && (keystatus[0x4a] || ((bstatus&32) && !eitherCTRL)))  // -, mousewheel down
        {
            if (circlepoints > 1)
                circlepoints--;
            keystatus[0x4a] = 0;
            mouseb &= ~32;
            bstatus &= ~32;
        }
        if (circlewall != -1 && (keystatus[0x4e] || ((bstatus&16) && !eitherCTRL)))  // +, mousewheel up
        {
            if (circlepoints < 63)
                circlepoints++;
            keystatus[0x4e] = 0;
            mouseb &= ~16;
            bstatus &= ~16;
        }

        if (keystatus[0x3d])  // F3
        {
            keystatus[0x3d]=0;
            if (!m32_sideview && (newnumwalls>=0 || joinsector[0]>=0 || circlewall>=0 || (bstatus&1)))
                message("Must not be editing map while switching to side view mode.");
            else
            {
                m32_sideview = !m32_sideview;
                printmessage16("Side view %s", m32_sideview?"enabled":"disabled");

                m32_setkeyfilter(1);
            }
        }

        if (m32_sideview && (keystatus[0x10] || keystatus[0x11]))
        {
            if (eitherCTRL)
            {
                if (m32_sideang&63)
                {
                    m32_sideang += (1-2*keystatus[0x10])*(1-2*sideview_reversehrot)*32;
                    m32_sideang &= (2047&~63);
                }
                else
                {
                    m32_sideang += (1-2*keystatus[0x10])*(1-2*sideview_reversehrot)*64;
                    m32_sideang &= 2047;
                }

                keystatus[0x10] = keystatus[0x11] = 0;
            }
            else
            {
                m32_sideang += (1-2*keystatus[0x10])*(1-2*sideview_reversehrot)*synctics<<(eitherSHIFT*2);
                m32_sideang &= 2047;
            }
            _printmessage16("Sideview angle: %d", (int32_t)m32_sideang);
        }

        if (m32_sideview && (eitherSHIFT || (bstatus&(16|32))))
        {
            if ((DOWN_BK(MOVEUP) || (bstatus&16)) && m32_sideelev < 512)
            {
                m32_sideelev += synctics<<(1+!!(bstatus&16));
                if (m32_sideelev > 512)
                    m32_sideelev = 512;
                _printmessage16("Sideview elevation: %d", m32_sideelev);
            }
            if ((DOWN_BK(MOVEDOWN) || (bstatus&32)) && m32_sideelev > 0)
            {
                m32_sideelev -= synctics<<(1+!!(bstatus&32));
                if (m32_sideelev < 0)
                    m32_sideelev = 0;
                _printmessage16("Sideview elevation: %d", m32_sideelev);
            }
        }
        else
        {
            int32_t didzoom=0;

            if ((DOWN_BK(MOVEUP) || (bstatus&16)) && zoom < 65536)
            {
                zoom += synctics*(zoom>>4);
                if (zoom < 24) zoom += 2;
                didzoom = 1;
            }
            if ((DOWN_BK(MOVEDOWN) || (bstatus&32)) && zoom > 8)
            {
                zoom -= synctics*(zoom>>4);
                didzoom = 1;
            }

            if (didzoom)
            {
                if (eitherALT)
                {
                    searchx = halfxdim16;
                    searchy = midydim16;
                    pos.x = mousxplc;
                    pos.y = mousyplc;
                }
                zoom = clamp(zoom, 8, 65536);
                _printmessage16("Zoom: %d",zoom);
            }
        }

        if (keystatus[0x22])  // G (grid on/off)
        {
            keystatus[0x22] = 0;
            grid++;
            if (grid == 7) grid = 0;
        }
        if (keystatus[0x26])  // L (grid lock)
        {
            keystatus[0x26] = 0;
            gridlock = !gridlock;
            printmessage16("Grid locking %s", gridlock?"on":"off");
        }

        if (keystatus[0x24])  // J (join sectors)
        {
            keystatus[0x24] = 0;

            if (newnumwalls >= 0)
            {
                printmessage16("Can't join sectors while editing.");
                goto end_join_sectors;
            }

#ifdef YAX_ENABLE
            if (highlightsectorcnt > 0 && eitherCTRL)
            {
                // [component][ceiling(0) or floor(1)]
                // compstat: &1: "has extension", &2: "differ in z", &4: "sloped", -1: "uninited"
                int32_t cf, comp, compstat[2][2]={{-1,-1},{-1,-1}}, compcfz[2][2];

                // joinstat: join what to what?
                //  &1: ceil(comp 0) <-> flor(comp 1),  &2: flor(comp 0) <-> ceil(comp 1)
                //  (doesn't yet say which is stationary)
                // movestat: which component can be displaced?
                //  &1: first,  &2: second
                int32_t askres, joinstat, needsdisp, moveonwp;
                int32_t movestat, dx=0,dy=0,dz, delayerr=0;

                // tempxyar: int32_t [MAXWALLS][2]
                int32_t numouterwalls[2] = {0,0}, numowals;
                static int16_t outerwall[2][MAXWALLS];
                const walltype *wal0, *wal1;

                // join sector ceilings/floors to a new bunch
                if (numyaxbunches==YAX_MAXBUNCHES)
                {
                    message("Bunch limit of %d reached, cannot join", YAX_MAXBUNCHES);
                    goto end_join_sectors;
                }

                // first, see whether we have exactly two connected components
                // wrt wall[].nextsector
                if (highlighted_sectors_components(0,0) != 2)
                {
                    message("Sectors must be partitioned in two components to join");
                    goto end_join_sectors;
                }

                for (k=0; k<highlightsectorcnt; k++)
                {
                    j = highlightsector[k];
                    comp = !!(collsectbitmap[1][j>>3]&(1<<(j&7)));

                    for (cf=0; cf<2; cf++)
                    {
                        if (compstat[comp][cf]==-1)
                        {
                            compstat[comp][cf] = 0;
                            compcfz[comp][cf] = SECTORFLD(j,z, cf);
                        }

                        if (yax_getbunch(j, cf)>=0)
                            compstat[comp][cf] |= 1;
                        if (SECTORFLD(j,z, cf) != compcfz[comp][cf])
                            compstat[comp][cf] |= 2;
                        if (SECTORFLD(j,stat, cf)&2)
                            compstat[comp][cf] |= 4;

                        compcfz[comp][cf] = SECTORFLD(j,z, cf);
                    }
                }

                // check for consistency
                joinstat = 0;
                if (!compstat[0][YAX_CEILING] && !compstat[1][YAX_FLOOR])
                    joinstat |= 1;
                if (!compstat[0][YAX_FLOOR] && !compstat[1][YAX_CEILING])
                    joinstat |= 2;

                if (joinstat==0)
                {
                    message("No consistent joining combination found");
                    //for (i=0; i<2; i++) for (j=0; j<2; j++) message("%d", compstat[i][j]);
                    goto end_join_sectors;
                }
                if (joinstat==3)
                {
                    if (compcfz[0][YAX_CEILING] != compstat[1][YAX_FLOOR])
                        joinstat &= 1;
                    if (compcfz[0][YAX_CEILING] != compstat[1][YAX_FLOOR])
                        joinstat &= 2;

                    if (joinstat == 0)
                        joinstat = 3;  // we couldn't disambiguate
                }

                for (comp=0; comp<2; comp++)
                    for (k=0; k<collnumsects[comp]; k++)
                    {
                        i = collsectlist[comp][k];
                        for (WALLS_OF_SECTOR(i, j))
                            if (wall[j].nextwall < 0)
                                outerwall[comp][numouterwalls[comp]++] = j;
                    }

                if (numouterwalls[0] != numouterwalls[1])
                {
                    message("Number of outer walls must be equal for both components");
                    if (numouterwalls[0]>0 && numouterwalls[1]>0)
                        delayerr = 1;
                    else
                        goto end_join_sectors;
                }
                numowals = min(numouterwalls[0], numouterwalls[1]);

                // now sort outer walls 'geometrically'
                for (comp=0; comp<2; comp++)
                    sort_walls_geometrically(outerwall[comp], numouterwalls[comp]);

                for (k=0; k<numowals; k++)
                {
                    wal0 = &wall[outerwall[0][k]];
                    wal1 = &wall[outerwall[1][k]];

                    if (k==0)
                    {
                        dx = wal1->x - wal0->x;
                        dy = wal1->y - wal0->y;
                    }

                    if (wal1->x - wal0->x != dx || wal1->y - wal0->y != dy ||
                            wall[wal1->point2].x - wall[wal0->point2].x != dx ||
                            wall[wal1->point2].y - wall[wal0->point2].y != dy)
                    {
                        pos.x = wal0->x;
                        pos.y = wal0->y;
                        pos.z = getflorzofslope(sectorofwall(wal0-wall), pos.x, pos.y);

                        message("Outer wall coordinates must coincide for both components");
                        OSD_Printf("wal0:%d (%d,%d)--(%d,%d)\n",(int)(wal0-wall), wal0->x,wal0->y,
                                   wall[wal0->point2].x,wall[wal0->point2].y);
                        OSD_Printf("wal1:%d (%d,%d)--(%d,%d)\n",(int)(wal1-wall), wal1->x,wal1->y,
                                   wall[wal1->point2].x,wall[wal1->point2].y);

                        goto end_join_sectors;
                    }
                }

                if (delayerr)
                    goto end_join_sectors;

                if (joinstat == 3)
                {
                    char askchars[2] = {'1', 'v'};

                    // now is a good time to ask...
                    for (comp=0; comp<2; comp++)
                        for (k=0; k<collnumsects[comp]; k++)
                            fillsector(collsectlist[comp][k], comp==0 ? 159 : editorcolors[11]);

                    fade_editor_screen(editorcolors[11] | (159<<8));
                    askres = editor_ask_function("Connect yellow ceil w/ blue floor (1) or (v)ice versa?", askchars, 2);
                    if (askres==-1)
                        goto end_join_sectors;
                    joinstat &= (1<<askres);
                }

                joinstat--;  // 0:ceil(0)<->flor(1), 1:ceil(1)<->flor(0)

                dz = compcfz[1][!joinstat] - compcfz[0][joinstat];
                needsdisp = (dx || dy || dz);

                if (needsdisp)
                {
                    // a component is more likely to be displaced if it's not
                    // extended on the non-joining side
                    movestat = (!(compstat[0][!joinstat]&1)) | ((!(compstat[1][joinstat]&1))<<1);
                    if (!movestat)
                    {
                        movestat = 3;
//                        message("Internal error while TROR-joining: movestat inconsistent!");
//                        goto end_join_sectors;
                    }

                    if (movestat==3)
                    {
                        char askchars[2] = {'y', 'b'};

                        for (comp=0; comp<2; comp++)
                            for (k=0; k<collnumsects[comp]; k++)
                                fillsector(collsectlist[comp][k], comp==0 ? 159 : editorcolors[11]);

                        fade_editor_screen(editorcolors[11] | (159<<8));
                        askres = editor_ask_function("Move (y)ellow or (b)lue component?", askchars, 2);
                        if (askres==-1)
                            goto end_join_sectors;
                        movestat &= (1<<askres);
                    }

                    movestat--;  // 0:move 1st, 1:move 2nd component
                    if (movestat==1)
                        dx*=-1, dy*=-1, dz*=-1;

                    moveonwp = 0;
                    if (onwisvalid())
                    {
                        static int16_t ocollsectlist[MAXSECTORS];
                        static uint8_t tcollbitmap[MAXSECTORS>>3];
                        int16_t ocollnumsects=collnumsects[movestat], tmpsect;

                        Bmemcpy(ocollsectlist, collsectlist[movestat], ocollnumsects*sizeof(int16_t));
                        Bmemset(tcollbitmap, 0, sizeof(tcollbitmap));

                        for (k=0; k<ocollnumsects; k++)
                            for (WALLS_OF_SECTOR(ocollsectlist[k], j))
                            {
                                if (onextwall[j] < 0)
                                    continue;

                                tmpsect = sectorofwall(onextwall[j]);
                                sectors_components(1, &tmpsect, 1,0);

                                for (m=0; m<(numsectors+7)>>3; m++)
                                    tcollbitmap[m] |= collsectbitmap[0][m];
                                moveonwp = 1;
                            }

                        if (moveonwp)
                        {
                            int32_t movecol = movestat==0 ? 159 : editorcolors[11];
                            for (i=0; i<numsectors; i++)
                                if (tcollbitmap[i>>3]&(1<<(i&7)))
                                    fillsector(i, editorcolors[12]);

                            fade_editor_screen(editorcolors[12] | (movecol<<8));
                            moveonwp = ask_if_sure("Also move formerly wall-connected sectors?",0);
                            if (moveonwp==-1)
                                goto end_join_sectors;
                        }
                    }

                    // now need to collect them wrt. the nextsector but also
                    // the yax-nextsector relation
                    if (highlighted_sectors_components(1,moveonwp) != 2)
                    {
                        message("Must not have TROR connections between the two components");
                        goto end_join_sectors;
                    }

                    // displace!
                    for (k=0; k<collnumsects[movestat]; k++)
                    {
                        i = collsectlist[movestat][k];

                        sector[i].floorz += dz;
                        sector[i].ceilingz += dz;

                        for (WALLS_OF_SECTOR(i, j))
                        {
                            wall[j].x += dx;
                            wall[j].y += dy;
                        }

                        for (j=headspritesect[i]; j>=0; j=nextspritesect[j])
                        {
                            sprite[j].x += dx;
                            sprite[j].y += dy;
                            sprite[j].z += dz;
                        }
                    }

                    // restore old components, i.e. only the bunch sectors
                    highlighted_sectors_components(0,0);

                }  // end if (needsdisp)

                /*** construct the YAX connection! ***/
                for (comp=0; comp<2; comp++)
                {
                    // walls
                    for (j=0; j<numowals; j++)
                        yax_setnextwall(outerwall[comp][j], comp^joinstat, outerwall[!comp][j]);

                    // sectors
                    for (k=0; k<collnumsects[comp]; k++)
                    {
                        i = collsectlist[comp][k];
                        yax_setbunch(i, comp^joinstat, numyaxbunches);
                        SECTORFLD(i,stat, comp^joinstat) &= ~1;  // no plax

                        // restore red walls AFTER setting nextwalls
                        // (see checksectorpointer() for why)
                        for (WALLS_OF_SECTOR(i, j))
                            if (wall[j].nextwall < 0)
                                checksectorpointer(j, i);
                    }
                }

                Bmemset(hlsectorbitmap, 0, sizeof(hlsectorbitmap));
                update_highlightsector();

                yax_update(0);
                yax_updategrays(pos.z);

                message("Joined highlighted sectors to new bunch %d", numyaxbunches);
                asksave = 1;
            }
            else
#endif  // defined YAX_ENABLE
            if (joinsector[0] < 0)
            {
                joinsector[0] = -1;
                for (i=0; i<numsectors; i++)
                {
                    YAX_SKIPSECTOR(i);
                    if (inside_editor_curpos(i) == 1)
                    {
                        joinsector[0] = i;
                        printmessage16("Join sector - press J again on sector to join with.");
                        break;
                    }
                }
                goto end_join_sectors;
            }
            else
            {
                int16_t joink;
#ifdef YAX_ENABLE
                int16_t cb0,fb0, cb1,fb1;
#endif
                joinsector[1] = -1;

                for (i=0; i<numsectors; i++)
                {
                    YAX_SKIPSECTOR(i);

                    if (inside_editor_curpos(i) == 1)
                    {
                        startwall = sector[i].wallptr;
                        endwall = startwall + sector[i].wallnum;
                        for (j=startwall; j<endwall; j++)
                            if (wall[j].nextsector == joinsector[0])
                                break;

                        joinsector[1] = i;
#ifdef YAX_ENABLE
                        yax_getbunches(joinsector[0], &cb0, &fb0);
                        yax_getbunches(joinsector[1], &cb1, &fb1);
#endif
                        // pressing J into the same sector is the same as saying 'no'
                        //                  v----------------v
                        if (j == endwall && i != joinsector[0])
                        {
#ifdef YAX_ENABLE
                            if (cb0>=0 || fb0>=0 || cb1>=0 || fb1>=0)
                            {
                                message("Joining non-adjacent extended sectors not allowed!");
                                joinsector[0] = joinsector[1] = -1;
                                goto end_join_sectors;
                            }
#endif
                            {
                                fillsector(i, editorcolors[9]);
                                fillsector(joinsector[0], editorcolors[9]);
                                fade_editor_screen(editorcolors[9]);

                                if (!ask_if_sure("Join non-adjacent sectors? (Y/N)", 0))
                                    joinsector[1] = joinsector[0];
                            }
                        }
#ifdef YAX_ENABLE
                        if (cb0!=cb1 || fb0!=fb1)
                        {
                            message("Joining of extended sectors with different bunches not allowed!");
                            joinsector[0] = joinsector[1] = -1;
                            goto end_join_sectors;                            
                        }
#endif
                        break;
                    }
                }

                if (joinsector[1] < 0 || joinsector[0] == joinsector[1])
                {
                    printmessage16("No sectors joined.");
                    joinsector[0] = -1;
                    goto end_join_sectors;
                }


                for (i=0; i<numwalls; i++)
                    wall[i].cstat &= ~(1<<14);

                newnumwalls = numwalls;

                for (k=0; k<2; k++)
                {
                    for (WALLS_OF_SECTOR(joinsector[k], j))
                    {
                        int32_t loopnum=MAXWALLS*2;

                        if (wall[j].cstat & (1<<14))
                            continue;

                        if (wall[j].nextsector == joinsector[1-k])
                        {
                            wall[j].cstat |= (1<<14);
                            continue;
                        }

                        i = j;
                        m = newnumwalls;
                        joink = k;
                        do
                        {
                            if (newnumwalls >= MAXWALLS + M32_FIXME_WALLS)
                            {
                                message("Joining sectors failed: not enough space beyond wall[]");
                                joinsector[0] = -1;
                                newnumwalls = -1;

                                for (i=0; i<numwalls; i++)
                                    wall[i].cstat &= ~(1<<14);

                                goto end_join_sectors;
                            }

                            Bmemcpy(&wall[newnumwalls], &wall[i], sizeof(walltype));
#ifdef YAX_ENABLE
                            yax_fixreverselinks(newnumwalls, newnumwalls);
#endif
                            wall[newnumwalls].point2 = newnumwalls+1;
                            newnumwalls++;

                            wall[i].cstat |= (1<<14);

                            i = wall[i].point2;
                            if (wall[i].nextsector == joinsector[1-joink])
                            {
                                i = NEXTWALL(i).point2;
                                joink = 1 - joink;
                            }

                            loopnum--;
                        }
                        while (loopnum>0 && ((wall[i].cstat & (1<<14))==0)
                                   && (wall[i].nextsector != joinsector[1-joink]));

                        wall[newnumwalls-1].point2 = m;

                        if (loopnum==0)
                        {
                            message("internal error while joining sectors: infloop!");
                            newnumwalls = -1;
                        }
                    }
                }

                if (newnumwalls > numwalls)
                {
                    Bmemcpy(&sector[numsectors], &sector[joinsector[0]], sizeof(sectortype));
                    sector[numsectors].wallptr = numwalls;
                    sector[numsectors].wallnum = newnumwalls-numwalls;

                    //fix sprites
                    for (i=0; i<2; i++)
                    {
                        j = headspritesect[joinsector[i]];
                        while (j != -1)
                        {
                            k = nextspritesect[j];
                            changespritesect(j, numsectors);
                            j = k;
                        }
                    }

                    numsectors++;

                    for (i=numwalls; i<newnumwalls; i++)
                    {
                        if (wall[i].nextwall >= 0)
                        {
                            NEXTWALL(i).nextwall = i;
                            NEXTWALL(i).nextsector = numsectors-1;
                        }
                    }

                    numwalls = newnumwalls;
                    newnumwalls = -1;

                    for (k=0; k<2; k++)
                        for (WALLS_OF_SECTOR(joinsector[k], j))
                            wall[j].nextwall = wall[j].nextsector = -1;

                    deletesector(joinsector[0]);
                    if (joinsector[0] < joinsector[1])
                        joinsector[1]--;
                    deletesector(joinsector[1]);

                    printmessage16("Sectors joined.");
                    mkonwinvalid();
                    asksave = 1;
#ifdef YAX_ENABLE
                    yax_update(0);
                    yax_updategrays(pos.z);
#endif
                }

                joinsector[0] = -1;
            }
        }
end_join_sectors:

// PK
        for (i=0x02; i<=0x0b; i++)  // keys '1' to '0' on the upper row
            if (keystatus[i])
            {
                prefixarg = prefixtiles[i-2];
                break;
            }

        if (eitherALT && keystatus[0x1f]) //ALT-S
        {
            keystatus[0x1f] = 0;

            if (linehighlight >= 0 && wall[linehighlight].nextwall == -1)
            {
                newnumwalls = whitelinescan(sectorofwall(linehighlight), linehighlight);
                if (newnumwalls < numwalls)
                {
                    printmessage16("Can't make a sector out there.");
                    newnumwalls = -1;
                }
                else if (newnumwalls > MAXWALLS)
                {
                    printmessage16("Making new sector from inner loop would exceed wall limits.");
                    newnumwalls = -1;
                }
                else
                {
                    for (i=numwalls; i<newnumwalls; i++)
                    {
                        NEXTWALL(i).nextwall = i;
                        NEXTWALL(i).nextsector = numsectors;
                    }
#ifdef YAX_ENABLE
                    yax_setbunches(numsectors, -1, -1);
                    yax_update(0);
                    yax_updategrays(pos.z);
#endif
                    numwalls = newnumwalls;
                    newnumwalls = -1;
                    numsectors++;

                    printmessage16("Inner loop made into new sector.");
                }
            }
        }
        else if (keystatus[0x1f])  //S
        {
            keystatus[0x1f] = 0;

            sucksect = -1;
            for (i=0; i<numsectors; i++)
            {
                YAX_SKIPSECTOR(i);
                if (inside_editor_curpos(i) == 1)
                {
                    sucksect = i;
                    break;
                }
            }

            if (sucksect >= 0)
            {
                dax = mousxplc;
                day = mousyplc;
                if (gridlock && grid > 0)
                    locktogrid(&dax, &day);

                i = insert_sprite_common(sucksect, dax, day);

                if (i < 0)
                    printmessage16("Couldn't insert sprite.");
                else
                {
                    sprite[i].z = getflorzofslope(sucksect,dax,day);
// PK
                    if (prefixarg)
                    {
                        sprite[i].picnum = prefixarg;
                        sprite[i].xrepeat = sprite[i].yrepeat = 48;
                        prefixarg = 0;
                    }
                    else if (somethingintab == 3)
                    {
                        sprite[i].picnum = temppicnum;
                        if (tilesizx[temppicnum] <= 0 || tilesizy[temppicnum] <= 0)
                        {
                            j = 0;
                            for (k=0; k<MAXTILES; k++)
                                if (tilesizx[k] > 0 && tilesizy[k] > 0)
                                {
                                    j = k;
                                    break;
                                }
                            sprite[i].picnum = j;
                        }
                        sprite[i].shade = tempshade;
                        sprite[i].pal = temppal;
                        sprite[i].xrepeat = max(tempxrepeat, 1);
                        sprite[i].yrepeat = max(tempyrepeat, 1);
                        sprite[i].cstat = tempcstat;
                    }

                    if (tilesizy[sprite[i].picnum] >= 32)
                        sprite[i].cstat |= 1;

                    correct_sprite_yoffset(i);

                    printmessage16("Sprite inserted.");
                    updatenumsprites();
                    asksave = 1;

                    VM_OnEvent(EVENT_INSERTSPRITE2D, i);
                }
            }
        }

        if (keystatus[0x2e])  // C (make circle of points)
        {
            if (highlightsectorcnt > 0)
                duplicate_selected_sectors();
            else if (highlightcnt > 0)
                duplicate_selected_sprites();

            else if (circlewall >= 0)
            {
                circlewall = -1;
            }
            else
            {
                if (linehighlight >= 0)
                {
#ifdef YAX_ENABLE
                    if (yax_islockedwall(sectorofwall(linehighlight), linehighlight))
                        printmessage16("Can't make circle in wall constrained by sector extension.");
                    else
#endif
                    circlewall = linehighlight;
                }
            }
            keystatus[0x2e] = 0;
        }

        bad = keystatus[0x39];  //Gotta do this to save lots of 3 spaces!

        if (circlewall >= 0)
        {
            x1 = wall[circlewall].x;
            y1 = wall[circlewall].y;
            x2 = POINT2(circlewall).x;
            y2 = POINT2(circlewall).y;
            x3 = mousxplc;
            y3 = mousyplc;
            adjustmark(&x3,&y3,newnumwalls);
            tempint1 = dmulscale4(x3-x2,x1-x3, y1-y3,y3-y2);
            tempint2 = dmulscale4(y1-y2,x1-x3, y1-y3,x2-x1);

            if (tempint2 != 0)
            {
                int32_t ps = 2, goodtogo;  // pointsize

                centerx = ((x1+x2) + scale(y1-y2,tempint1,tempint2))>>1;
                centery = ((y1+y2) + scale(x2-x1,tempint1,tempint2))>>1;

                dax = mulscale14(centerx-pos.x,zoom);
                day = mulscale14(centery-pos.y,zoom);
                drawline16base(halfxdim16+dax,midydim16+day, -2,-2, +2,+2, editorcolors[14]);
                drawline16base(halfxdim16+dax,midydim16+day, -2,+2, +2,-2, editorcolors[14]);

                circleang1 = getangle(x1-centerx,y1-centery);
                circleang2 = getangle(x2-centerx,y2-centery);

                circleangdir = 1;
                k = ((circleang2-circleang1)&2047);
                if (mulscale4(x3-x1,y2-y1) < mulscale4(x2-x1,y3-y1))
                {
                    circleangdir = -1;
                    k = -((circleang1-circleang2)&2047);
                }

                circlerad = ksqrt(dmulscale4(centerx-x1,centerx-x1, centery-y1,centery-y1))<<2;
                goodtogo = (numwalls+circlepoints <= MAXWALLS);

                for (i=circlepoints; i>0; i--)
                {
                    j = (circleang1 + scale(i,k,circlepoints+1))&2047;
                    dax = centerx + mulscale14(sintable[(j+512)&2047],circlerad);
                    day = centery + mulscale14(sintable[j],circlerad);

                    inpclamp(&dax, -editorgridextent, editorgridextent);
                    inpclamp(&day, -editorgridextent, editorgridextent);

                    if (bad > 0 && goodtogo)
                    {
                        insertpoint(circlewall,dax,day);
                        if (wall[circlewall].nextwall >= 0 && wall[circlewall].nextwall < circlewall)
                            circlewall++;
                    }

                    dax = mulscale14(dax-pos.x,zoom);
                    day = mulscale14(day-pos.y,zoom);
                    drawline16base(halfxdim16+dax,midydim16+day, -ps,-ps, +ps,-ps, editorcolors[14]);
                    drawline16base(halfxdim16+dax,midydim16+day, +ps,-ps, +ps,+ps, editorcolors[14]);
                    drawline16base(halfxdim16+dax,midydim16+day, +ps,+ps, -ps,+ps, editorcolors[14]);
                    drawline16base(halfxdim16+dax,midydim16+day, -ps,+ps, -ps,-ps, editorcolors[14]);
//                    drawcircle16(halfxdim16+dax, midydim16+day, 3, 14);
                }

                if (bad > 0)
                {
                    bad = 0;
                    keystatus[0x39] = 0;

                    if (goodtogo)
                    {
                        asksave = 1;
                        printmessage16("Circle points inserted.");
                        circlewall = -1;

                        mkonwinvalid();
                    }
                    else
                        printmessage16("Inserting circle points would exceed wall limit.");
                }
            }
        }

        if (bad > 0)   //Space bar test
        {
            keystatus[0x39] = 0;
            adjustmark(&mousxplc,&mousyplc,newnumwalls);

            if (checkautoinsert(mousxplc,mousyplc,newnumwalls) == 1)
            {
                printmessage16("You must insert a point there first.");
                bad = 0;
            }
        }

        if (bad > 0)  //Space
        {
            if (newnumwalls < numwalls)  // starting wall drawing
            {
                if (numwalls >= MAXWALLS-1)
                {
                    // whatever we do, we will need at least two new walls
                    printmessage16("Can't start sector drawing: wall limit reached.");
                    goto end_space_handling;
                }

                if (numsectors >= MAXSECTORS)
                {
                    printmessage16("Can't start sector drawing: sector limit reached.");
                    goto end_space_handling;
                }

                firstx = mousxplc;
                firsty = mousyplc;  //Make first point
                newnumwalls = numwalls;
                suckwall = -1;
                split = 0;

                init_new_wall1(&suckwall, &mousxplc, &mousyplc);

                printmessage16("Sector drawing started.");
            }
            else  // 2nd point and up...
            {
                //if not back to first point
                if (firstx != mousxplc || firsty != mousyplc)  //nextpoint
                {
                    if (newnumwalls>=MAXWALLS)
                    {
                        printmessage16("Inserting another point would exceed wall limit.");
                        goto end_space_handling;
                    }

                    j = 0;
                    for (i=numwalls; i<newnumwalls; i++)
                        if (mousxplc == wall[i].x && mousyplc == wall[i].y)
                            j = 1;

                    if (j == 0)  // if new point is not on a position of already drawn points
                    {
                        // on the second point insertion, check if starting to split a sector
                        if (newnumwalls == numwalls+1)
                        {
                            dax = (wall[numwalls].x+mousxplc)>>1;
                            day = (wall[numwalls].y+mousyplc)>>1;

                            for (i=0; i<numsectors; i++)
                            {
                                YAX_SKIPSECTOR(i);
                                if (inside(dax,day,i) != 1)
                                    continue;

                                //check if first point at point of sector
                                m = -1;
                                for (WALLS_OF_SECTOR(i, k))
                                    if (wall[k].x==wall[numwalls].x && wall[k].y==wall[numwalls].y)
                                    {
                                        YAX_SKIPWALL(k);
                                        if (wall[k].nextwall >= 0)
                                            YAX_SKIPWALL(wall[k].nextwall);

                                        m = k;
                                        break;
                                    }

                                // if the second insertion is not on a neighboring point of the first one...
                                if (m>=0 && (POINT2(k).x != mousxplc || POINT2(k).y != mousyplc))
                                    if (wall[lastwall(k)].x != mousxplc || wall[lastwall(k)].y != mousyplc)
                                    {
                                        split = 1;
                                        splitsect = i;
                                        splitstartwall = m;
                                        break;
                                    }
                            }
                        }

                        //make new point

                        //make sure not drawing over old red line
                        bad = 0;
                        for (i=0; i<numwalls; i++)
                        {
                            YAX_SKIPWALL(i);

                            if (wall[i].nextwall >= 0)
                            {
                                int32_t lastwalx = wall[newnumwalls-1].x;
                                int32_t lastwaly = wall[newnumwalls-1].y;

                                YAX_SKIPWALL(wall[i].nextwall);

                                if (wall[i].x == mousxplc && wall[i].y == mousyplc)
                                    if (POINT2(i).x == lastwalx && POINT2(i).y == lastwaly)
                                        bad = 1;
                                if (wall[i].x == lastwalx && wall[i].y == lastwaly)
                                    if (POINT2(i).x == mousxplc && POINT2(i).y == mousyplc)
                                        bad = 1;
                            }
                        }

                        if (bad == 0)
                        {
                            init_new_wall1(&suckwall, &mousxplc, &mousyplc);
                        }
                        else
                        {
                            printmessage16("You can't draw new lines over red lines.");
                            goto end_space_handling;
                        }
                    }
                }

                ////////// newnumwalls is at most MAXWALLS here //////////

                //if not split and back to first point
                if (!split && newnumwalls >= numwalls+3
                        && firstx==mousxplc && firsty==mousyplc)
                {
                    wall[newnumwalls-1].point2 = numwalls;

                    if (suckwall == -1)  //if no connections to other sectors
                    {
                        k = -1;
                        for (i=0; i<numsectors; i++)
                        {
                            YAX_SKIPSECTOR(i);

                            if (inside(firstx,firsty,i) == 1)
                            {
                                // if all points inside that one sector i,
                                // will add an inner loop
                                for (j=numwalls+1; j<newnumwalls; j++)
                                {
                                    if (inside(wall[j].x, wall[j].y, i) == 0)
                                        goto check_next_sector;
                                }

                                k = i;
                                break;
                            }
check_next_sector: ;
                        }

                        if (k == -1)   //if not inside another sector either
                        {
                            //add island sector
                            if (clockdir(numwalls) == 1)
                                flipwalls(numwalls,newnumwalls);

                            Bmemset(&sector[numsectors], 0, sizeof(sectortype));
                            sector[numsectors].extra = -1;

                            sector[numsectors].wallptr = numwalls;
                            sector[numsectors].wallnum = newnumwalls-numwalls;
                            sector[numsectors].ceilingz = -(32<<8);
                            sector[numsectors].floorz = (32<<8);

                            for (i=numwalls; i<newnumwalls; i++)
                                copy_some_wall_members(i, -1, 1);
#ifdef YAX_ENABLE
                            yax_setbunches(numsectors, -1, -1);
#endif
                            headspritesect[numsectors] = -1;
                            numsectors++;

                            numwalls = newnumwalls;
                            newnumwalls = -1;

                            printmessage16("Created new sector %d", numsectors-1);
                        }
                        else       //else add loop to sector
                        {
#ifdef YAX_ENABLE
                            int16_t cbunch, fbunch;
                            int32_t extendedSector, newnumwalls2;

                            yax_getbunches(k, &cbunch, &fbunch);
                            extendedSector = (cbunch>=0 || fbunch>=0);
#endif
                            j = newnumwalls-numwalls;
#ifdef YAX_ENABLE
                            newnumwalls2 = newnumwalls + j;
                            if (extendedSector)
                            {
                                if ((cbunch>=0 && (sector[k].ceilingstat&2))
                                        || (fbunch>=0 && (sector[k].floorstat&2)))
                                {
                                    message("Sloped extended sectors cannot be subdivided.");
                                    newnumwalls--;
                                    goto end_space_handling;
                                }

                                if (newnumwalls + j > MAXWALLS || numsectors+1 > MAXSECTORS)
                                {
                                    message("Automatically adding inner sector to new extended sector would exceed limits!");
                                    newnumwalls--;
                                    goto end_space_handling;
                                }
                            }
#endif
                            if (clockdir(numwalls) == 0)
                                flipwalls(numwalls,newnumwalls);

                            sector[k].wallnum += j;
                            for (i=k+1; i<numsectors; i++)
                                sector[i].wallptr += j;
                            suckwall = sector[k].wallptr;

                            for (i=0; i<numwalls; i++)
                            {
                                if (wall[i].nextwall >= suckwall)
                                    wall[i].nextwall += j;
                                if (wall[i].point2 >= suckwall)
                                    wall[i].point2 += j;
                            }
#ifdef YAX_ENABLE
                            yax_tweakwalls(suckwall, j);
#endif

                            Bmemmove(&wall[suckwall+j], &wall[suckwall], (newnumwalls-suckwall)*sizeof(walltype));
                            Bmemmove(&wall[suckwall], &wall[newnumwalls], j*sizeof(walltype));

                            for (i=suckwall; i<suckwall+j; i++)
                            {
                                wall[i].point2 += (suckwall-numwalls);

                                copy_some_wall_members(i, suckwall+j, 1);
                                wall[i].cstat &= ~(1+16+32+64);
                            }

                            numwalls = newnumwalls;
                            newnumwalls = -1;
#ifdef YAX_ENABLE
                            if (extendedSector)
                            {
                                newnumwalls = whitelinescan(k, suckwall);
                                if (newnumwalls != newnumwalls2)
                                    message("YAX: WTF?");
                                for (i=numwalls; i<newnumwalls2; i++)
                                {
                                    NEXTWALL(i).nextwall = i;
                                    NEXTWALL(i).nextsector = numsectors;
                                }

                                yax_setbunches(numsectors, cbunch, fbunch);

                                numwalls = newnumwalls2;
                                newnumwalls = -1;
                                numsectors++;
                            }
#endif
                            setfirstwall(k, suckwall+j);  // restore old first wall
#ifdef YAX_ENABLE
                            if (extendedSector)
                                printmessage16("Added inner loop to sector %d and made new inner sector", k);
                            else
#endif
                            printmessage16("Added inner loop to sector %d", k);
                            mkonwinvalid();
                        }
                    }
                    else  // if connected to at least one other sector
                    {
                        //add new sector with connections

                        if (clockdir(numwalls) == 1)
                            flipwalls(numwalls,newnumwalls);

                        for (i=numwalls; i<newnumwalls; i++)
                        {
                            copy_some_wall_members(i, suckwall, 1);
                            if (checksectorpointer(i, numsectors) > 0)
                            {
                                // if new red line, prefer the other-side wall as base
                                suckwall = wall[i].nextwall;
                            }
                        }
                        sucksect = sectorofwall(suckwall);

                        if (numsectors != sucksect)
                            Bmemcpy(&sector[numsectors], &sector[sucksect], sizeof(sectortype));

                        sector[numsectors].wallptr = numwalls;
                        sector[numsectors].wallnum = newnumwalls-numwalls;

                        sector[numsectors].extra = -1;
                        sector[numsectors].lotag = sector[numsectors].hitag = 0;

                        sector[numsectors].ceilingstat &= ~2;
                        sector[numsectors].floorstat &= ~2;
                        sector[numsectors].ceilingheinum = sector[numsectors].floorheinum = 0;
                        sector[numsectors].ceilingpal = sector[numsectors].floorpal = 0;
#ifdef YAX_ENABLE
                        yax_setbunches(numsectors, -1, -1);
#endif
                        headspritesect[numsectors] = -1;
                        numsectors++;

                        numwalls = newnumwalls;
                        newnumwalls = -1;

                        message("Created new sector %d based on sector %d", numsectors-1, sucksect);
                    }

                    asksave = 1;
#ifdef YAX_ENABLE
                    yax_update(0);
                    yax_updategrays(pos.z);
#endif

                    goto end_space_handling;
                }
                ////////// split sector //////////
                else if (split == 1)
                {
                    int16_t danumwalls, splitendwall, doSectorSplit;
                    int16_t secondstartwall=-1;  // used only with splitting
                    int32_t expectedNumwalls = numwalls+2*(newnumwalls-numwalls-1), loopnum;

                    startwall = sector[splitsect].wallptr;
                    endwall = startwall + sector[splitsect].wallnum - 1;

//                    OSD_Printf("numwalls: %d, newnumwalls: %d\n", numwalls, newnumwalls);
                    i = -1;
                    for (k=startwall; k<=endwall; k++)
                        if (wall[k].x == wall[newnumwalls-1].x && wall[k].y == wall[newnumwalls-1].y)
                        {
                            i = k;
                            break;
                        }
                    //           vvvv shouldn't happen, but you never know...
                    if (i==-1 || k==splitstartwall)
                        goto end_space_handling;

                    splitendwall = k;
                    doSectorSplit = (loopnumofsector(splitsect,splitstartwall)
                                     == loopnumofsector(splitsect,splitendwall));

                    if (expectedNumwalls > MAXWALLS)
                    {
                        printmessage16("%s would exceed wall limit.", bad==0 ?
                                       "Splitting sector" : "Joining sector loops");
                        newnumwalls--;
                        break;
                    }

                    ////////// common code for splitting/loop joining //////////

                    newnumwalls--;  //first fix up the new walls
                    for (i=numwalls; i<newnumwalls; i++)
                    {
                        copy_some_wall_members(i, startwall, 1);
                        wall[i].point2 = i+1;
                    }

                    danumwalls = newnumwalls;  //where to add more walls

                    if (doSectorSplit)
                    {
                        // Copy outer loop of first sector
                        if (do_while_copyloop1(splitendwall, splitstartwall, &danumwalls, numwalls))
                            goto split_not_enough_walls;

                        //Add other loops for 1st sector
                        i = loopnum = loopnumofsector(splitsect,splitstartwall);

                        for (j=startwall; j<=endwall; j++)
                        {
                            k = loopnumofsector(splitsect,j);
                            if (k == i)
                                continue;

                            if (k == loopnum)
                                continue;

                            i = k;

                            if (loopinside(wall[j].x,wall[j].y, numwalls) != 1)
                                continue;

                            if (do_while_copyloop1(j, j, &danumwalls, danumwalls))
                                goto split_not_enough_walls;
                        }

                        secondstartwall = danumwalls;
                    }
                    else
                    {
                        if (do_while_copyloop1(splitendwall, splitendwall, &danumwalls, -1))
                            goto split_not_enough_walls;
                    }

                    //copy split points for other sector backwards
                    for (j=newnumwalls; j>numwalls; j--)
                    {
                        Bmemcpy(&wall[danumwalls], &wall[j], sizeof(walltype));
                        wall[danumwalls].nextwall = -1;
                        wall[danumwalls].nextsector = -1;
                        wall[danumwalls].point2 = danumwalls+1;
#ifdef YAX_ENABLE
                        yax_setnextwall(danumwalls,YAX_CEILING, -1);
                        yax_setnextwall(danumwalls,YAX_FLOOR, -1);
#endif
                        danumwalls++;
                    }

                    //copy rest of loop next
                    if (doSectorSplit)
                    {
                        if (do_while_copyloop1(splitstartwall, splitendwall, &danumwalls, secondstartwall))
                            goto split_not_enough_walls;
                    }
                    else
                    {
                        if (do_while_copyloop1(splitstartwall, splitstartwall, &danumwalls, numwalls))
                            goto split_not_enough_walls;
                    }

                    //Add other loops for 2nd sector
                    i = loopnum = loopnumofsector(splitsect,splitstartwall);

                    for (j=startwall; j<=endwall; j++)
                    {
                        k = loopnumofsector(splitsect, j);
                        if (k==i)
                            continue;

                        if (doSectorSplit && k==loopnum)
                            continue;
                        if (!doSectorSplit && (k == loopnumofsector(splitsect,splitstartwall) || k == loopnumofsector(splitsect,splitendwall)))
                            continue;

                        i = k;

                        // was loopinside(... , secondstartwall) != 1, but this way there are
                        // no duplicate or left-out loops (can happen with convoluted geometry)
                        if (doSectorSplit && (loopinside(wall[j].x,wall[j].y, numwalls) != 0))
                            continue;

                        if (do_while_copyloop1(j, j, &danumwalls, danumwalls))
                            goto split_not_enough_walls;
                    }

                    //fix all next pointers on old sector line
                    for (j=numwalls; j<danumwalls; j++)
                    {
#ifdef YAX_ENABLE
//                        if (doSectorSplit || (j!=numwalls && j!=danumwalls-1))
                            yax_fixreverselinks(j, j);
#endif
                        if (wall[j].nextwall >= 0)
                        {
                            NEXTWALL(j).nextwall = j;

                            if (!doSectorSplit || j < secondstartwall)
                                NEXTWALL(j).nextsector = numsectors;
                            else
                                NEXTWALL(j).nextsector = numsectors+1;
                        }
                    }

                    //copy sector attributes & fix wall pointers
                    Bmemcpy(&sector[numsectors], &sector[splitsect], sizeof(sectortype));
                    sector[numsectors].wallptr = numwalls;
                    sector[numsectors].wallnum = (doSectorSplit?secondstartwall:danumwalls) - numwalls;

                    if (doSectorSplit)
                    {
                        //set all next pointers on split
                        for (j=numwalls; j<newnumwalls; j++)
                        {
                            m = secondstartwall+(newnumwalls-1-j);
                            wall[j].nextwall = m;
                            wall[j].nextsector = numsectors+1;
                            wall[m].nextwall = j;
                            wall[m].nextsector = numsectors;
                        }

                        Bmemcpy(&sector[numsectors+1], &sector[splitsect], sizeof(sectortype));
                        sector[numsectors+1].wallptr = secondstartwall;
                        sector[numsectors+1].wallnum = danumwalls-secondstartwall;
                    }

                    //fix sprites
                    j = headspritesect[splitsect];
                    while (j != -1)
                    {
                        k = nextspritesect[j];
                        if (!doSectorSplit || loopinside(sprite[j].x,sprite[j].y,numwalls) == 1)
                            changespritesect(j,numsectors);
                        //else if (loopinside(sprite[j].x,sprite[j].y,secondstartwall) == 1)
                        else  //Make sure no sprites get left out & deleted!
                            changespritesect(j,numsectors+1);
                        j = k;
                    }

                    numsectors += 1 + doSectorSplit;

                    k = danumwalls-numwalls;  //Back of number of walls of new sector for later
                    numwalls = danumwalls;

                    //clear out old sector's next pointers for clean deletesector
                    for (j=startwall; j<=endwall; j++)
                    {
#ifdef YAX_ENABLE
                        // same thing for yax-nextwalls (only forward links!)
                        yax_setnextwall(j, YAX_CEILING, -1);
                        yax_setnextwall(j, YAX_FLOOR, -1);
#endif
                        wall[j].nextwall = wall[j].nextsector = -1;
                    }
                    deletesector(splitsect);

                    //Check pointers
                    for (j=numwalls-k; j<numwalls; j++)
                    {
                        if (wall[j].nextwall >= 0)
                            checksectorpointer(wall[j].nextwall, wall[j].nextsector);
                        checksectorpointer(j, sectorofwall(j));
                    }

                    //k now safe to use as temp

                    if (numwalls==expectedNumwalls)
                    {
                        message("%s", doSectorSplit ? "Sector split." : "Loops joined.");
                    }
                    else
                    {
                        message("%s WARNING: CREATED %d MORE WALLS THAN EXPECTED!",
                                doSectorSplit ? "Sector split." : "Loops joined.",
                                numwalls-expectedNumwalls);
                        // this would display 'num* out of bounds' but this corruption
                        // is almost as bad... (shouldn't happen anymore)
                        if (numcorruptthings < MAXCORRUPTTHINGS)
                            corruptthings[numcorruptthings++] = 0;
                        corruptlevel = 5;
                    }

                    if (0)
                    {
split_not_enough_walls:
                        message("%s failed: not enough space beyond wall[]",
                                doSectorSplit ? "Splitting sectors" : "Joining loops");
                    }

                    newnumwalls = -1;
                    asksave = 1;

                    mkonwinvalid();
#ifdef YAX_ENABLE
                    yax_update(0);
                    yax_updategrays(pos.z);
#endif
                }
            }
        }
end_space_handling:

        if (keystatus[0x1c]) //Left Enter
        {
            keystatus[0x1c] = 0;
            if (keystatus[0x2a] && keystatus[0x1d])  // LCtrl+LShift
            {
                printmessage16("CHECKING ALL POINTERS!");
                for (i=0; i<numsectors; i++)
                {
                    startwall = sector[i].wallptr;
                    for (j=startwall; j<numwalls; j++)
                        if (startwall > wall[j].point2)
                            startwall = wall[j].point2;
                    sector[i].wallptr = startwall;
                }
                for (i=numsectors-2; i>=0; i--)
                    sector[i].wallnum = sector[i+1].wallptr-sector[i].wallptr;
                sector[numsectors-1].wallnum = numwalls-sector[numsectors-1].wallptr;

                for (i=0; i<numwalls; i++)
                {
                    wall[i].nextsector = -1;
                    wall[i].nextwall = -1;
                }
                for (i=0; i<numsectors; i++)
                {
                    for (WALLS_OF_SECTOR(i, j))
                        checksectorpointer(j, i);
                }
                printmessage16("ALL POINTERS CHECKED!");
                asksave = 1;
            }
            else
            {
                if (linehighlight >= 0)
                {
                    checksectorpointer(linehighlight,sectorofwall(linehighlight));
                    printmessage16("Checked pointers of highlighted line.");
                    asksave = 1;
                }
            }
        }

        {
            static int32_t backspace_last = 0;

            if (keystatus[0x0e]) //Backspace
            {
                keystatus[0x0e] = 0;

                if (newnumwalls >= numwalls)
                {
                    backspace_last = 1;

                    if (newnumwalls == numwalls+1 || keystatus[0x1d])  // LCtrl: delete all newly drawn walls
                        newnumwalls = -1;
                    else
                        newnumwalls--;
                }
                else if (backspace_last==0)
                {
                    graphicsmode += (1-2*(DOWN_BK(RUN) || keystatus[0x36]))+3;
                    graphicsmode %= 3;
                    printmessage16("2D mode textures %s",
                                   (graphicsmode == 2)?"enabled w/ animation":graphicsmode?"enabled":"disabled");
                }
            }
            else
                backspace_last = 0;
        }

        if (keystatus[0xd3] && eitherCTRL && numwalls > 0)  //sector delete
        {
            int32_t numdelsectors = 0, didbak=0;
            char *origframe=NULL;

#ifdef YAX_ENABLE
            int16_t cb, fb;
            uint8_t bunchbitmap[YAX_MAXBUNCHES>>3];
#endif
            keystatus[0xd3] = 0;

            sucksect = -1;

            for (i=0; i<numsectors; i++)
            {
                YAX_SKIPSECTOR(i);
                numdelsectors += (inside_editor_curpos(i) == 1);
            }

            for (i=0; i<numsectors; i++)
            {
                if (highlightsectorcnt <= 0 || !keystatus[0x2a])
                {
                    YAX_SKIPSECTOR(i);

                    if (inside_editor_curpos(i) != 1)
                        continue;
                }

                k = 0;
                if (highlightsectorcnt > 0)
                {
                    // LShift: force highlighted sector deleting
                    if (keystatus[0x2a] || (hlsectorbitmap[i>>3]&(1<<(i&7))))
                    {
                        for (j=highlightsectorcnt-1; j>=0; j--)
                        {
#ifdef YAX_ENABLE
                            yax_getbunches(highlightsector[j], &cb, &fb);
                            if (cb>=0) bunchbitmap[cb>>3] |= (1<<(cb&7));
                            if (fb>=0) bunchbitmap[fb>>3] |= (1<<(fb&7));
#endif
                            deletesector(highlightsector[j]);
                            for (k=j-1; k>=0; k--)
                                if (highlightsector[k] >= highlightsector[j])
                                    highlightsector[k]--;
                        }

                        printmessage16("Highlighted sectors deleted.");
                        mkonwinvalid();
                        k = 1;
                    }
                }

                if (k == 0)
                {
                    if (numdelsectors > 1)
                    {
                        if (!didbak)
                        {
                            origframe = Bmalloc(xdim*ydim);
                            if (origframe)
                            {
                                begindrawing();
                                Bmemcpy(origframe, (char *)frameplace, xdim*ydim);
                                enddrawing();
                                didbak = 1;
                            }
                        }
                        else
                        {
                            begindrawing();
                            Bmemcpy((char *)frameplace, origframe, xdim*ydim);
                            enddrawing();                            
                        }

                        fillsector(i, editorcolors[9]);
                        fade_editor_screen(editorcolors[9]);

                        if (!ask_if_sure("Delete this sector? (Y/N)", 0))
                            continue;
                    }

#ifdef YAX_ENABLE
                    yax_getbunches(i, &cb, &fb);
                    if (cb>=0) bunchbitmap[cb>>3] |= (1<<(cb&7));
                    if (fb>=0) bunchbitmap[fb>>3] |= (1<<(fb&7));
#endif
                    deletesector(i);
                    mkonwinvalid();
                    printmessage16("Sector deleted.");
                }

                if (origframe)
                    Bfree(origframe);

#ifdef YAX_ENABLE
                for (j=0; j<numsectors; j++)
                {
                    yax_getbunches(j, &cb, &fb);
                    if (bunchbitmap[cb>>3] & (1<<(cb&7)))
                        yax_setbunch(j, YAX_CEILING, -1);
                    if (bunchbitmap[fb>>3] & (1<<(fb&7)))
                        yax_setbunch(j, YAX_FLOOR, -1);
                }
#endif
                Bmemset(hlsectorbitmap, 0, sizeof(hlsectorbitmap));
                update_highlightsector();

                Bmemset(show2dwall, 0, sizeof(show2dwall));
                update_highlight();

                newnumwalls = -1;
                asksave = 1;
#ifdef YAX_ENABLE
                yax_update(0);
                yax_updategrays(pos.z);
#endif
                break;
            }
        }

        if (keystatus[0xd3] && (pointhighlight >= 0))
        {
            if ((pointhighlight&0xc000) == 16384)   //Sprite Delete
            {
                deletesprite(pointhighlight&16383);
                printmessage16("Sprite deleted.");
                updatenumsprites();
                update_highlight();
                asksave = 1;
            }
            keystatus[0xd3] = 0;
        }

        if (keystatus[0xd2] || keystatus[0x17])  //InsertPoint
        {
            if (highlightsectorcnt > 0)
                duplicate_selected_sectors();
            else if (highlightcnt > 0)
                duplicate_selected_sprites();
            else if (linehighlight >= 0)
            {
                int32_t onewnumwalls = newnumwalls;
                int32_t wallis2sided = (wall[linehighlight].nextwall>=0);

                if (backup_drawn_walls(0))
                {
                    message("OUT OF MEMORY!");
                }
                else if (max(numwalls,onewnumwalls) >= MAXWALLS-wallis2sided)
                {
                    printmessage16("Inserting point would exceed wall limit.");
                }
                else
                {
                    getclosestpointonwall(m32_sideview?searchx:mousxplc, m32_sideview?searchy:mousyplc,
                                          linehighlight, &dax,&day, 1);
                    i = linehighlight;
                    if (m32_sideview)
                    {
                        int32_t y_p, d, dx, dy, frac;

                        dx = dax - m32_wallscreenxy[i][0];
                        dy = day - m32_wallscreenxy[i][1];
                        d = max(dx, dy);
                        y_p = (dy>dx);

                        if (d==0)
                            goto point_not_inserted;

                        frac = divscale24(d, m32_wallscreenxy[wall[i].point2][y_p]-m32_wallscreenxy[i][y_p]);
                        dax = POINT2(i).x - wall[i].x;
                        day = POINT2(i).y - wall[i].y;
                        dax = wall[i].x + mulscale24(dax,frac);
                        day = wall[i].y + mulscale24(day,frac);
                    }

                    adjustmark(&dax,&day, newnumwalls);
                    if ((wall[i].x == dax && wall[i].y == day) || (POINT2(i).x == dax && POINT2(i).y == day))
                    {
point_not_inserted:
                        printmessage16("Point not inserted.");                        
                    }
                    else
                    {
#ifdef YAX_ENABLE
                        int32_t sec = sectorofwall(linehighlight), nextw=wall[linehighlight].nextwall;
                        int32_t tmpcf;

                        k = linehighlight;
                        if (yax_islockedwall(sec, k) || yax_islockedwall(wall[k].nextsector, wall[k].nextwall))
                        {
                            // yax'ed wall -- first find out which walls are affected
                            for (i=0; i<numwalls; i++)
                                wall[i].cstat &= ~(1<<14);

                            // round 1
                            for (YAX_ITER_WALLS(linehighlight, i, tmpcf))
                                wall[i].cstat |= (1<<14);
                            if (nextw >= 0)
                                for (YAX_ITER_WALLS(nextw, i, tmpcf))
                                    wall[i].cstat |= (1<<14);
                            // round 2 (enough?)
                            for (YAX_ITER_WALLS(linehighlight, i, tmpcf))
                                if (wall[i].nextwall >= 0 && (wall[wall[i].nextwall].cstat&(1<<14))==0)
                                    wall[wall[i].nextwall].cstat |= (1<<14);                                    
                            if (nextw >= 0)
                                for (YAX_ITER_WALLS(nextw, i, tmpcf))
                                    if (wall[i].nextwall >= 0 && (wall[wall[i].nextwall].cstat&(1<<14))==0)
                                        wall[wall[i].nextwall].cstat |= (1<<14);

                            j = 0;
                            for (i=0; i<numwalls; i++)
                                j += !!(wall[i].cstat&(1<<14));
                            if (max(numwalls,onewnumwalls)+j > MAXWALLS)
                            {
                                printmessage16("Inserting points would exceed wall limit.");
                                goto end_insert_points;
                            }
                            m = 0;
                            for (i=0; i<numwalls; i++)
                            {
                                if (wall[i].cstat&(1<<14))
                                    if (wall[i].nextwall<0 || i<wall[i].nextwall) // || !(NEXTWALL(i).cstat&(1<<14)) ??
                                    {
                                        insertpoint(i, dax,day);
                                        m += 1+(wall[i].nextwall>=0);
                                    }
                            }

                            for (i=0; i<numwalls; i++)
                            {
                                if (wall[i].cstat&(1<<14))
                                {
                                    wall[i].cstat &= ~(1<<14);
                                    k = yax_getnextwall(i+1, YAX_CEILING);
                                    if (k >= 0)
                                        yax_setnextwall(i+1, YAX_CEILING, k+1);
                                    k = yax_getnextwall(i+1, YAX_FLOOR);
                                    if (k >= 0)
                                        yax_setnextwall(i+1, YAX_FLOOR, k+1);
                                }
                            }

                            mkonwinvalid();
                            if (m==j)
                                message("Inserted %d points for constrained wall.", m);
                            else
                                message("Inserted %d points for constrained wall (expected %d, WTF?).", m, j);
                        }
                        else
#endif
                        {
                            insertpoint(linehighlight, dax,day);
                            mkonwinvalid();
                            printmessage16("Point inserted.");
                        }
#ifdef YAX_ENABLE
                        yax_updategrays(pos.z);
#endif
                    }
                }
#ifdef YAX_ENABLE
end_insert_points:
#endif
                backup_drawn_walls(1);

                asksave = 1;
            }
            keystatus[0xd2] = keystatus[0x17] = 0;
        }

        /*j = 0;
        for(i=22-1;i>=0;i--) updatecrc16(j,kensig[i]);
        if ((j&0xffff) != 0xebf)
        {
        	printf("Don't screw with my name.\n");
        	exit(0);
        }*/
        //printext16(9L,336+9L,4,-1,kensig,0);
        //printext16(8L,336+8L,12,-1,kensig,0);

        showframe(1);
        synctics = totalclock-lockclock;
        lockclock += synctics;

        if (keystatus[buildkeys[BK_MODE2D_3D]])
        {
            updatesector(pos.x,pos.y,&cursectnum);
            if (cursectnum >= 0)
                keystatus[buildkeys[BK_MODE2D_3D]] = 2;
            else
                printmessage16("Arrow must be inside a sector before entering 3D mode.");
        }

// vvv PK ------------------------------------ (LShift) Ctrl-X: (prev) next map
// this is copied from 'L' (load map), but without copying the highlighted sectors

        if (quickmapcycling && keystatus[0x2d])   //X
        {
            if (eitherCTRL)  //Ctrl
            {
nextmap:
//				bad = 0;
                i = menuselect_auto(keystatus[0x2a] ? 0:1); // Left Shift: prev map
                if (i < 0)
                {
                    if (i == -1)
                        message("No more map files.");
                    else if (i == -2)
                        message("No .MAP files found.");
                    else if (i == -3)
                        message("Load map first!");
                }
                else
                {
                    if (LoadBoard(NULL, 4))
                        goto nextmap;

                    RESET_EDITOR_VARS();
                    oposz = pos.z;
                }
                showframe(1);
                keystatus[0x1c] = 0;

                keystatus[0x2d]=keystatus[0x13]=0;

            }
        }
// ^^^ PK ------------------------------------

        if (keystatus[1] && joinsector[0] >= 0)
        {
            keystatus[1]=0;
            joinsector[0]=-1;
            printmessage16("No sectors joined.");
        }

CANCEL:
        if (keystatus[1])
        {
            m32_setkeyfilter(0);

            keystatus[1] = 0;
            _printmessage16("(N)ew, (L)oad, (S)ave, save (A)s, (T)est map, (U)ndo, (R)edo, (Q)uit");
            showframe(1);
            bflushchars();
            bad = 1;
            while (bad == 1)
            {
                if (handleevents())
                {
                    if (quitevent)
                        quitevent = 0;
                }
                idle();

                ch = bgetchar();

                if (keystatus[1])
                {
                    keystatus[1] = 0;
                    bad = 0;
                    // printmessage16("");
                }
                else if (ch == 'n' || ch == 'N')  //N
                {
                    bad = 0;

                    if (!ask_if_sure("Are you sure you want to start a new board? (Y/N)", 0))
                        break;
                    else
                    {
                        int32_t bakstat=-1;
                        mapinfofull_t bakmap;

                        if (highlightsectorcnt > 0)
                            bakstat = backup_highlighted_map(&bakmap);

//                        Bmemset(hlsectorbitmap, 0, sizeof(hlsectorbitmap));
//                        highlightsectorcnt = -1;

                        highlightcnt = -1;
                        //Clear all highlights
                        Bmemset(show2dwall, 0, sizeof(show2dwall));
                        Bmemset(show2dsprite, 0, sizeof(show2dsprite));

                        for (i=0; i<MAXSECTORS; i++) sector[i].extra = -1;
                        for (i=0; i<MAXWALLS; i++) wall[i].extra = -1;
                        for (i=0; i<MAXSPRITES; i++) sprite[i].extra = -1;

                        RESET_EDITOR_VARS();
                        mkonwinvalid();

                        reset_default_mapstate();

                        Bstrcpy(boardfilename,"newboard.map");
                        map_undoredo_free();

                        if (bakstat==0)
                        {
                            bakstat = restore_highlighted_map(&bakmap);
                            if (bakstat == -1)
                                message("Can't copy highlighted portion of old map: limits exceeded.");
                        }

                        break;
                    }

                    // printmessage16("");
                    showframe(1);
                }
                else if (ch == 'l' || ch == 'L')  //L
                {
                    bad = 0;
                    i = menuselect();
                    if (i < 0)
                    {
                        if (i == -2)
                            printmessage16("No .MAP files found.");
                    }
                    else
                    {
                        int32_t bakstat=-1;
                        mapinfofull_t bakmap;

                        if (highlightsectorcnt > 0)
                            bakstat = backup_highlighted_map(&bakmap);
// __old_mapcopy_2__
                        if (LoadBoard(NULL, 4))
                        {
                            message("Invalid map format, nothing loaded.");
                            if (bakstat==0)
                                mapinfofull_free(&bakmap);
                        }
                        else
                        {
                            RESET_EDITOR_VARS();
                            oposz = pos.z;

                            if (bakstat==0)
                            {
                                bakstat = restore_highlighted_map(&bakmap);
                                if (bakstat == -1)
                                    message("Can't copy highlighted portion of old map: limits exceeded.");
                            }
                        }
                    }
                    showframe(1);
                    keystatus[0x1c] = 0;
                }
                else if (ch == 'a' || ch == 'A')  //A
                {
                    int32_t corrupt = CheckMapCorruption(4, 0);

                    bad = 0;

                    Bstrcpy(selectedboardfilename, boardfilename);
                    if (Bstrrchr(boardfilename, '/'))
                        Bstrcpy(boardfilename, Bstrrchr(boardfilename, '/')+1);

                    i = 0;
                    while ((boardfilename[i] != 0) && (i < 64))
                        i++;
                    if (boardfilename[i-4] == '.')
                        i -= 4;
                    boardfilename[i] = 0;

                    bflushchars();
                    while (bad == 0)
                    {
                        _printmessage16("%sSave as: ^011%s%s", corrupt>=4?"(map corrupt) ":"",
                                        boardfilename, (totalclock&32)?"_":"");
                        showframe(1);

                        if (handleevents())
                            quitevent = 0;

                        idle();

                        ch = bgetchar();

                        if (keystatus[1]) bad = 1;
                        else if (ch == 13) bad = 2;
                        else if (ch > 0)
                        {
                            if (i > 0 && (ch == 8 || ch == 127))
                            {
                                i--;
                                boardfilename[i] = 0;
                            }
                            else if (i < 40 && ch > 32 && ch < 128)
                            {
                                boardfilename[i++] = ch;
                                boardfilename[i] = 0;
                            }
                        }
                    }

                    if (bad == 1)
                    {
                        Bstrcpy(boardfilename, selectedboardfilename);
                        keystatus[1] = 0;
                        printmessage16("Operation cancelled");
                        showframe(1);
                    }
                    else if (bad == 2)
                    {
                        const char *f;
                        char *slash;

                        keystatus[0x1c] = 0;

                        Bstrcpy(&boardfilename[i], ".map");

                        slash = Bstrrchr(selectedboardfilename,'/');
                        Bstrcpy(slash ? slash+1 : selectedboardfilename, boardfilename);

                        _printmessage16("Saving board...");
                        showframe(1);

                        f = SaveBoard(selectedboardfilename, 0);

                        if (f)
                            printmessage16("Saved board %sto %s.",
                                           saveboard_savedtags?"and tags ":"", f);
                        else
                            printmessage16("Saving board failed.");

                        Bstrcpy(boardfilename, selectedboardfilename);
                    }
                    bad = 0;
                }
                else if (ch == 's' || ch == 'S')  //S
                {
                    const char *f;

                    bad = 0;

                    if (CheckMapCorruption(4, 0)>=4)
                    {
                        fade_editor_screen(-1);
                        if (!ask_if_sure("Map is corrupt. Are you sure you want to save? (Y/N)", 0))
                            break;
                    }

                    _printmessage16("Saving board...");
                    showframe(1);

                    f = SaveBoard(NULL, 0);

                    if (f)
                        printmessage16("Saved board %sto %s.",
                                       saveboard_savedtags?"and tags ":"", f);
                    else
                        printmessage16("Saving board failed.");

                    showframe(1);
                }
                else if (ch == 't' || ch == 'T')
                {
                    test_map(0);
                }
                else if (ch == 'u' || ch == 'U')
                {
                    bad = 0;
                    if (map_undoredo(0)) printmessage16("Nothing to undo!");
                    else printmessage16("Revision %d undone",map_revision);
                }
                else if (ch == 'r' || ch == 'R')
                {
                    bad = 0;
                    if (map_undoredo(1)) printmessage16("Nothing to redo!");
                    else printmessage16("Restored revision %d",map_revision-1);
                }
                else if (ch == 'q' || ch == 'Q')  //Q
                {
                    bad = 0;

                    if (ask_if_sure("Are you sure you want to quit?", 0))
                    {
                        //QUIT!

                        int32_t corrupt = CheckMapCorruption(4, 0);

                        if (ask_if_sure(corrupt<4?"Save changes?":"Map corrupt. Save changes?", 2+(corrupt>=4)))
                            SaveBoard(NULL, 0);

                        while (keystatus[1] || keystatus[0x2e])
                        {
                            keystatus[1] = 0;
                            keystatus[0x2e] = 0;
                            quitevent = 0;
                            printmessage16("Operation cancelled");
                            showframe(1);
                            goto CANCEL;
                        }

                        ExtUnInit();
//                        clearfilenames();
                        uninittimer();
                        uninitinput();
//                        uninitengine();
                        uninitsystem();
                        exit(0);
                    }

                    // printmessage16("");
                    showframe(1);
                }
            }

            clearkeys();

            m32_setkeyfilter(1);
        }

        VM_OnEvent(EVENT_KEYS2D, -1);

        //nextpage();
    }

    for (i=0; i<highlightsectorcnt; i++)
    {
        for (WALLS_OF_SECTOR(highlightsector[i], j))
        {
            if (wall[j].nextwall >= 0)
                checksectorpointer(wall[j].nextwall, wall[j].nextsector);
            checksectorpointer(j, highlightsector[i]);
        }
    }

    fixspritesectors();

    if (setgamemode(fullscreen,xdimgame,ydimgame,bppgame) < 0)
    {
        initprintf("%d * %d not supported in this graphics mode\n",xdim,ydim);
        ExtUnInit();
        uninitinput();
        uninittimer();
        uninitsystem();
//        clearfilenames();
        exit(1);
    }

    setbrightness(GAMMA_CALC,0,0);

    pos.z = oposz;

    searchx = clamp(scale(searchx,xdimgame,xdim2d), 8, xdimgame-8-1);
    searchy = clamp(scale(searchy,ydimgame,ydim2d-STATUS2DSIZ), 8, ydimgame-8-1);

    m32_setkeyfilter(0);

    VM_OnEvent(EVENT_ENTER3DMODE, -1);
}

// flags:
// 1: quit_is_yes
// 2: don't clear keys on return
int32_t ask_if_sure(const char *query, uint32_t flags)
{
    char ch;
    int32_t ret=-1;

    if (!query)
        _printmessage16("Are you sure?");
    else
        _printmessage16("%s", query);
    showframe(1);
    bflushchars();

    while ((keystatus[1]|keystatus[0x2e]) == 0 && ret==-1)
    {
        if (handleevents())
        {
            if (quitevent)
            {
                if (flags&1)
                    return 1;
                else
                    quitevent = 0;
            }
        }
        idle();

        ch = bgetchar();

        if (ch == 'y' || ch == 'Y')
            ret = 1;
        else if (ch == 'n' || ch == 'N' || ch == 13 || ch == ' ')
            ret = 0;
    }

    if ((flags&2)==0)
        clearkeys();

    if (ret >= 0)
        return ret;

    return 0;
}

int32_t editor_ask_function(const char *question, const char *dachars, int32_t numchars)
{
    char ch;
    int32_t i, ret=-1;

    _printmessage16("%s", question);

    showframe(1);
    bflushchars();

    // 'c' is cancel too, but can be overridden
    while ((keystatus[1]|keystatus[0x2e]) == 0 && ret==-1)
    {
        if (handleevents())
            quitevent = 0;

        idle();
        ch = bgetchar();

        for (i=0; i<numchars; i++)
            if (ch==Btolower(dachars[i]) || ch==Btoupper(dachars[i]))
                ret = i;
    }

    clearkeys();

    return ret;
}

#ifdef YAX_ENABLE
static int32_t ask_above_or_below(void)
{
    char dachars[2] = {'a', 'z'};
    return editor_ask_function("Extend above (a) or below (z)?", dachars, 2);
}
#endif

// flags:  1:no ExSaveMap (backup.map) and no taglabels saving
const char *SaveBoard(const char *fn, uint32_t flags)
{
    const char *f;
    int32_t ret;

    saveboard_savedtags = 0;

    if (!fn)
        fn = boardfilename;

    if (pathsearchmode)
        f = fn;
    else
    {
        // virtual filesystem mode can't save to directories so drop the file into
        // the current directory
        f = Bstrrchr(fn, '/');
        if (!f)
            f = fn;
        else
            f++;
    }

    fixspritesectors();   //Do this before saving!
    updatesector(startposx,startposy,&startsectnum);
    ExtPreSaveMap();
    ret = saveboard(f,&startposx,&startposy,&startposz,&startang,&startsectnum);
    if ((flags&1)==0)
    {
        ExtSaveMap(f);
        saveboard_savedtags = !taglab_save(f);
    }

    if (!ret)
        return f;
    else
        return NULL;
}

// flags:  1: for running on Mapster32 init
//         4: passed to loadboard flags (no polymer_loadboard); implies no maphack loading
int32_t LoadBoard(const char *filename, uint32_t flags)
{
    int32_t i, tagstat, loadingflags=(!pathsearchmode&&grponlymode?2:0);

    if (!filename)
        filename = selectedboardfilename;

    if (filename != boardfilename)
        Bstrcpy(boardfilename, filename);

    for (i=0; i<MAXSECTORS; i++) sector[i].extra = -1;
    for (i=0; i<MAXWALLS; i++) wall[i].extra = -1;
    for (i=0; i<MAXSPRITES; i++) sprite[i].extra = -1;

    editorzrange[0] = INT32_MIN;
    editorzrange[1] = INT32_MAX;

    ExtPreLoadMap();
    i = loadboard(boardfilename,(flags&4)|loadingflags, &pos.x,&pos.y,&pos.z,&ang,&cursectnum);
    if (i == -2)
        i = loadoldboard(boardfilename,loadingflags, &pos.x,&pos.y,&pos.z,&ang,&cursectnum);
    if (i < 0)
    {
//        printmessage16("Invalid map format.");
        return i;
    }

    mkonwinvalid();

    highlightcnt = -1;
    Bmemset(show2dwall, 0, sizeof(show2dwall));  //Clear all highlights
    Bmemset(show2dsprite, 0, sizeof(show2dsprite));    

    if ((flags&4)==0)
        loadmhk(0);

    tagstat = taglab_load(boardfilename, loadingflags);
    ExtLoadMap(boardfilename);

    if (mapversion < 7)
        message("Map %s loaded successfully and autoconverted to V7!",boardfilename);
    else
    {
        i = CheckMapCorruption(4, 0);
        message("Loaded map %s%s %s", boardfilename, tagstat==0?" w/tags":"",
                i==0?"successfully": (i<4 ? "(moderate corruption)" : "(HEAVY corruption)"));
    }

    updatenumsprites();
    startposx = pos.x;      //this is same
    startposy = pos.y;
    startposz = pos.z;
    startang = ang;
    startsectnum = cursectnum;

    return 0;
}

void getpoint(int32_t searchxe, int32_t searchye, int32_t *x, int32_t *y)
{
    inpclamp(&pos.x, -editorgridextent, editorgridextent);
    inpclamp(&pos.y, -editorgridextent, editorgridextent);

    searchxe -= halfxdim16;
    searchye -= midydim16;

    if (m32_sideview)
    {
        if (m32_sidesin!=0)
            searchye = divscale14(searchye, m32_sidesin);
        rotatepoint(0,0, searchxe,searchye, -m32_sideang, &searchxe,&searchye);
    }

    *x = pos.x + divscale14(searchxe,zoom);
    *y = pos.y + divscale14(searchye,zoom);

    inpclamp(x, -editorgridextent, editorgridextent);
    inpclamp(y, -editorgridextent, editorgridextent);
}

static int32_t getlinehighlight(int32_t xplc, int32_t yplc, int32_t line)
{
    int32_t i, j, dst, dist, closest, x1, y1, x2, y2, nx, ny;
    int32_t daxplc, dayplc;

    if (numwalls == 0)
        return -1;

    if (mouseb & 1)
        return line;

    if ((pointhighlight&0xc000) == 16384)
        return -1;

    dist = 1024;
    if (m32_sideview)
    {
        daxplc = searchx;
        dayplc = searchy;
        dist = mulscale14(dist, zoom);
    }
    else
    {
        daxplc = xplc;
        dayplc = yplc;
    }

    closest = -1;
    for (i=0; i<numwalls; i++)
    {
        if (!m32_sideview)
            YAX_SKIPWALL(i);

        getclosestpointonwall(daxplc,dayplc, i, &nx,&ny, 1);
        dst = klabs(daxplc-nx) + klabs(dayplc-ny);
        if (dst <= dist)
        {
            dist = dst;
            closest = i;
        }
    }

    j = wall[closest].nextwall;
    if (closest>=0 && j >= 0)
#ifdef YAX_ENABLE
    if (m32_sideview || ((graywallbitmap[j>>3]&(1<<(j&7)))==0))
#endif
    {
        //if red line, allow highlighting of both sides
        if (m32_sideview)
        {
            x1 = m32_wallscreenxy[closest][0];
            y1 = m32_wallscreenxy[closest][1];
            x2 = m32_wallscreenxy[wall[closest].point2][0];
            y2 = m32_wallscreenxy[wall[closest].point2][1];
        }
        else
        {
            x1 = wall[closest].x;
            y1 = wall[closest].y;
            x2 = POINT2(closest).x;
            y2 = POINT2(closest).y;
        }

        i = wall[closest].nextwall;
        if (!m32_sideview ||
            ((*(int64_t *)m32_wallscreenxy[closest]==*(int64_t *)m32_wallscreenxy[wall[j].point2]) &&
             (*(int64_t *)m32_wallscreenxy[wall[closest].point2]==*(int64_t *)m32_wallscreenxy[j])))
            if (dmulscale32(daxplc-x1,y2-y1,-(x2-x1),dayplc-y1) >= 0)
                closest = j;
    }

    return closest;
}

int32_t getpointhighlight(int32_t xplc, int32_t yplc, int32_t point)
{
    int32_t i, j, dst, dist = 512, closest = -1;
    int32_t dax,day;

    if (numwalls == 0)
        return -1;

    if (mouseb & 1)
        return point;

    if (grid < 1)
        dist = 0;

    for (i=0; i<numsectors; i++)
    {
        if (!m32_sideview)
            YAX_SKIPSECTOR(i);

        for (j=sector[i].wallptr; j<sector[i].wallptr+sector[i].wallnum; j++)
        {
            if (!m32_sideview)
                dst = klabs(xplc-wall[j].x) + klabs(yplc-wall[j].y);
            else
            {
                screencoords(&dax,&day, wall[j].x-pos.x,wall[j].y-pos.y, zoom);
                day += getscreenvdisp(getflorzofslope(i, wall[j].x,wall[j].y)-pos.z, zoom);

                if (halfxdim16+dax < 0 || halfxdim16+dax >= xdim || midydim16+day < 0 || midydim16+day >= ydim)
                    continue;

                dst = klabs(halfxdim16+dax-searchx) + klabs(midydim16+day-searchy);
            }

            if (dst <= dist)
            {
                // prefer white walls
                if (dst<dist || closest==-1 || (wall[j].nextwall>=0)-(wall[closest].nextwall>=0) <= 0)
                    dist = dst, closest = j;
            }
        }
    }

    if (zoom >= 256)
        for (i=0; i<MAXSPRITES; i++)
            if (sprite[i].statnum < MAXSTATUS)
            {
                if (!m32_sideview && sprite[i].sectnum >= 0)
                    YAX_SKIPSECTOR(sprite[i].sectnum);

                if (!m32_sideview)
                {
                    dst = klabs(xplc-sprite[i].x) + klabs(yplc-sprite[i].y);
                }
                else
                {
                    screencoords(&dax,&day, sprite[i].x-pos.x,sprite[i].y-pos.y, zoom);
                    day += getscreenvdisp(sprite[i].z-pos.z, zoom);

                    if (halfxdim16+dax < 0 || halfxdim16+dax >= xdim || midydim16+day < 0 || midydim16+day >= ydim)
                        continue;

                    dst = klabs(halfxdim16+dax-searchx) + klabs(midydim16+day-searchy);
                }

                // was (dst <= dist), but this way, when duplicating sprites,
                // the selected ones are dragged first
                if (dst < dist || (dst == dist && (show2dsprite[i>>3]&(1<<(i&7)))))
                    dist = dst, closest = i+16384;
            }

    return closest;
}

static void locktogrid(int32_t *dax, int32_t *day)
{
    *dax = ((*dax+(1024>>grid))&(0xffffffff<<(11-grid)));
    *day = ((*day+(1024>>grid))&(0xffffffff<<(11-grid)));
}

static int32_t adjustmark(int32_t *xplc, int32_t *yplc, int16_t danumwalls)
{
    int32_t i, dst, dist, dax, day, pointlockdist;

    if (danumwalls < 0)
        danumwalls = numwalls;

    pointlockdist = 0;
    if (grid > 0 && gridlock)
        pointlockdist = (128>>grid);

    dist = pointlockdist;
    dax = *xplc;
    day = *yplc;

    for (i=0; i<danumwalls; i++)
    {
        YAX_SKIPWALL(i);

        dst = klabs((*xplc)-wall[i].x) + klabs((*yplc)-wall[i].y);
        if (dst < dist)
        {
            dist = dst;
            dax = wall[i].x;
            day = wall[i].y;
        }
    }
    if (dist == pointlockdist)
        if (gridlock && grid > 0)
            locktogrid(&dax, &day);

    *xplc = dax;
    *yplc = day;

    return(0);
}

static int32_t checkautoinsert(int32_t dax, int32_t day, int16_t danumwalls)
{
    int32_t i, x1, y1, x2, y2;

    if (danumwalls < 0)
        danumwalls = numwalls;

    for (i=0; i<danumwalls; i++)    // Check if a point should be inserted
    {
        YAX_SKIPWALL(i);

        x1 = wall[i].x;
        y1 = wall[i].y;
        x2 = POINT2(i).x;
        y2 = POINT2(i).y;

        if ((x1 != dax || y1 != day) && (x2 != dax || y2 != day))
            if ((x1 <= dax && dax <= x2) || (x2 <= dax && dax <= x1))
                if ((y1 <= day && day <= y2) || (y2 <= day && day <= y1))
                    if ((dax-x1)*(y2-y1) == (day-y1)*(x2-x1))
                        return(1);          //insertpoint((short)i,dax,day);
    }

    return(0);
}

static int32_t clockdir(int16_t wallstart)   //Returns: 0 is CW, 1 is CCW
{
    int16_t i, themin;
    int32_t minx, tempint, x0, x1, x2, y0, y1, y2;

    minx = 0x7fffffff;
    themin = -1;
    i = wallstart-1;
    do
    {
        i++;
        if (POINT2(i).x < minx)
        {
            minx = POINT2(i).x;
            themin = i;
        }
    }
    while ((wall[i].point2 != wallstart) && (i < MAXWALLS));

    x0 = wall[themin].x;
    y0 = wall[themin].y;
    x1 = POINT2(themin).x;
    y1 = POINT2(themin).y;
    x2 = POINT2(wall[themin].point2).x;
    y2 = POINT2(wall[themin].point2).y;

    if (y1 >= y2 && y1 <= y0) return(0);
    if (y1 >= y0 && y1 <= y2) return(1);

    tempint = (x0-x1)*(y2-y1) - (x2-x1)*(y0-y1);
    if (tempint < 0)
        return(0);
    else
        return(1);
}

static void flipwalls(int16_t numwalls, int16_t newnumwalls)
{
    int32_t i, j, nume;

    nume = newnumwalls-numwalls;

    for (i=numwalls; i<numwalls+(nume>>1); i++)
    {
        j = numwalls+newnumwalls-i-1;

        swapshort(&onextwall[i], &onextwall[j]);

        swaplong(&wall[i].x, &wall[j].x);
        swaplong(&wall[i].y, &wall[j].y);
    }
}

static void insertpoint(int16_t linehighlight, int32_t dax, int32_t day)
{
    int16_t sucksect;
    int32_t i, j, k;
    uint32_t templenrepquot;

    j = linehighlight;
    sucksect = sectorofwall(j);
    templenrepquot = getlenbyrep(wallength(j), wall[j].xrepeat);

    sector[sucksect].wallnum++;
    for (i=sucksect+1; i<numsectors; i++)
        sector[i].wallptr++;

    movewalls(j+1, +1);
    Bmemcpy(&wall[j+1], &wall[j], sizeof(walltype));
#ifdef YAX_ENABLE
    wall[j+1].cstat &= ~(1<<14);
#endif
    wall[j].point2 = j+1;
    wall[j+1].x = dax;
    wall[j+1].y = day;
    fixxrepeat(j, templenrepquot);
    AlignWallPoint2(j);
    fixxrepeat(j+1, templenrepquot);

    if (wall[j].nextwall >= 0)
    {
        k = wall[j].nextwall;
        templenrepquot = getlenbyrep(wallength(k), wall[k].xrepeat);

        sucksect = sectorofwall(k);

        sector[sucksect].wallnum++;
        for (i=sucksect+1; i<numsectors; i++)
            sector[i].wallptr++;

        movewalls(k+1, +1);
        Bmemcpy(&wall[k+1], &wall[k], sizeof(walltype));
#ifdef YAX_ENABLE
        wall[k+1].cstat &= ~(1<<14);
#endif
        wall[k].point2 = k+1;
        wall[k+1].x = dax;
        wall[k+1].y = day;
        fixxrepeat(k, templenrepquot);
        AlignWallPoint2(k);
        fixxrepeat(k+1, templenrepquot);

        j = wall[k].nextwall;
        wall[j].nextwall = k+1;
        wall[j+1].nextwall = k;
        wall[k].nextwall = j+1;
        wall[k+1].nextwall = j;
    }
}

// runi: 0=check, 1=prepare, 2=do!
// if runi==0, returns error message on fail, in all other cases NULL
static const char *deletepoint(int16_t point, int32_t runi)
{
    int32_t i, j, sucksect;

    if (runi==0)  // consistency check -- return !=0 on fail
    {
        i = wall[point].nextsector;
        if (i >= 0 && sector[i].wallnum <= 3)
            return "Invalid operation, delete or join sector instead.";

        return NULL;
    }
    else if (runi==1)
    {
        i = wall[point].nextwall;
        if (i >= 0)
        {
            NEXTWALL(i).nextwall = NEXTWALL(i).nextsector = -1;
            wall[i].nextwall = wall[i].nextsector = -1;
        }

        return NULL;
    }

    sucksect = sectorofwall(point);

    sector[sucksect].wallnum--;
    for (i=sucksect+1; i<numsectors; i++)
        sector[i].wallptr--;

    j = lastwall(point);
    wall[j].point2 = wall[point].point2;

#if 0
    if (wall[j].nextwall >= 0)
    {
        NEXTWALL(j).nextwall = -1;
        NEXTWALL(j).nextsector = -1;
    }
    if (wall[point].nextwall >= 0)
    {
        NEXTWALL(point).nextwall = -1;
        NEXTWALL(point).nextsector = -1;
    }
#endif
    movewalls(point, -1);

//    checksectorpointer(j, sucksect);

    return NULL;
}

static int32_t deletesector(int16_t sucksect)
{
    int32_t i, j, k, nextk, startwall, endwall;

    while (headspritesect[sucksect] >= 0)
        deletesprite(headspritesect[sucksect]);
    updatenumsprites();

    startwall = sector[sucksect].wallptr;
    endwall = startwall + sector[sucksect].wallnum - 1;
    j = sector[sucksect].wallnum;

#ifdef YAX_ENABLE
    yax_setbunches(sucksect, -1, -1);
#endif

    for (i=sucksect; i<numsectors-1; i++)
    {
        k = headspritesect[i+1];
        while (k != -1)
        {
            nextk = nextspritesect[k];
            changespritesect(k, i);
            k = nextk;
        }

        Bmemcpy(&sector[i], &sector[i+1], sizeof(sectortype));
        sector[i].wallptr -= j;
    }
    numsectors--;

    for (i=startwall; i<=endwall; i++)
    {
        if (wall[i].nextwall >= 0)
        {
            NEXTWALL(i).nextwall = -1;
            NEXTWALL(i).nextsector = -1;
        }
    }

    movewalls(startwall, -j);
    for (i=0; i<numwalls; i++)
        if (wall[i].nextwall >= startwall)
            wall[i].nextsector--;

    return(0);
}

void fixspritesectors(void)
{
    int32_t i, j, dax, day, cz, fz;

    for (i=numsectors-1; i>=0; i--)
        if (sector[i].wallnum <= 0 || sector[i].wallptr >= numwalls)
            deletesector(i);

    if (m32_script_expertmode)
        return;

    for (i=0; i<MAXSPRITES; i++)
        if (sprite[i].statnum < MAXSTATUS)
        {
            dax = sprite[i].x;
            day = sprite[i].y;

            if (inside(dax,day,sprite[i].sectnum) != 1)
            {
                spriteoncfz(i, &cz, &fz);

                for (j=0; j<numsectors; j++)
                {
                    if (inside(dax,day, j) != 1)
                        continue;

                    if (cz <= sprite[i].z && sprite[i].z <= fz)
                    {
                        changespritesect(i, j);
                        break;
                    }
                }
            }
        }
}

static int32_t movewalls(int32_t start, int32_t offs)
{
    int32_t i;

    if (offs < 0)  //Delete
    {
        for (i=start; i<numwalls+offs; i++)
            Bmemcpy(&wall[i], &wall[i-offs], sizeof(walltype));
    }
    else if (offs > 0)  //Insert
    {
        for (i=numwalls+offs-1; i>=start+offs; i--)
            Bmemcpy(&wall[i], &wall[i-offs], sizeof(walltype));
    }

    numwalls += offs;
    for (i=0; i<numwalls; i++)
    {
        if (wall[i].nextwall >= start) wall[i].nextwall += offs;
        if (wall[i].point2 >= start) wall[i].point2 += offs;
    }
#ifdef YAX_ENABLE
    yax_tweakwalls(start, offs);
#endif

    return(0);
}

int32_t wallength(int16_t i)
{
    int64_t dax = POINT2(i).x - wall[i].x;
    int64_t day = POINT2(i).y - wall[i].y;
#if 1 //def POLYMOST
    int64_t hypsq = dax*dax + day*day;
    if (hypsq > (int64_t)INT32_MAX)
        return (int32_t)sqrt((double)hypsq);
    else
        return ksqrt((int32_t)hypsq);
#else
    return ksqrt(dax*dax + day*day);
#endif
}

void fixrepeats(int16_t i)
{
    int32_t dist = wallength(i);
    int32_t day = wall[i].yrepeat;

    wall[i].xrepeat = clamp(mulscale10(dist,day), 1, 255);
}

uint32_t getlenbyrep(int32_t len, int32_t repeat)
{
    if (repeat <= 0)
        return ((uint32_t)len)<<12;

    return divscale12(len, repeat);
}

void fixxrepeat(int16_t wallnum, uint32_t lenrepquot)  // lenrepquot: divscale12(wallength,xrepeat)
{
    if (lenrepquot != 0)
    {
        uint32_t res = (((wallength(wallnum)<<12)+(1<<11))/lenrepquot);
        wall[wallnum].xrepeat = clamp(res, 1, 255);
    }
}


int32_t overridepm16y = -1;

void clearmidstatbar16(void)
{
    int32_t y = overridepm16y<0 ? STATUS2DSIZ : overridepm16y;

    begindrawing();
    CLEARLINES2D(ydim-y+25, STATUS2DSIZ+2-(25<<1), 0);
    enddrawing();
}

static void clearministatbar16(void)
{
    int32_t i, col = whitecol - 21;
//    static const char *tempbuf = "Mapster32" " " VERSION;
    char tempbuf[16];

    begindrawing();

    for (i=ydim-STATUS2DSIZ2; i<ydim; i++)
    {
//        drawline256(0, i<<12, xdim<<12, i<<12, col);
        CLEARLINES2D(i, 1, (col<<24)|(col<<16)|(col<<8)|col);

        col--;
        if (col <= 0) break;
    }

    CLEARLINES2D(i, ydim-i, 0);

    Bsprintf(tempbuf, "Mapster32 %s", ExtGetVer());
    printext16(xdim2d-(Bstrlen(tempbuf)<<3)-3, ydim2d-STATUS2DSIZ2+10, editorcolors[4],-1, tempbuf, 0);
    printext16(xdim2d-(Bstrlen(tempbuf)<<3)-2, ydim2d-STATUS2DSIZ2+9, editorcolors[12],-1, tempbuf, 0);

    enddrawing();
}

static int16_t loopinside(int32_t x, int32_t y, int16_t startwall)
{
    int32_t x1, y1, x2, y2;
    int16_t i, cnt;

    cnt = clockdir(startwall);
    i = startwall;
    do
    {
        x1 = wall[i].x;
        x2 = POINT2(i).x;

        if (x1 >= x || x2 >= x)
        {
            y1 = wall[i].y;
            y2 = POINT2(i).y;

            if (y1 > y2)
            {
                swaplong(&x1, &x2);
                swaplong(&y1, &y2);
            }
            if (y1 <= y && y2 > y)
                if (x1*(y-y2)+x2*(y1-y) <= x*(y1-y2))
                    cnt ^= 1;
        }
        i = wall[i].point2;
    }
    while (i != startwall);

    return(cnt);
}

#if 0
static int32_t numloopsofsector(int16_t sectnum)
{
    int32_t i, numloops, startwall, endwall;

    numloops = 0;
    startwall = sector[sectnum].wallptr;
    endwall = startwall + sector[sectnum].wallnum;
    for (i=startwall; i<endwall; i++)
        if (wall[i].point2 < i) numloops++;
    return(numloops);
}
#endif

int32_t getnumber_internal1(char ch, int32_t *danumptr, int32_t maxnumber, char sign)
{
    int32_t danum = *danumptr;

    if (ch >= '0' && ch <= '9')
    {
        int64_t nbig;
        if (danum >= 0)
        {
            nbig = ((int64_t)danum*10)+(ch-'0');
            if (nbig <= (int64_t)maxnumber) danum = nbig;
        }
        else if (sign) // this extra check isn't hurting anything
        {
            nbig = ((int64_t)danum*10)-(ch-'0');
            if (nbig >= (int64_t)(-maxnumber)) danum = nbig;
        }
    }
    else if (ch == 8 || ch == 127)  	// backspace
    {
        danum /= 10;
    }
    else if (ch == 13)
    {
        return 1;
    }
    else if (ch == '-' && sign)  	// negate
    {
        danum = -danum;
    }

    *danumptr = danum;
    return 0;
}

int32_t getnumber_autocomplete(const char *namestart, char ch, int32_t *danum, int32_t flags)
{
    if (flags!=1 && flags!=2)
        return 0;

    if (flags==2 && *danum<0)
        return 0;

    if (isalpha(ch))
    {
        char b[2];
        const char *gotstr;
        int32_t i, diddel;

        b[0] = ch;
        b[1] = 0;
        gotstr = getstring_simple(namestart, b, (flags==1)?sizeof(names[0])-1:TAGLAB_MAX-1, flags);
        if (!gotstr || !gotstr[0])
            return 0;

        if (flags==1)
        {
            for (i=0; i<MAXTILES; i++)
                if (!Bstrcasecmp(names[i], gotstr))
                {
                    *danum = i;
                    return 1;
                }
        }
        else
        {
            i = taglab_gettag(gotstr);
//initprintf("taglab: s=%s, i=%d\n",gotstr,i);
            if (i > 0)
            {
                *danum = i;
                return 1;
            }
            else
            {
                // insert new tag
                if (*danum > 0 && *danum<32768)
                {
                    diddel = taglab_add(gotstr, *danum);
                    message("Added label '%s' for tag %d%s%s", gotstr, *danum,
                            diddel?", deleting old ":"",
                            (!diddel)?"":(diddel==1?"label":"tag"));
                    return 1;
                }
                else if (*danum==0)
                {
                    i = taglab_getnextfreetag();
                    if (i >= 1)
                    {
                        *danum = i;
                        diddel = taglab_add(gotstr, *danum);
                        message("%sadded label '%s' for tag %d%s%s",
                                diddel?"Auto-":"Automatically ", gotstr, *danum,
                                diddel?", deleting old ":"",
                                (!diddel)?"":(diddel==1?"label":"tag"));
                        return 1;
                    }
                }
            }
        }
    }

    return 0;
}

// sign is now used for more than one flag:
//  1: sign
//  2: autocomplete names
//  4: autocomplete taglabels
//  8: return -1 if cancelled
int32_t _getnumber16(const char *namestart, int32_t num, int32_t maxnumber, char sign, void *(func)(int32_t))
{
    char buffer[80], ch;
    int32_t n, danum, oldnum;
    uint8_t flags = (sign>>1)&7;
    sign &= 1;

    danum = num;
    oldnum = danum;

    bflushchars();
    while (keystatus[0x1] == 0)
    {
        if (handleevents())
            quitevent = 0;

        idle();
        ch = bgetchar();

        Bsprintf(buffer,"%s^011%d", namestart, danum);
        n = Bstrlen(buffer);
        if (totalclock & 32) Bstrcat(buffer,"_ ");
        _printmessage16("%s", buffer);

        if (func != NULL)
        {
            Bsprintf(buffer, "%s", (char *)func(danum));
            // printext16(200L-24, ydim-STATUS2DSIZ+20L, editorcolors[9], editorcolors[0], buffer, 0);
            printext16(n<<3, ydim-STATUS2DSIZ+128, editorcolors[11], -1, buffer,0);
        }

        showframe(1);

        n = 0;
        if (getnumber_internal1(ch, &danum, maxnumber, sign) ||
            (n=getnumber_autocomplete(namestart, ch, &danum, flags)))
        {
            if (flags==1 || n==0)
                printmessage16("%s", buffer);
            if (danum != oldnum)
                asksave = 1;
            oldnum = danum;
            break;
        }
    }

    if (keystatus[0x1] && (flags&4))
        oldnum = -1;

    clearkeys();

    return oldnum;
}

static void getnumber_clearline(void)
{
    char cbuf[128];
    int32_t i;
    for (i=0; i<min(xdim>>3, (signed)sizeof(cbuf)-1); i++)
        cbuf[i] = ' ';
    cbuf[i] = 0;
    printext256(0, 0, whitecol, 0, cbuf, 0);
}

// sign: |16: don't draw scene
int32_t _getnumber256(const char *namestart, int32_t num, int32_t maxnumber, char sign, void *(func)(int32_t))
{
    char buffer[80], ch;
    int32_t danum, oldnum;
    uint8_t flags = (sign>>1)&(3|8);
    sign &= 1;

    danum = num;
    oldnum = danum;

    bflushchars();
    while (keystatus[0x1] == 0)
    {
        if (handleevents())
            quitevent = 0;

        if ((flags&8)==0)
        {
            yax_preparedrawrooms();
            drawrooms(pos.x,pos.y,pos.z,ang,horiz,cursectnum);
            yax_drawrooms(ExtAnalyzeSprites, horiz, cursectnum);

            ExtAnalyzeSprites();
            drawmasks();

#ifdef POLYMER
            if (rendmode == 4 && searchit == 2)
            {
                polymer_editorpick();
                drawrooms(pos.x,pos.y,pos.z,ang,horiz,cursectnum);
                ExtAnalyzeSprites();
                drawmasks();
            }
#endif
        }

        ch = bgetchar();
        if (keystatus[0x1])
            break;
        clearkeys();

        mouseb = 0;
        searchx = osearchx;
        searchy = osearchy;

        if ((flags&8)==0)
            ExtCheckKeys();

        getnumber_clearline();

        Bsprintf(buffer,"%s%d",namestart,danum);
        if (totalclock & 32) Bstrcat(buffer,"_ ");
        printmessage256(0, 0, buffer);
        if (func != NULL)
        {
            Bsprintf(buffer, "%s", (char *)func(danum));
            printmessage256(0, 9, buffer);
        }

        showframe(1);

        if (getnumber_internal1(ch, &danum, maxnumber, sign) ||
            getnumber_autocomplete(namestart, ch, &danum, flags&(1+2)))
        {
            if (danum != oldnum)
                asksave = 1;
            oldnum = danum;
            break;
        }
    }

    clearkeys();

    lockclock = totalclock;  //Reset timing

    return oldnum;
}

// querystr: e.g. "Name: ", must be !=NULL
// defaultstr: can be NULL
//  NO overflow checks are done when copying them!
// maxlen: maximum length of entry string, if ==1, enter single char
// completion: 0=none, 1=names[][], 2=taglabels
const char *getstring_simple(const char *querystr, const char *defaultstr, int32_t maxlen, int32_t completion)
{
    static char buf[128];
    int32_t ei=0, qrylen=0, maxidx, havecompl=0;
    char ch;

    bflushchars();
    clearkeys();

    Bmemset(buf, 0, sizeof(buf));

    qrylen = Bstrlen(querystr);
    Bmemcpy(buf, querystr, qrylen);

    if (maxlen==0)
        maxlen = 64;

    maxidx = min((signed)sizeof(buf), xdim>>3);

    ei = qrylen;

    if (defaultstr)
    {
        int32_t deflen = Bstrlen(defaultstr);
        Bmemcpy(&buf[ei], defaultstr, deflen);
        ei += deflen;
    }

    buf[ei] = '_';
    buf[ei+1] = 0;

    while (1)
    {
        if (qsetmode==200)
            getnumber_clearline();

        if (qsetmode==200)
            printext256(0, 0, whitecol, 0, buf, 0);
        else
            _printmessage16("%s", buf);

        showframe(1);

        if (handleevents())
            quitevent = 0;

        idle();
        ch = bgetchar();

        if (ch==13)
        {
            if (maxlen != 1)
                buf[ei] = 0;
            break;
        }
        else if (keystatus[1])
        {
            clearkeys();
            return completion ? NULL : defaultstr;
        }

        if (maxlen!=1)
        {
            // blink...
            if (totalclock&32)
            {
                buf[ei] = '_';
                buf[ei+1] = 0;
            }
            else
            {
                buf[ei] = 0;
            }

            if (ei>qrylen && (ch==8 || ch==127))
            {
                buf[ei--] = 0;
                buf[ei] = '_';
                havecompl = 0;
            }
            else if (ei<maxidx-2 && ei-qrylen<maxlen && isprint(ch))
            {
                if (completion==2 && ch==' ')
                    ch = '_';
                buf[ei++] = ch;
                buf[ei] = '_';
                buf[ei+1] = 0;
            }

            if (completion && ((ei>qrylen && ch==9) || havecompl))  // tab: maybe do auto-completion
            {
                char cmpbuf[128];
                char completions[3][16];
                const char *cmpstr;
                int32_t len=ei-qrylen, i, j, k=len, first=1, numcompl=0;

                Bmemcpy(cmpbuf, &buf[qrylen], len);
                cmpbuf[len] = 0;

                for (i=(completion!=1); i<((completion==1)?MAXTILES:32768); i++)
                {
                    cmpstr = (completion==1) ? names[i] : taglab_getlabel(i);
                    if (!cmpstr)
                        continue;

                    if (Bstrncasecmp(cmpbuf, cmpstr, len) || Bstrlen(cmpstr)==(unsigned)len)  // compare the prefix
                        continue;

                    if (ch==9)
                    {
                        if (first)
                        {
                            Bstrncpy(cmpbuf+len, cmpstr+len, sizeof(cmpbuf)-len);
                            cmpbuf[sizeof(cmpbuf)-1] = 0;
                            first = 0;
                        }
                        else
                        {
                            for (k=len; cmpstr[k] && cmpbuf[k] && Btolower(cmpstr[k])==Btolower(cmpbuf[k]); k++)
                                /* nop */;
                            cmpbuf[k] = 0;
                        }
                    }

                    if (numcompl<3)
                    {
                        Bstrncpy(completions[numcompl], cmpstr+len, sizeof(completions[0]));
                        completions[numcompl][sizeof(completions[0])-1] = 0;
                        for (k=0; completions[numcompl][k]; k++)
                            completions[numcompl][k] = Btolower(completions[numcompl][k]);
                        numcompl++;
                    }

                    for (k=len; cmpbuf[k]; k++)
                        cmpbuf[k] = Btolower(cmpbuf[k]);
                }

                ei = qrylen;
                for (i=0; i<k && i<maxlen && ei<maxidx-2; i++)
                    buf[ei++] = cmpbuf[i];

                if (k==len && numcompl>0)  // no chars autocompleted/completion request
                {
                    buf[ei] = '{';
                    buf[ei+1] = 0;

                    i = ei+1;
                    for (k=0; k<numcompl; k++)
                    {
                        j = 0;
                        while (i<maxidx-1 && completions[k][j])
                            buf[i++] = completions[k][j++];
                        if (i<maxidx-1)
                            buf[i++] = (k==numcompl-1) ? '}' : ',';
                    }
                    buf[i] = 0;

                    havecompl = 1;
                }
                else
                {
                    buf[ei] = '_';
                    buf[ei+1] = 0;
                }
            }
        }
        else
        {
            if (isalnum(ch) || ch==' ')
                buf[ei] = Btoupper(ch);
        }
    }

    clearkeys();

    return buf+qrylen;
}

static void clearfilenames(void)
{
    klistfree(finddirs);
    klistfree(findfiles);
    finddirs = findfiles = NULL;
    numfiles = numdirs = 0;
}

static int32_t getfilenames(const char *path, const char *kind)
{
    CACHE1D_FIND_REC *r;

    clearfilenames();
    finddirs = klistpath(path,"*",CACHE1D_FIND_DIR|CACHE1D_FIND_DRIVE|(!pathsearchmode&&grponlymode?CACHE1D_OPT_NOSTACK:0));
    findfiles = klistpath(path,kind,CACHE1D_FIND_FILE|(!pathsearchmode&&grponlymode?CACHE1D_OPT_NOSTACK:0));
    for (r = finddirs; r; r=r->next) numdirs++;
    for (r = findfiles; r; r=r->next) numfiles++;

    finddirshigh = finddirs;
    findfileshigh = findfiles;
    currentlist = 0;
    if (findfileshigh) currentlist = 1;
//initprintf("path=%s || kind=%s || numfiles=%d, numdirs=%d\n", path, kind, numfiles, numdirs);
    return(0);
}

// vvv PK ------------------------------------
// copied off menuselect

static const char *g_oldpath=NULL;
static int32_t menuselect_auto(int32_t direction) // 20080104: jump to next (direction!=0) or prev (direction==0) file
{
    const char *boardbasename;

    if (!g_oldpath)
        return -3;  // not inited
    else
        Bmemcpy(selectedboardfilename, g_oldpath, BMAX_PATH);

    if (pathsearchmode)
        Bcanonicalisefilename(selectedboardfilename, 1);  // clips off the last token and compresses relative path
    else
        Bcorrectfilename(selectedboardfilename, 1);

    getfilenames(selectedboardfilename, "*.map");
    if (numfiles==0)
        return -2;

    boardbasename = Bstrrchr(boardfilename,'/'); // PK
    if (!boardbasename)
        boardbasename=boardfilename;
    else
        boardbasename++;

    for (; findfileshigh; findfileshigh=findfileshigh->next)
        if (!Bstrcmp(findfileshigh->name,boardbasename))
            break;

    if (!findfileshigh)
        findfileshigh=findfiles;

    if (direction)
    {
        if (findfileshigh->next)
            findfileshigh=findfileshigh->next;
        else
            return -1;
    }
    else
    {
        if (findfileshigh->prev)
            findfileshigh=findfileshigh->prev;
        else
            return -1;
    }

    Bstrcat(selectedboardfilename, findfileshigh->name);

    return(0);
}
// ^^^ PK ------------------------------------

static int32_t menuselect(void)
{
    int32_t listsize;
    int32_t i;
    char ch, buffer[96], /*PK*/ *boardbasename;
    static char oldpath[BMAX_PATH];
    CACHE1D_FIND_REC *dir;
    int32_t bakpathsearchmode = pathsearchmode;

    g_oldpath=oldpath; //PK: need it in menuselect_auto

    Bstrcpy(selectedboardfilename, oldpath);
    if (pathsearchmode)
        Bcanonicalisefilename(selectedboardfilename, 1);		// clips off the last token and compresses relative path
    else
        Bcorrectfilename(selectedboardfilename, 1);

    getfilenames(selectedboardfilename, "*.map");

    // PK 20080103: start with last selected map
    boardbasename = Bstrrchr(boardfilename,'/');
    if (!boardbasename)
        boardbasename=boardfilename;
    else
        boardbasename++;

    for (; findfileshigh; findfileshigh=findfileshigh->next)
        if (!Bstrcmp(findfileshigh->name,boardbasename))
            break;

    if (!findfileshigh)
        findfileshigh=findfiles;

    _printmessage16("Select map file with arrow keys and enter.");

    ydim16 = ydim-STATUS2DSIZ2;
    listsize = (ydim16-32)/9;

    do
    {
        begindrawing(); //{{{

        CLEARLINES2D(0, ydim16, 0);

        if (pathsearchmode)
            Bstrcpy(buffer,"Local filesystem mode.  Ctrl-F: game filesystem");
        else
            Bsprintf(buffer,"Game filesystem %smode.  Ctrl-F: local filesystem, Ctrl-G: %s",
                     grponlymode?"GRP-only ":"", grponlymode?"all files":"GRP contents only");

        printext16(halfxdim16-(8*Bstrlen(buffer)/2), 4, editorcolors[12],editorcolors[0],buffer,0);

        Bsnprintf(buffer,sizeof(buffer)-1,"(%d dirs, %d files) %s",numdirs,numfiles,selectedboardfilename);
        buffer[sizeof(buffer)-1] = 0;

        printext16(8,ydim16-8-1,editorcolors[8],editorcolors[0],buffer,0);

        if (finddirshigh)
        {
            dir = finddirshigh;
            for (i=(listsize/2)-1; i>=0; i--)
            {
                if (!dir->prev) break;
                else dir=dir->prev;
            }
            for (i=0; ((i<listsize) && dir); i++, dir=dir->next)
            {
                int32_t c = (dir->type == CACHE1D_FIND_DIR ? 2 : 3); //PK
                Bmemset(buffer,0,sizeof(buffer));
                Bstrncpy(buffer,dir->name,25);
                if (Bstrlen(buffer) == 25)
                    buffer[21] = buffer[22] = buffer[23] = '.', buffer[24] = 0;
                if (dir == finddirshigh)
                {
                    if (currentlist == 0) printext16(8,16+9*i,editorcolors[c|8],editorcolors[0],"->",0);
                    printext16(32,16+9*i,editorcolors[c|8],editorcolors[0],buffer,0);
                }
                else
                {
                    printext16(32,16+9*i,editorcolors[c],editorcolors[0],buffer,0);
                }
            }
        }

        if (findfileshigh)
        {
            dir = findfileshigh;
            for (i=(listsize/2)-1; i>=0; i--)
            {
                if (!dir->prev) break;
                else dir=dir->prev;
            }
            for (i=0; ((i<listsize) && dir); i++, dir=dir->next)
            {
                if (dir == findfileshigh)
                {
                    if (currentlist == 1) printext16(240,16+9*i,editorcolors[7|8],editorcolors[0],"->",0);
                    printext16(240+24,16+9*i,editorcolors[7|8],editorcolors[0],dir->name,0);
                }
                else
                {
                    printext16(240+24,16+9*i,editorcolors[7],editorcolors[0],dir->name,0);
                }
            }
        }

        enddrawing(); //}}}
        showframe(1);

        keystatus[0xcb] = 0;
        keystatus[0xcd] = 0;
        keystatus[0xc8] = 0;
        keystatus[0xd0] = 0;
        keystatus[0x1c] = 0;	//enter
        keystatus[0xf] = 0;		//tab
        keystatus[1] = 0;		//esc
        ch = 0;                      //Interesting fakery of ch = getch()
        while (ch == 0)
        {
            if (handleevents())
                if (quitevent)
                {
                    keystatus[1] = 1;
                    quitevent = 0;
                }

            idle();

            ch = bgetchar();

            {
                // JBF 20040208: seek to first name matching pressed character
                CACHE1D_FIND_REC *seeker = currentlist ? findfiles : finddirs;
                if (keystatus[0xc7]||keystatus[0xcf]) // home/end
                {
                    while (keystatus[0xcf]?seeker->next:seeker->prev)
                        seeker = keystatus[0xcf]?seeker->next:seeker->prev;
                    if (seeker)
                    {
                        if (currentlist) findfileshigh = seeker;
                        else finddirshigh = seeker;
                    }
                    ch = keystatus[0xcf]?80:72;
                    keystatus[0xc7] = keystatus[0xcf] = 0;
                }
                else if (keystatus[0xc9]|keystatus[0xd1]) // page up/down
                {
                    seeker = currentlist?findfileshigh:finddirshigh;
                    i = (ydim2d-STATUS2DSIZ2-48)>>5/*3*/;  //PK

                    while (i>0)
                    {
                        if (keystatus[0xd1]?seeker->next:seeker->prev)
                            seeker = keystatus[0xd1]?seeker->next:seeker->prev;
                        i--;
                    }
                    if (seeker)
                    {
                        if (currentlist) findfileshigh = seeker;
                        else finddirshigh = seeker;
                    }
                    ch = keystatus[0xd1]?80:72;
                    keystatus[0xc9] = keystatus[0xd1] = 0;
                }
                else
                {
                    char ch2;
                    if (ch > 0 && ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') ||
                                   (ch >= '0' && ch <= '9') || (ch=='_')))
                    {
#ifdef _WIN32
                        if (ch >= 'a') ch -= ('a'-'A');
#endif
                        while (seeker)
                        {
                            ch2 = seeker->name[0];
#ifdef _WIN32
                            if (ch2 >= 'a' && ch2 <= 'z') ch2 -= ('a'-'A');
#endif
                            if (ch2 == ch) break;
                            seeker = seeker->next;
                        }
                        if (seeker)
                        {
                            if (currentlist) findfileshigh = seeker;
                            else finddirshigh = seeker;
                            continue;
                        }
                    }
                }
            }
            if (keystatus[0xcb]) ch = 9;		// left arr
            if (keystatus[0xcd]) ch = 9;		// right arr
            if (keystatus[0xc8]) ch = 72;   	// up arr
            if (keystatus[0xd0]) ch = 80;   	// down arr
        }

        if (ch==6)  // Ctrl-F
        {
            currentlist = 0;
            pathsearchmode = 1-pathsearchmode;
            if (pathsearchmode)
            {
                Bstrcpy(selectedboardfilename, "");
                Bcanonicalisefilename(selectedboardfilename, 0);
            }
            else Bstrcpy(selectedboardfilename, "/");

            getfilenames(selectedboardfilename, "*.map");
            Bstrcpy(oldpath,selectedboardfilename);
        }
        else if (ch==7)  // Ctrl-G
        {
            if (!pathsearchmode)
            {
                grponlymode = 1-grponlymode;
                getfilenames(selectedboardfilename, "*.map");
                Bstrcpy(oldpath,selectedboardfilename);
            }
        }
        else if (ch == 9)
        {
            if ((currentlist == 0 && findfiles) || (currentlist == 1 && finddirs))
                currentlist = 1-currentlist;
        }
        else if (keystatus[0xc8] /*(ch == 75) || (ch == 72)*/)
        {
            if (currentlist == 0)
            {
                if (finddirshigh && finddirshigh->prev)
                    finddirshigh = finddirshigh->prev;
            }
            else
            {
                if (findfileshigh && findfileshigh->prev)
                    findfileshigh = findfileshigh->prev;
            }
        }
        else if (keystatus[0xd0] /*(ch == 77) || (ch == 80)*/)
        {
            if (currentlist == 0)
            {
                if (finddirshigh && finddirshigh->next)
                    finddirshigh = finddirshigh->next;
            }
            else
            {
                if (findfileshigh && findfileshigh->next)
                    findfileshigh = findfileshigh->next;
            }
        }
        else if ((ch == 13) && (currentlist == 0))
        {
            if (finddirshigh->type == CACHE1D_FIND_DRIVE)
                Bstrcpy(selectedboardfilename, finddirshigh->name);
            else
                Bstrcat(selectedboardfilename, finddirshigh->name);

            Bstrcat(selectedboardfilename, "/");

            if (pathsearchmode)
                Bcanonicalisefilename(selectedboardfilename, 1);
            else
                Bcorrectfilename(selectedboardfilename, 1);

            Bstrcpy(oldpath,selectedboardfilename);
            //printf("Changing directories to: %s\n", selectedboardfilename);

            getfilenames(selectedboardfilename, "*.map");
            ch = 0;

            begindrawing();
            CLEARLINES2D(0, ydim16, 0);
            enddrawing();
            showframe(1);
        }

        if (ch == 13 && !findfileshigh) ch = 0;
    }
    while ((ch != 13) && (ch != 27));

    if (ch == 13)
    {
        Bstrcat(selectedboardfilename, findfileshigh->name);
        //printf("Selected file: %s\n", selectedboardfilename);

        return(0);
    }

    pathsearchmode = bakpathsearchmode;

    return(-1);
}

static inline int32_t imod(int32_t a, int32_t b)
{
    if (a >= 0) return(a%b);
    return(((a+1)%b)+b-1);
}

int32_t fillsector(int16_t sectnum, int32_t fillcolor)
{
    int32_t x1, x2, y1, y2, sy, y, daminy;
    int32_t lborder, rborder, uborder, dborder, miny, maxy, dax;
    int16_t z, zz, startwall, endwall, fillcnt;

    char col;

    if (fillcolor < 0)
        col = 159-klabs(sintable[((totalclock<<3)&2047)]>>11);
    else
        col = fillcolor;

    lborder = 0; rborder = xdim;
    y = OSD_GetRowsCur();
    uborder = (y>=0)?(y+1)*8:0; dborder = ydim16-STATUS2DSIZ2;

    if (sectnum == -1)
        return 0;

    miny = dborder-1;
    maxy = uborder;

    startwall = sector[sectnum].wallptr;
    endwall = startwall + sector[sectnum].wallnum - 1;
    for (z=startwall; z<=endwall; z++)
    {
        screencoords(&x1,&y1, wall[z].x-pos.x,wall[z].y-pos.y, zoom);
        if (m32_sideview)
            y1 += getscreenvdisp(getflorzofslope(sectnum,wall[z].x,wall[z].y)-pos.z, zoom);

        x1 += halfxdim16;
        y1 += midydim16;

        if (m32_sideview)
        {
            tempxyar[z][0] = x1;
            tempxyar[z][1] = y1;
        }

        miny = min(miny, y1);
        maxy = max(maxy, y1);
    }

    if (miny < uborder) miny = uborder;
    if (maxy >= dborder) maxy = dborder-1;

    daminy = miny+2 - imod(miny+2,3);
    if (sector[sectnum].floorz > minhlsectorfloorz)
        daminy++;

//+((totalclock>>2)&3)
    for (sy=daminy; sy<=maxy; sy+=3)	// JBF 20040116: numframes%3 -> (totalclock>>2)&3
    {
        y = pos.y + ((sy-midydim16)<<14)/zoom;

        fillist[0] = lborder; fillcnt = 1;
        for (z=startwall; z<=endwall; z++)
        {
            if (m32_sideview)
            {
                x1 = tempxyar[z][0];
                y1 = tempxyar[z][1];
                x2 = tempxyar[wall[z].point2][0];
                y2 = tempxyar[wall[z].point2][1];

                if (y1 > y2)
                {
                    swaplong(&x1, &x2);
                    swaplong(&y1, &y2);
                }

                if (y1 <= sy && sy < y2)
                {
                    if (fillcnt == sizeof(fillist)/sizeof(fillist[0]))
                        break;

                    x1 += scale(sy-y1, x2-x1, y2-y1);
                    fillist[fillcnt++] = x1;
                }
            }
            else
            {
                x1 = wall[z].x; x2 = POINT2(z).x;
                y1 = wall[z].y; y2 = POINT2(z).y;

                if (y1 > y2)
                {
                    swaplong(&x1, &x2);
                    swaplong(&y1, &y2);
                }

                if (y1 <= y && y < y2)
                    //if (x1*(y-y2) + x2*(y1-y) <= 0)
                {
                    dax = x1 + scale(y-y1, x2-x1, y2-y1);
                    dax = halfxdim16 + (((dax-pos.x)*zoom)>>14);
                    if (dax >= lborder)
                    {
                        if (fillcnt == sizeof(fillist)/sizeof(fillist[0]))
                            break;

                        fillist[fillcnt++] = dax;
                    }
                }
            }
        }

        if (fillcnt > 0)
        {
            for (z=1; z<fillcnt; z++)
                for (zz=0; zz<z; zz++)
                    if (fillist[z] < fillist[zz])
                        swaplong(&fillist[z], &fillist[zz]);

            for (z=(fillcnt&1); z<fillcnt-1; z+=2)
            {
                if (fillist[z] > rborder)
                    break;
                if (fillist[z+1] > rborder)
                    fillist[z+1] = rborder;

                drawline16(fillist[z]+1,sy, fillist[z+1]-1,sy, col);  //editorcolors[fillcolor]
            }
        }
    }

    return(0);
}

static int16_t whitelinescan(int16_t sucksect, int16_t dalinehighlight)
{
    int32_t i, j, k;
    int16_t tnewnumwalls;

    if (numsectors >= MAXSECTORS)
        return MAXWALLS+1;

    Bmemcpy(&sector[numsectors], &sector[sucksect], sizeof(sectortype));
    sector[numsectors].wallptr = numwalls;
    sector[numsectors].wallnum = 0;

    i = dalinehighlight;
    tnewnumwalls = numwalls;
    do
    {
        if (tnewnumwalls >= MAXWALLS)
            return MAXWALLS+1;

        j = lastwall(i);
        if (wall[j].nextwall >= 0)
        {
            j = wall[j].point2;
            for (k=0; k<numwalls; k++)
            {
                if (POINT2(k).x == wall[j].x && POINT2(k).y == wall[j].y)
                    if (wall[k].nextwall == -1)
                    {
                        j = k;
                        break;
                    }
            }
        }

        Bmemcpy(&wall[tnewnumwalls], &wall[i], sizeof(walltype));

        wall[tnewnumwalls].nextwall = j;
        wall[tnewnumwalls].nextsector = sectorofwall(j);

        tnewnumwalls++;
        sector[numsectors].wallnum++;

        i = j;
    }
    while (i != dalinehighlight);

    for (i=numwalls; i<tnewnumwalls-1; i++)
        wall[i].point2 = i+1;
    wall[tnewnumwalls-1].point2 = numwalls;

    if (clockdir(numwalls) == 1)
        return(-1);
    else
        return(tnewnumwalls);
}

int32_t loadnames(const char *namesfile)
{
    char buffer[1024], *p, *name, *number, *endptr;
    int32_t num, syms=0, line=0, a, comment=0;
    BFILE *fp;

    Bstrncpy(buffer, namesfile, sizeof(buffer));
    buffer[sizeof(buffer)-1] = 0;

    p = buffer;
    while (*p)
    {
        *p = Btoupper(*p);
        p++;
    }

    fp = fopenfrompath(buffer,"r");
    if (!fp)
    {
        p = buffer;
        while (*p)
        {
            *p = Btolower(*p);
            p++;
        }

        if ((fp = fopenfrompath(buffer,"r")) == NULL)
        {
            initprintf("Failed to open %s\n", buffer);
            return -1;
        }
    }

    //clearbufbyte(names, sizeof(names), 0);
    Bmemset(names,0,sizeof(names));

    initprintf("Loading %s\n", buffer);

    while (Bfgets(buffer, 1024, fp))
    {
        a = Bstrlen(buffer);
        if (a >= 1)
        {
            if (a > 1)
                if (buffer[a-2] == '\r') buffer[a-2] = 0;
            if (buffer[a-1] == '\n') buffer[a-1] = 0;
        }

        p = buffer;
        line++;
        while (*p == 32) p++;
        if (*p == 0) continue;	// blank line

        if (*p == '#' && !comment)
        {
            p++;
            while (*p == 32) p++;
            if (*p == 0) continue;	// null directive

            if (!Bstrncmp(p, "define ", 7))
            {
                // #define_...
                p += 7;
                while (*p == 32) p++;
                if (*p == 0)
                {
                    initprintf("Error: Malformed #define at line %d\n", line-1);
                    continue;
                }

                name = p;
                while (*p != 32 && *p != 0) p++;
                if (*p == 32)
                {
                    *(p++) = 0;
                    while (*p == 32) p++;
                    if (*p == 0)  	// #define_NAME with no number
                    {
                        initprintf("Error: No number given for name \"%s\" (line %d)\n", name, line-1);
                        continue;
                    }

                    number = p;
                    while (*p != 0 && *p != 32) p++;
                    *p = 0;

                    // add to list
                    num = Bstrtol(number, &endptr, 10);
                    if (*endptr != 0)
                    {
                        p = endptr;
                        goto badline;
                    }
                    //printf("Grokked \"%s\" -> \"%d\"\n", name, num);
                    if (num < 0 || num >= MAXTILES)
                    {
                        initprintf("Error: Constant %d for name \"%s\" out of range (line %d)\n", num, name, line-1);
                        continue;
                    }

                    if (Bstrlen(name) > 24)
                        initprintf("Warning: Name \"%s\" longer than 24 characters (line %d). Truncating.\n", name, line-1);

                    Bstrncpy(names[num], name, 24);
                    names[num][24] = 0;

                    syms++;

                    continue;
                }
                else  	// #define_NAME with no number
                {
                    initprintf("Error: No number given for name \"%s\" (line %d)\n", name, line-1);
                    continue;
                }
            }
            else goto badline;
        }
        else if (*p == '/')
        {
            if (*(p+1) == '*') {comment++; continue;}
            if (*(p+1) == '/') continue;	// comment
        }
        else if (*p == '*' && p[1] == '/')
        {
            comment--;
            continue;
        }
        else if (comment)
            continue;
badline:
        initprintf("Error: Invalid statement found at character %d on line %d\n", (int32_t)(p-buffer), line-1);
    }
    initprintf("Read %d lines, loaded %d names.\n", line, syms);

    Bfclose(fp);
    return 0;
}

void printcoords16(int32_t posxe, int32_t posye, int16_t ange)
{
    char snotbuf[80];
    int32_t i, m;
    int32_t v8 = (numsectors > MAXSECTORSV7 || numwalls > MAXWALLSV7 ||
                  numsprites > MAXSPRITESV7 || numyaxbunches > 0);

    Bsprintf(snotbuf,"x:%d y:%d ang:%d r%d",posxe,posye,ange,map_revision-1);
    i = 0;
    while ((snotbuf[i] != 0) && (i < 33))
        i++;
    while (i < 33)
    {
        snotbuf[i] = 32;
        i++;
    }
    snotbuf[33] = 0;

    clearministatbar16();

    printext16(8, ydim-STATUS2DSIZ+128, whitecol, -1, snotbuf,0);

    if (highlightcnt<=0 && highlightsectorcnt<=0)
    {
        Bsprintf(snotbuf,"%d/%d %s. %d/%d walls %d/%d spri.",
                 numsectors, v8?MAXSECTORSV8:MAXSECTORSV7,
                 numyaxbunches>0 ? "SECT":"sect",
                 numwalls, v8?MAXWALLSV8:MAXWALLSV7,
                 numsprites, v8?MAXSPRITESV8:MAXSPRITESV7);
    }
    else
    {
        if (highlightcnt>0)
        {
            m = 0;
            for (i=0; i<highlightcnt; i++)
                m += !!(highlight[i]&16384);
            Bsprintf(snotbuf, "%d sprites, %d walls selected", m, highlightcnt-m);
        }
        else if (highlightsectorcnt>0)
            Bsprintf(snotbuf, "%d sectors selected", highlightsectorcnt);
        else
            snotbuf[0] = 0;

        v8 = 1;
    }

    m = xdim/8 - 264/8;
    m = clamp(m, 1, (signed)sizeof(snotbuf)-1);

    i = 0;
    while (snotbuf[i] && i < m)
        i++;
    while (i < m)
    {
        snotbuf[i] = 32;
        i++;
    }
    snotbuf[m] = 0;

    printext16(264, ydim-STATUS2DSIZ+128, v8?editorcolors[10]:whitecol, -1, snotbuf,0);
}

void updatenumsprites(void)
{
    int32_t i;

    numsprites = 0;
    for (i=0; i<MAXSPRITES; i++)
        numsprites += (sprite[i].statnum != MAXSTATUS);
}

static void copysector(int16_t soursector, int16_t destsector, int16_t deststartwall, char copystat,
                       const int16_t *oldtonewsect)
{
    int16_t i, j, k, m, newnumwalls, startwall, endwall;

    newnumwalls = deststartwall;  //erase existing sector fragments

    //duplicate walls
    startwall = sector[soursector].wallptr;
    endwall = startwall + sector[soursector].wallnum;
    for (j=startwall; j<endwall; j++)
    {
        Bmemcpy(&wall[newnumwalls], &wall[j], sizeof(walltype));
        wall[newnumwalls].point2 += deststartwall-startwall;

        if (wall[newnumwalls].nextwall < 0)
        {
            onextwall[newnumwalls] = onextwall[j];
        }
        else
        {
            k = wall[newnumwalls].nextsector;
            if (oldtonewsect && oldtonewsect[k]>=0)
            {
                wall[newnumwalls].nextsector = oldtonewsect[k];
                m = 0;
                for (i=0; i<highlightsectorcnt; i++)
                {
                    if (highlightsector[i]==k)
                        break;
                    m += sector[highlightsector[i]].wallnum;
                }

                if (i==highlightsectorcnt)
                {
                    message("internal error in copysector(): i==highlightsectorcnt");
                    goto nonextsector;
                }
                else if (highlightsector[i]==soursector)
                {
                    message("internal error in copysector(): highlightsector[i]==soursector");
                    goto nonextsector;
                }
                wall[newnumwalls].nextwall = numwalls + m + (wall[j].nextwall-sector[k].wallptr);
            }
            else
            {
nonextsector:
                wall[newnumwalls].nextsector = -1;
                wall[newnumwalls].nextwall = -1;
            }
            // the below code is incorrect in the general case since, in a set of
            // selected sectors, the order may not be the same as the destination ones
//            wall[newnumwalls].nextwall += deststartwall-startwall;
//            wall[newnumwalls].nextsector += destsector-soursector;
        }

        newnumwalls++;
    }

    //for(j=deststartwall;j<newnumwalls;j++)
    //{
    //   if (wall[j].nextwall >= 0)
    //      checksectorpointer(wall[j].nextwall,wall[j].nextsector);
    //   checksectorpointer((short)j,destsector);
    //}

    if (newnumwalls > deststartwall)
    {
        //duplicate sectors
        Bmemcpy(&sector[destsector],&sector[soursector],sizeof(sectortype));
        sector[destsector].wallptr = deststartwall;
        sector[destsector].wallnum = newnumwalls-deststartwall;

        if (copystat == 1)
        {
            //duplicate sprites
            j = headspritesect[soursector];
            while (j >= 0)
            {
                m = insertsprite(destsector,sprite[j].statnum);
                if (m<0)
                {
                    message("Some sprites not duplicated because limit was reached.");
                    break;
                }

                Bmemcpy(&sprite[m],&sprite[j],sizeof(spritetype));
                sprite[m].sectnum = destsector;   //Don't let memcpy overwrite sector!

                j = nextspritesect[j];
            }
        }
    }
}

#define DOPRINT(Yofs, fmt, ...) \
    Bsprintf(snotbuf, fmt, ## __VA_ARGS__); \
    printext16(8+col*200, ydim/*-(row*96)*/-STATUS2DSIZ+Yofs, color, -1, snotbuf, 0);

void showsectordata(int16_t sectnum, int16_t small)
{
    sectortype *sec;
    char snotbuf[80];
    int32_t col=0;  //,row = 0;
    int32_t color = small ? whitecol : editorcolors[11];

    sec = &sector[sectnum];

    if (small)
    {
        _printmessage16("^10Sector %d %s ^O(F7 to edit)", sectnum, ExtGetSectorCaption(sectnum));
        return;
    }

    DOPRINT(32, "^10Sector %d", sectnum);
    DOPRINT(48, "Firstwall: %d", sec->wallptr);
    DOPRINT(56, "Numberofwalls: %d", sec->wallnum);
    DOPRINT(64, "Firstsprite: %d", headspritesect[sectnum]);
    DOPRINT(72, "Tags: %d, %d", sec->hitag, sec->lotag);
    DOPRINT(80, "     (0x%x), (0x%x)", sec->hitag, sec->lotag);
    DOPRINT(88, "Extra: %d", sec->extra);
    DOPRINT(96, "Visibility: %d", sec->visibility);
    DOPRINT(104, "Pixel height: %d", (sec->floorz-sec->ceilingz)>>8);

    col++;

    DOPRINT(32, "^10CEILING:^O");
    DOPRINT(48, "Flags (hex): %x", sec->ceilingstat);
    {
        int32_t xp=sec->ceilingxpanning, yp=sec->ceilingypanning;
#ifdef YAX_ENABLE
        if (yax_getbunch(searchsector, YAX_CEILING) >= 0)
            xp = yp = 0;
#endif
        DOPRINT(56, "(X,Y)pan: %d, %d", xp, yp);
    }
    DOPRINT(64, "Shade byte: %d", sec->ceilingshade);
    DOPRINT(72, "Z-coordinate: %d", sec->ceilingz);
    DOPRINT(80, "Tile number: %d", sec->ceilingpicnum);
    DOPRINT(88, "Ceiling heinum: %d", sec->ceilingheinum);
    DOPRINT(96, "Palookup number: %d", sec->ceilingpal);
#ifdef YAX_ENABLE
    DOPRINT(104, "Bunch number: %d", yax_getbunch(sectnum, YAX_CEILING));
#endif

    col++;

    DOPRINT(32, "^10FLOOR:^O");
    DOPRINT(48, "Flags (hex): %x", sec->floorstat);
    {
        int32_t xp=sec->floorxpanning, yp=sec->floorypanning;
#ifdef YAX_ENABLE
        if (yax_getbunch(searchsector, YAX_FLOOR) >= 0)
            xp = yp = 0;
#endif
        DOPRINT(56, "(X,Y)pan: %d, %d", xp, yp);
    }
    DOPRINT(64, "Shade byte: %d", sec->floorshade);
    DOPRINT(72, "Z-coordinate: %d", sec->floorz);
    DOPRINT(80, "Tile number: %d", sec->floorpicnum);
    DOPRINT(88, "Floor heinum: %d", sec->floorheinum);
    DOPRINT(96, "Palookup number: %d", sec->floorpal);
#ifdef YAX_ENABLE
    DOPRINT(104, "Bunch number: %d", yax_getbunch(sectnum, YAX_FLOOR));
#endif
}

void showwalldata(int16_t wallnum, int16_t small)
{
    walltype *wal;
    int32_t sec;
    char snotbuf[80];
    int32_t col=0; //, row = 0;
    int32_t color = small ? whitecol : editorcolors[11];

    wal = &wall[wallnum];

    if (small)
    {
        _printmessage16("^10Wall %d %s ^O(F8 to edit)", wallnum,
                        ExtGetWallCaption(wallnum));
        return;
    }

    DOPRINT(32, "^10Wall %d", wallnum);
    DOPRINT(48, "X-coordinate: %d", wal->x);
    DOPRINT(56, "Y-coordinate: %d", wal->y);
    DOPRINT(64, "Point2: %d", wal->point2);
    DOPRINT(72, "Sector: ^010%d", sectorofwall(wallnum));

    DOPRINT(88, "Tags: %d,  %d", wal->hitag, wal->lotag);
    DOPRINT(96, "     (0x%x),  (0x%x)", wal->hitag, wal->lotag);

    col++;

    DOPRINT(32, "^10%s^O", (wal->picnum>=0 && wal->picnum<MAXTILES) ? names[wal->picnum] : "!INVALID!");
    DOPRINT(48, "Flags (hex): %x", wal->cstat);
    DOPRINT(56, "Shade: %d", wal->shade);
    DOPRINT(64, "Pal: %d", wal->pal);
    DOPRINT(72, "(X,Y)repeat: %d, %d", wal->xrepeat, wal->yrepeat);
    DOPRINT(80, "(X,Y)pan: %d, %d", wal->xpanning, wal->ypanning);
    DOPRINT(88, "Tile number: %d", wal->picnum);
    DOPRINT(96, "OverTile number: %d", wal->overpicnum);

    col++;

    DOPRINT(48-(small?16:0), "nextsector: %d", wal->nextsector);
    DOPRINT(56-(small?16:0), "nextwall: %d", wal->nextwall);

    DOPRINT(72-(small?16:0), "Extra: %d", wal->extra);

    // TX 20050102 I'm not sure what unit dist<<4 is supposed to be, but dist itself is correct in terms of game coordinates as one would expect
    DOPRINT(96-(small?16:0),  "Wall length: %d",  wallength(wallnum));

    sec = sectorofwall(wallnum);
    DOPRINT(104-(small?16:0), "Pixel height: %d", (sector[sec].floorz-sector[sec].ceilingz)>>8);
}

void showspritedata(int16_t spritenum, int16_t small)
{
    spritetype *spr;
    char snotbuf[80];
    int32_t col=0; //, row = 0;
    int32_t color = small ? whitecol : editorcolors[11];

    spr = &sprite[spritenum];

    if (small)
    {
        _printmessage16("^10Sprite %d %s ^O(F8 to edit)",spritenum, ExtGetSpriteCaption(spritenum));
        return;
    }

    DOPRINT(32, "^10Sprite %d", spritenum);
    DOPRINT(48, "X-coordinate: %d", spr->x);
    DOPRINT(56, "Y-coordinate: %d", spr->y);
    DOPRINT(64, "Z-coordinate: %d", spr->z);

    DOPRINT(72, "Sectnum: ^010%d", spr->sectnum);
    DOPRINT(80, "Statnum: %d", spr->statnum);

    DOPRINT(96, "Tags: %d,  %d", spr->hitag, spr->lotag);
    DOPRINT(104, "     (0x%x),  (0x%x)", spr->hitag, spr->lotag);

    col++;

    DOPRINT(32, "^10,0                        ^O");  // 24 blanks
    DOPRINT(32, "^10%s^O", (spr->picnum>=0 && spr->picnum<MAXTILES) ? names[spr->picnum] : "!INVALID!");
    DOPRINT(48, "Flags (hex): %x", spr->cstat);
    DOPRINT(56, "Shade: %d", spr->shade);
    DOPRINT(64, "Pal: %d", spr->pal);
    DOPRINT(72, "(X,Y)repeat: %d, %d", spr->xrepeat, spr->yrepeat);
    DOPRINT(80, "(X,Y)offset: %d, %d", spr->xoffset, spr->yoffset);
    DOPRINT(88, "Tile number: %d", spr->picnum);

    col++;

    DOPRINT(48, "Angle (2048 degrees): %d", spr->ang);
    DOPRINT(56, "X-Velocity: %d", spr->xvel);
    DOPRINT(64, "Y-Velocity: %d", spr->yvel);
    DOPRINT(72, "Z-Velocity: %d", spr->zvel);
    DOPRINT(80, "Owner: %d", spr->owner);
    DOPRINT(88, "Clipdist: %d", spr->clipdist);
    DOPRINT(96, "Extra: %d", spr->extra);
}

#undef DOPRINT

// gets called once per totalclock increment since last call
void keytimerstuff(void)
{
    if (DOWN_BK(STRAFE) == 0)
    {
        if (DOWN_BK(TURNLEFT)) angvel = max(angvel-pk_turnaccel, -128);
        if (DOWN_BK(TURNRIGHT)) angvel = min(angvel+pk_turnaccel, 127);
    }
    else
    {
        if (DOWN_BK(TURNLEFT)) svel = min(svel+16, 255); // svel and vel aren't even chars...
        if (DOWN_BK(TURNRIGHT)) svel = max(svel-16, -256);
    }
    if (DOWN_BK(MOVEFORWARD))  vel = min(vel+16, 255);
    if (DOWN_BK(MOVEBACKWARD)) vel = max(vel-16, -256);
    /*  if (DOWN_BK(STRAFELEFT))  svel = min(svel+8, 127);
    	if (DOWN_BK(STRAFERIGHT)) svel = max(svel-8, -128); */

    if (angvel < 0) angvel = min(angvel+pk_turndecel, 0);
    if (angvel > 0) angvel = max(angvel-pk_turndecel, 0);
    if (svel < 0) svel = min(svel+6, 0);
    if (svel > 0) svel = max(svel-6, 0);
    if (vel < 0) vel = min(vel+6, 0);
    if (vel > 0) vel = max(vel-6, 0);
    /*    if(mlook) pos.z -= (horiz-101)*(vel/40); */
}

#if 0
int32_t snfillprintf(char *outbuf, size_t bufsiz, int32_t fill, const char *fmt, ...)
{
    char tmpstr[256];
    int32_t nwritten, ofs;
    va_list va;

    va_start(va, fmt);
    nwritten = Bvsnprintf(tmpstr, bufsiz, fmt, va);
    va_end(va);

    ofs = min(nwritten, (signed)bufsiz-1);
    Bmemset(outbuf, fill, bufsiz-ofs);

    return ofs;
}
#endif

void _printmessage16(const char *fmt, ...)
{
    int32_t i, ybase;
    char snotbuf[156];
    char tmpstr[160];
    va_list va;

    va_start(va, fmt);
    Bvsnprintf(tmpstr, 156, fmt, va);
    va_end(va);

    i = 0;
    while (tmpstr[i] && i < 146)
    {
        snotbuf[i] = tmpstr[i];
        i++;
    }
    snotbuf[i] = 0;
    if (lastpm16time == totalclock)
        Bstrcpy(lastpm16buf, snotbuf);

    clearministatbar16();

    ybase = ydim-STATUS2DSIZ+128-8;

    printext16(8, ybase+8, whitecol, -1, snotbuf, 0);
}

void printmessage256(int32_t x, int32_t y, const char *name)
{
    char snotbuf[80];
    int32_t i;

    i = 0;
    while (name[i] && i < 62)
    {
        snotbuf[i] = name[i];
        i++;
    }
    while (i < 62)
    {
        snotbuf[i] = 32;
        i++;
    }
    snotbuf[62] = 0;
    printext256(x+2,y+2,0,-1,snotbuf,0);
    printext256(x,y,whitecol,-1,snotbuf,0);
}

//Find closest point (*dax, *day) on wall (dawall) to (x, y)
static void getclosestpointonwall(int32_t x, int32_t y, int32_t dawall, int32_t *nx, int32_t *ny,
                                  int32_t maybe_screen_coord_p)
{
    int64_t i, j, wx,wy, wx2,wy2, dx, dy;

    if (m32_sideview && maybe_screen_coord_p)
    {
        wx = m32_wallscreenxy[dawall][0];
        wy = m32_wallscreenxy[dawall][1];
        wx2 = m32_wallscreenxy[wall[dawall].point2][0];
        wy2 = m32_wallscreenxy[wall[dawall].point2][1];
    }
    else
    {
        wx = wall[dawall].x;
        wy = wall[dawall].y;
        wx2 = POINT2(dawall).x;
        wy2 = POINT2(dawall).y;
    }

    dx = wx2 - wx;
    dy = wy2 - wy;
    i = dx*(x-wx) + dy*(y-wy);
    if (i <= 0) { *nx = wx; *ny = wy; return; }
    j = dx*dx + dy*dy;
    if (i >= j) { *nx = wx2; *ny = wy2; return; }
    i=((i<<15)/j)<<15;
    *nx = wx + ((dx*i)>>30);
    *ny = wy + ((dy*i)>>30);
}

static void initcrc(void)
{
    int32_t i, j, k, a;

    for (j=0; j<256; j++)   //Calculate CRC table
    {
        k = (j<<8); a = 0;
        for (i=7; i>=0; i--)
        {
            if (((k^a)&0x8000) > 0)
                a = ((a<<1)&65535) ^ 0x1021;   //0x1021 = genpoly
            else
                a = ((a<<1)&65535);
            k = ((k<<1)&65535);
        }
        crctable[j] = (a&65535);
    }
}

static int32_t GetWallBaseZ(int32_t wallnum)
{
    int32_t z=0, sectnum, nextsec;

    sectnum = sectorofwall(wallnum);
    nextsec = wall[wallnum].nextsector;

    if (nextsec == -1)  //1-sided wall
    {
        if (wall[wallnum].cstat&4)  // floor-aligned
            z = sector[sectnum].floorz;
        else
            z = sector[sectnum].ceilingz;
    }
    else  //2-sided wall
    {
        if (wall[wallnum].cstat&4)
            z = sector[sectnum].ceilingz;
        else
        {
            if (sector[nextsec].ceilingz > sector[sectnum].ceilingz)
                z = sector[nextsec].ceilingz;   //top step
            if (sector[nextsec].floorz < sector[sectnum].floorz)
                z = sector[nextsec].floorz;   //bottom step
        }
    }
    return(z);
}

static void AlignWalls(int32_t w0, int32_t z0, int32_t w1, int32_t z1, int32_t tilenum)
{
    int32_t n;

    //do the x alignment
    wall[w1].xpanning = (uint8_t)((wall[w0].xpanning + (wall[w0].xrepeat<<3))%tilesizx[tilenum]);

    for (n=picsiz[tilenum]>>4; (1<<n)<tilesizy[tilenum]; n++);

    wall[w1].yrepeat = wall[w0].yrepeat;
    wall[w1].ypanning = (uint8_t)(wall[w0].ypanning + (((z1-z0)*wall[w0].yrepeat)>>(n+3)));
}

void AlignWallPoint2(int32_t w0)
{
    int32_t w1 = wall[w0].point2;
    AlignWalls(w0,GetWallBaseZ(w0), w1,GetWallBaseZ(w1), wall[w0].picnum);
}

#define ALIGN_WALLS_CSTAT_MASK (4+8+256)

// flags:
//  1: recurse nextwalls
//  2: iterate point2's
//  4: carry pixel width from first wall over to the rest
int32_t AutoAlignWalls(int32_t w0, uint32_t flags, int32_t nrecurs)
{
    int32_t z0, z1, tilenum, w1, visible, nextsec, sectnum;
    static int32_t numaligned, wall0, cstat0;
    static uint32_t lenrepquot;

    tilenum = wall[w0].picnum;

    if (nrecurs == 0)
    {
        //clear visited bits
        Bmemset(visited, 0, sizeof(visited));
        visited[w0>>3] |= (1<<(w0&7));
        numaligned = 0;
        lenrepquot = getlenbyrep(wallength(w0), wall[w0].xrepeat);
        wall0 = w0;
        cstat0 = wall[w0].cstat & ALIGN_WALLS_CSTAT_MASK;  // top/bottom orientation; x/y-flip
    }

    z0 = GetWallBaseZ(w0);

    w1 = wall[w0].point2;

    //loop through walls at this vertex in CCW order
    while (1)
    {
        //break if this wall would connect us in a loop
        if (visited[w1>>3]&(1<<(w1&7)))
            break;

        visited[w1>>3] |= (1<<(w1&7));

        //break if reached back of left wall
        if (wall[w1].nextwall == w0)
            break;

        if (wall[w1].picnum == tilenum)
        {
            z1 = GetWallBaseZ(w1);
            visible = 0;

            nextsec = wall[w1].nextsector;
            if (nextsec < 0)
                visible = 1;
            else
            {
                int32_t cz,fz, czn,fzn;

                //ignore two sided walls that have no visible face
                sectnum = NEXTWALL(w1).nextsector;
                getzsofslope(sectnum, wall[w1].x,wall[w1].y, &cz, &fz);
                getzsofslope(nextsec, wall[w1].x,wall[w1].y, &czn, &fzn);

                if (cz < czn || fz > fzn)
                    visible = 1;
            }

            if (visible)
            {
                if ((flags&4) && w0!=wall0)
                    fixxrepeat(w0, lenrepquot);
                AlignWalls(w0,z0, w1,z1, tilenum);
                wall[w1].cstat &= ~ALIGN_WALLS_CSTAT_MASK;
                wall[w1].cstat |= cstat0;
                numaligned++;

                //if wall was 1-sided, no need to recurse
                if (wall[w1].nextwall < 0)
                {
                    if (!(flags&2))
                        break;
                    w0 = w1;
                    z0 = GetWallBaseZ(w0);
                    w1 = wall[w0].point2;
                    continue;
                }
                else if (flags&1)
                    AutoAlignWalls(w1, flags, nrecurs+1);
            }
        }

        if (wall[w1].nextwall < 0 || !(flags&2))
            break;
        w1 = NEXTWALL(w1).point2;
    }

    return numaligned;
}

#define PLAYTEST_MAPNAME "autosave_playtest.map"

void test_map(int32_t mode)
{
    if (!mode)
        updatesector(pos.x, pos.y, &cursectnum);
    else
        updatesector(startposx, startposy, &startsectnum);

    if ((!mode && cursectnum >= 0) || (mode && startsectnum >= 0))
    {
        char *param = " -map " PLAYTEST_MAPNAME " -noinstancechecking";
        char *fullparam;
        char current_cwd[BMAX_PATH];
        int32_t slen = 0;
        BFILE *fp;

        if ((program_origcwd[0] == '\0') || !getcwd(current_cwd, BMAX_PATH))
            current_cwd[0] = '\0';
        else // Before we check if file exists, for the case there's no absolute path.
            chdir(program_origcwd);

        fp = fopen(game_executable, "rb"); // File exists?
        if (fp != NULL)
            fclose(fp);
        else
        {
#ifdef _WIN32
            fullparam = Bstrrchr(mapster32_fullpath, '\\');
#else
            fullparam = Bstrrchr(mapster32_fullpath, '/');
#endif
            if (fullparam)
            {
                slen = fullparam-mapster32_fullpath+1;
                Bstrncpy(game_executable, mapster32_fullpath, slen);
                // game_executable is now expected to not be NULL-terminated!
                Bstrcpy(game_executable+slen, DEFAULT_GAME_EXEC);
            }
            else
                Bstrcpy(game_executable, DEFAULT_GAME_LOCAL_EXEC);
        }

        if (current_cwd[0] != '\0') // Temporarily changing back,
            chdir(current_cwd);     // after checking if file exists.

        if (testplay_addparam)
            slen = Bstrlen(testplay_addparam);

        // Considering the NULL character, quatation marks
        // and a possible extra space not in testplay_addparam,
        // the length should be Bstrlen(game_executable)+Bstrlen(param)+(slen+1)+2+1.

        fullparam = Bmalloc(Bstrlen(game_executable)+Bstrlen(param)+slen+4);
        Bsprintf(fullparam,"\"%s\"",game_executable);

        if (testplay_addparam)
        {
            Bstrcat(fullparam, " ");
            Bstrcat(fullparam, testplay_addparam);
        }
        Bstrcat(fullparam, param);

        fixspritesectors();   //Do this before saving!
        ExtPreSaveMap();
        if (mode)
            saveboard(PLAYTEST_MAPNAME,&startposx,&startposy,&startposz,&startang,&startsectnum);
        else
            saveboard(PLAYTEST_MAPNAME,&pos.x,&pos.y,&pos.z,&ang,&cursectnum);

        message("Board saved to " PLAYTEST_MAPNAME ". Starting the game...");
        OSD_Printf("...as `%s'\n", fullparam);

        showframe(1);
        uninitmouse();
#ifdef _WIN32
        {
            STARTUPINFO si;
            PROCESS_INFORMATION pi;

            ZeroMemory(&si,sizeof(si));
            ZeroMemory(&pi,sizeof(pi));
            si.cb = sizeof(si);

            if (!CreateProcess(NULL,fullparam,NULL,NULL,0,0,NULL,NULL,&si,&pi))
                message("Error launching the game!");
            else WaitForSingleObject(pi.hProcess,INFINITE);
        }
#else
        if (current_cwd[0] != '\0')
        {
            chdir(program_origcwd);
            if (system(fullparam))
                message("Error launching the game!");
            chdir(current_cwd);
        }
        else system(fullparam);
#endif
        printmessage16("Game process exited");
        initmouse();
        clearkeys();

        Bfree(fullparam);
    }
    else
        printmessage16("Position must be in valid player space to test map!");
}
