Subversion Repositories eduke32

Rev

Rev 8425 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
6302 hendricks2 1
// This object is shared by the editors of *all* Build games!
4561 hendricks2 2
 
3
#include "compat.h"
4
#include "keys.h"
5
#include "build.h"
6
#include "cache1d.h"
4584 helixhorne 7
#ifdef POLYMER
8
# include "polymer.h"
9
#endif
4561 hendricks2 10
#include "editor.h"
11
#include "renderlayer.h"
12
 
13
#include "m32script.h"
14
#include "m32def.h"
15
 
16
#include "lz4.h"
17
#include "xxhash.h"
18
 
6302 hendricks2 19
// XXX: This breaks editors for games other than Duke. The OSD needs a way to specify colors in abstract instead of concatenating palswap escape sequences.
6300 terminx 20
#include "common_game.h"
21
 
7359 hendricks2 22
#include "vfs.h"
23
 
4561 hendricks2 24
//////////////////// Key stuff ////////////////////
25
 
26
#define eitherALT   (keystatus[KEYSC_LALT] || keystatus[KEYSC_RALT])
27
#define eitherCTRL  (keystatus[KEYSC_LCTRL] || keystatus[KEYSC_RCTRL])
28
#define eitherSHIFT (keystatus[KEYSC_LSHIFT] || keystatus[KEYSC_RSHIFT])
29
 
30
#define PRESSED_KEYSC(Key) (keystatus[KEYSC_##Key] && !(keystatus[KEYSC_##Key]=0))
31
 
32
////
33
 
34
// All these variables need verification that all relevant editor stubs are actually implementing them correctly.
35
 
36
char getmessage[162], getmessageleng;
37
int32_t getmessagetimeoff; //, charsperline;
38
 
39
int32_t mousxplc, mousyplc;
40
int32_t mouseaction;
41
 
42
char *scripthist[SCRIPTHISTSIZ];
43
int32_t scripthistend;
44
 
45
int32_t g_lazy_tileselector;
46
int32_t fixmaponsave_sprites = 1;
47
 
48
int32_t showambiencesounds=2;
49
 
50
int32_t autosave=180;
51
 
52
int32_t autocorruptcheck;
5320 helixhorne 53
int32_t corruptcheck_noalreadyrefd, corruptcheck_heinum=1;
5420 helixhorne 54
int32_t corruptcheck_game_duke3d=1;  // TODO: at startup, make conditional on which game we are editing for?
4561 hendricks2 55
int32_t corrupt_tryfix_alt;
56
int32_t corruptlevel, numcorruptthings, corruptthings[MAXCORRUPTTHINGS];
57
 
58
////
59
 
60
#ifdef YAX_ENABLE
61
const char *yupdownwall[2] = {"upwall","downwall"};
62
const char *YUPDOWNWALL[2] = {"UPWALL","DOWNWALL"};
63
#endif
64
 
65
////
66
 
67
void drawgradient(void)
68
{
5284 terminx 69
    int32_t i, col = editorcolors[25];
6828 terminx 70
    videoBeginDrawing();
4561 hendricks2 71
    for (i=ydim-STATUS2DSIZ+16; i<ydim && col>0; i++,col--)
72
        CLEARLINES2D(i, 1, (col<<24)|(col<<16)|(col<<8)|col);
73
    CLEARLINES2D(i, ydim-i, 0);
6828 terminx 74
    videoEndDrawing();
4561 hendricks2 75
}
76
 
77
static void message_common1(const char *tmpstr)
78
{
79
    Bstrncpyz(getmessage, tmpstr, sizeof(getmessage));
80
 
81
    getmessageleng = Bstrlen(getmessage);
8050 pogokeen 82
    getmessagetimeoff = (int32_t) totalclock + 120*2 + getmessageleng*(120/30);
4561 hendricks2 83
//    lastmessagetime = totalclock;
84
}
85
 
86
void message(const char *fmt, ...)
87
{
88
    char tmpstr[256];
89
    va_list va;
90
 
91
    va_start(va, fmt);
92
    Bvsnprintf(tmpstr, 256, fmt, va);
93
    va_end(va);
94
 
95
    message_common1(tmpstr);
96
 
97
    if (!mouseaction)
98
        OSD_Printf("%s\n", tmpstr);
99
}
100
 
101
void silentmessage(const char *fmt, ...)
102
{
103
    char tmpstr[256];
104
    va_list va;
105
 
106
    va_start(va, fmt);
107
    Bvsnprintf(tmpstr, 256, fmt, va);
108
    va_end(va);
109
 
110
    message_common1(tmpstr);
111
}
112
 
113
////////// tag labeling system //////////
114
 
115
typedef struct
116
{
117
    hashtable_t hashtab;
118
    char *label[32768];
119
    int32_t numlabels;
120
} taglab_t;
121
 
122
static taglab_t g_taglab;
123
 
124
static void tstrtoupper(char *s)
125
{
126
    int32_t i;
127
    for (i=0; s[i]; i++)
128
        s[i] = Btoupper(s[i]);
129
}
130
 
131
void taglab_init()
132
{
133
    int32_t i;
134
 
135
    g_taglab.numlabels = 0;
136
    g_taglab.hashtab.size = 16384;
137
    hash_init(&g_taglab.hashtab);
138
 
139
    for (i=0; i<32768; i++)
5352 hendricks2 140
        DO_FREE_AND_NULL(g_taglab.label[i]);
4561 hendricks2 141
}
142
 
143
int32_t taglab_load(const char *filename, int32_t flags)
144
{
7359 hendricks2 145
    int32_t len, i;
4561 hendricks2 146
    char buf[BMAX_PATH], *dot, *filebuf;
147
 
148
    taglab_init();
149
 
150
    len = Bstrlen(filename);
151
    if (len >= BMAX_PATH-1)
152
        return -1;
153
    Bmemcpy(buf, filename, len+1);
154
 
155
    //
156
    dot = Bstrrchr(buf, '.');
157
    if (!dot)
158
        dot = &buf[len];
159
 
160
    if (dot-buf+8 >= BMAX_PATH)
161
        return -1;
162
    Bmemcpy(dot, ".maptags", 9);
163
    //
164
 
7359 hendricks2 165
    buildvfs_kfd fil;
166
    if ((fil = kopen4load(buf,flags)) == buildvfs_kfd_invalid)
4561 hendricks2 167
        return -1;
168
 
169
    len = kfilelength(fil);
170
 
171
    filebuf = (char *)Xmalloc(len+1);
172
    if (!filebuf)
173
    {
174
        kclose(fil);
175
        return -1;
176
    }
177
 
178
    kread(fil, filebuf, len);
179
    filebuf[len] = 0;
180
    kclose(fil);
181
 
182
    // ----
183
 
184
    {
185
        int32_t tag;
186
        char *cp=filebuf, *bp, *ep;
187
 
188
        while (1)
189
        {
190
#define XTAGLAB_STRINGIFY(X) TAGLAB_STRINGIFY(X)
191
#define TAGLAB_STRINGIFY(X) #X
192
            i = sscanf(cp, "%d %" XTAGLAB_STRINGIFY(TAGLAB_MAX) "s", &tag, buf);
193
#undef XTAGLAB_STRINGIFY
194
#undef TAGLAB_STRINGIFY
195
            if (i != 2 || !buf[0] || tag<0 || tag>=32768)
196
                goto nextline;
197
 
198
            buf[TAGLAB_MAX-1] = 0;
199
 
200
            i = Bstrlen(buf);
201
            bp = buf; while (*bp && isspace(*bp)) bp++;
202
            ep = &buf[i-1]; while (ep>buf && isspace(*ep)) ep--;
203
            ep++;
204
 
205
            if (!(ep > bp))
206
                goto nextline;
207
            *ep = 0;
208
 
209
            taglab_add(bp, tag);
210
//initprintf("add tag %s:%d\n", bp, tag);
211
nextline:
212
            while (*cp && *cp!='\n')
213
                cp++;
214
            while (*cp=='\r' || *cp=='\n')
215
                cp++;
216
            if (*cp == 0)
217
                break;
218
        }
219
    }
220
 
221
    // ----
7705 terminx 222
    Xfree(filebuf);
4561 hendricks2 223
 
224
    return 0;
225
}
226
 
227
int32_t taglab_save(const char *mapname)
228
{
7359 hendricks2 229
    int32_t len, i;
4561 hendricks2 230
    char buf[BMAX_PATH], *dot;
231
    const char *label;
232
 
233
    if (g_taglab.numlabels==0)
234
        return 1;
235
 
236
    Bstrncpyz(buf, mapname, BMAX_PATH);
237
 
238
    len = Bstrlen(buf);
239
    //
240
    dot = Bstrrchr(buf, '.');
241
    if (!dot)
242
        dot = &buf[len];
243
 
244
    if (dot-buf+8 >= BMAX_PATH)
245
        return -1;
246
    Bmemcpy(dot, ".maptags", 9);
247
    //
248
 
7359 hendricks2 249
    buildvfs_fd fil;
250
    if ((fil = buildvfs_open_write(buf)) == buildvfs_fd_invalid)
4561 hendricks2 251
    {
252
        initprintf("Couldn't open \"%s\" for writing: %s\n", buf, strerror(errno));
253
        return -1;
254
    }
255
 
256
    for (i=0; i<32768; i++)
257
    {
258
        label = taglab_getlabel(i);
259
        if (!label)
260
            continue;
261
 
262
        len = Bsprintf(buf, "%d %s" OURNEWL, i, label);
7359 hendricks2 263
        if (buildvfs_write(fil, buf, len)!=len)
4561 hendricks2 264
            break;
265
    }
266
 
7359 hendricks2 267
    buildvfs_close(fil);
4561 hendricks2 268
 
269
    return (i!=32768);
270
}
271
 
272
int32_t taglab_add(const char *label, int16_t tag)
273
{
274
    const char *otaglabel;
275
    char buf[TAGLAB_MAX];
276
    int32_t olabeltag, diddel=0;
277
 
278
    if (tag < 0)
279
        return -1;
280
 
281
    Bstrncpyz(buf, label, sizeof(buf));
282
    // upcase the tag for storage and comparison
283
    tstrtoupper(buf);
284
 
285
    otaglabel = g_taglab.label[tag];
286
    if (otaglabel)
287
    {
288
        if (!Bstrcasecmp(otaglabel, buf))
289
            return 0;
290
 
291
//        hash_delete(&g_taglab.hashtab, g_taglab.label[tag]);
292
 
293
        // a label having the same tag number as 'tag' is deleted
5352 hendricks2 294
        DO_FREE_AND_NULL(g_taglab.label[tag]);
4561 hendricks2 295
        diddel |= 1;
296
    }
297
    else
298
    {
299
        olabeltag = hash_findcase(&g_taglab.hashtab, buf);
300
        if (olabeltag==tag)
301
            return 0;
302
 
303
        if (olabeltag>=0)
304
        {
305
            // the label gets assigned to a new tag number ('tag deleted')
5352 hendricks2 306
            DO_FREE_AND_NULL(g_taglab.label[olabeltag]);
4561 hendricks2 307
            diddel |= 2;
308
        }
309
    }
310
 
311
    if (!diddel)
312
        g_taglab.numlabels++;
313
    g_taglab.label[tag] = Xstrdup(buf);
314
//initprintf("added %s %d to hash\n", g_taglab.label[tag], tag);
315
    hash_add(&g_taglab.hashtab, g_taglab.label[tag], tag, 1);
316
 
317
    return diddel;
318
}
319
 
320
const char *taglab_getlabel(int16_t tag)
321
{
322
    if (tag < 0)  // || tag>=32768 implicitly
323
        return NULL;
324
 
325
    return g_taglab.label[tag];
326
}
327
 
328
int32_t taglab_gettag(const char *label)
329
{
330
    char buf[TAGLAB_MAX];
331
 
332
    Bstrncpyz(buf, label, TAGLAB_MAX);
333
 
334
    // need to upcase since hash_findcase doesn't work as expected:
335
    // getting the code is still (necessarily) case-sensitive...
336
    tstrtoupper(buf);
337
 
338
    return hash_findcase(&g_taglab.hashtab, buf);
339
}
340
////////// end tag labeling system //////////
341
 
342
////////// UNDO/REDO SYSTEM //////////
343
#if M32_UNDO
344
mapundo_t *mapstate = NULL;
345
 
346
int32_t map_revision = 1;
347
 
5546 terminx 348
static int32_t try_match_with_prev(int32_t idx, int32_t numsthgs, uintptr_t crc)
4561 hendricks2 349
{
350
    if (mapstate->prev && mapstate->prev->num[idx]==numsthgs && mapstate->prev->crc[idx]==crc)
351
    {
352
        // found match!
8425 terminx 353
        mapstate->lz4Blocks[idx] = mapstate->prev->lz4Blocks[idx];
354
        mapstate->lz4Size[idx] = mapstate->prev->lz4Size[idx];
355
        (*(int32_t *)mapstate->lz4Blocks[idx])++;  // increase refcount!
4561 hendricks2 356
 
357
        return 1;
358
    }
359
 
360
    return 0;
361
}
362
 
5546 terminx 363
static void create_compressed_block(int32_t idx, const void *srcdata, uint32_t size, uintptr_t crc)
4561 hendricks2 364
{
365
    // allocate
6181 hendricks2 366
    int const compressed_size = LZ4_compressBound(size);
8425 terminx 367
    Bassert(compressed_size);
368
    mapstate->lz4Blocks[idx] = (char *)Xmalloc(4+compressed_size);
4561 hendricks2 369
 
370
    // compress & realloc
8425 terminx 371
    mapstate->lz4Size[idx] = LZ4_compress_default((const char*)srcdata, mapstate->lz4Blocks[idx]+4, size, compressed_size);
372
    Bassert(mapstate->lz4Size[idx] > 0);
373
    mapstate->lz4Blocks[idx] = (char *)Xrealloc(mapstate->lz4Blocks[idx], 4+mapstate->lz4Size[idx]);
4561 hendricks2 374
 
375
    // write refcount
8425 terminx 376
    *(int32_t *)mapstate->lz4Blocks[idx] = 1;
4561 hendricks2 377
 
378
    mapstate->crc[idx] = crc;
379
}
380
 
381
static void free_self_and_successors(mapundo_t *mapst)
382
{
383
    mapundo_t *cur = mapst;
384
 
385
    mapst->prev = NULL;  // break the back link
386
 
387
    while (cur->next)
388
        cur = cur->next;
389
 
390
    while (1)
391
    {
392
        int32_t i;
393
        mapundo_t *const prev = cur->prev;
394
 
395
        for (i=0; i<3; i++)
396
        {
8425 terminx 397
            int32_t *const refcnt = (int32_t *)cur->lz4Blocks[i];
4561 hendricks2 398
 
399
            if (refcnt)
400
            {
401
                (*refcnt)--;
402
                if (*refcnt == 0)
7705 terminx 403
                    Xfree(refcnt);  // free the block!
4561 hendricks2 404
            }
405
        }
406
 
7705 terminx 407
        Xfree(cur);
4561 hendricks2 408
 
409
        if (!prev)
410
            break;
411
 
412
        cur = prev;
413
    }
414
}
415
 
416
// NOTE: only _consecutive_ matching (size+crc) sector/wall/sprite blocks are
417
// shared!
418
void create_map_snapshot(void)
419
{
420
    if (mapstate == NULL)
421
    {
422
        // create initial mapstate
423
 
424
        map_revision = 1;
425
 
426
        mapstate = (mapundo_t *)Xcalloc(1, sizeof(mapundo_t));
427
        mapstate->revision = map_revision;
428
        mapstate->prev = mapstate->next = NULL;
429
    }
430
    else
431
    {
432
        if (mapstate->next)
433
            free_self_and_successors(mapstate->next);
434
        // now, have no successors
435
 
436
        // calloc because not everything may be set in the following:
437
        mapstate->next = (mapundo_t *)Xcalloc(1, sizeof(mapundo_t));
438
        mapstate->next->prev = mapstate;
439
 
440
        mapstate = mapstate->next;
441
 
442
        mapstate->revision = ++map_revision;
443
    }
444
 
445
 
446
    fixspritesectors();
447
 
8425 terminx 448
    mapstate->num[UNDO_SECTORS] = numsectors;
449
    mapstate->num[UNDO_WALLS]   = numwalls;
450
    mapstate->num[UNDO_SPRITES] = Numsprites;
4561 hendricks2 451
 
452
    if (numsectors)
453
    {
5546 terminx 454
#if !defined UINTPTR_MAX
455
# error Need UINTPTR_MAX define to select between 32- and 64-bit functions
456
#endif
457
#if UINTPTR_MAX == 0xffffffff
458
        /* 32-bit */
459
#define XXH__ XXH32
460
#else
461
        /* 64-bit */
462
#define XXH__ XXH64
463
#endif
464
        uintptr_t temphash = XXH__((uint8_t *)sector, numsectors*sizeof(sectortype), numsectors*sizeof(sectortype));
4561 hendricks2 465
 
466
        if (!try_match_with_prev(0, numsectors, temphash))
467
            create_compressed_block(0, sector, numsectors*sizeof(sectortype), temphash);
468
 
469
        if (numwalls)
470
        {
5546 terminx 471
            temphash = XXH__((uint8_t *)wall, numwalls*sizeof(walltype), numwalls*sizeof(walltype));
4561 hendricks2 472
 
473
            if (!try_match_with_prev(1, numwalls, temphash))
474
                create_compressed_block(1, wall, numwalls*sizeof(walltype), temphash);
475
        }
476
 
477
        if (Numsprites)
478
        {
5546 terminx 479
            temphash = XXH__((uint8_t *)sprite, MAXSPRITES*sizeof(spritetype), MAXSPRITES*sizeof(spritetype));
4561 hendricks2 480
 
481
            if (!try_match_with_prev(2, Numsprites, temphash))
482
            {
483
                int32_t i = 0;
7776 terminx 484
                auto const tspri = (spritetype *)Xmalloc(Numsprites*sizeof(spritetype) + 4);
485
                auto spri = tspri;
4561 hendricks2 486
 
5829 terminx 487
                for (bssize_t j=0; j<MAXSPRITES && i < Numsprites; j++)
4561 hendricks2 488
                    if (sprite[j].statnum != MAXSTATUS)
489
                    {
490
                        Bmemcpy(spri++, &sprite[j], sizeof(spritetype));
491
                        i++;
492
                    }
493
 
494
                create_compressed_block(2, tspri, Numsprites*sizeof(spritetype), temphash);
7705 terminx 495
                Xfree(tspri);
4561 hendricks2 496
            }
497
        }
5546 terminx 498
#undef XXH__
4561 hendricks2 499
    }
500
 
501
    CheckMapCorruption(5, 0);
502
}
503
 
504
void map_undoredo_free(void)
505
{
506
    if (mapstate)
507
    {
508
        free_self_and_successors(mapstate);
509
        mapstate = NULL;
510
    }
511
 
512
    map_revision = 1;
513
}
514
 
8425 terminx 515
int32_t map_undoredo(int dir)
4561 hendricks2 516
{
517
    if (mapstate == NULL) return 1;
518
 
8425 terminx 519
    auto const which = dir ? mapstate->next : mapstate->prev;
520
    if (which == NULL || !which->num[UNDO_SECTORS]) return 1;
4561 hendricks2 521
 
8425 terminx 522
    mapstate = which;
4561 hendricks2 523
 
8425 terminx 524
    numsectors   = mapstate->num[UNDO_SECTORS];
525
    numwalls     = mapstate->num[UNDO_WALLS];
4561 hendricks2 526
    map_revision = mapstate->revision;
527
 
528
    Bmemset(show2dsector, 0, sizeof(show2dsector));
529
 
530
    reset_highlightsector();
531
    reset_highlight();
532
 
533
    initspritelists();
534
 
8425 terminx 535
    if (mapstate->num[UNDO_SECTORS])
4561 hendricks2 536
    {
537
        // restore sector[]
8425 terminx 538
        auto bytes = LZ4_decompress_safe(mapstate->lz4Blocks[UNDO_SECTORS]+4, (char*)sector, mapstate->lz4Size[UNDO_SECTORS], MAXSECTORS*sizeof(sectortype));
539
        Bassert(bytes > 0);
4561 hendricks2 540
 
8425 terminx 541
        if (mapstate->num[UNDO_WALLS])  // restore wall[]
542
        {
543
            bytes = LZ4_decompress_safe(mapstate->lz4Blocks[UNDO_WALLS]+4, (char*)wall, mapstate->lz4Size[UNDO_WALLS], MAXWALLS*sizeof(walltype));
544
            Bassert(bytes > 0);
545
        }
4561 hendricks2 546
 
8425 terminx 547
        if (mapstate->num[UNDO_SPRITES])  // restore sprite[]
548
        {
549
            bytes = LZ4_decompress_safe(mapstate->lz4Blocks[UNDO_SPRITES]+4, (char*)sprite, mapstate->lz4Size[UNDO_SPRITES], MAXSPRITES*sizeof(spritetype));
550
            Bassert(bytes > 0);
551
        }
4561 hendricks2 552
    }
553
 
554
    // insert sprites
8425 terminx 555
    for (int i=0; i<mapstate->num[UNDO_SPRITES]; i++)
4561 hendricks2 556
    {
557
        if ((sprite[i].cstat & 48) == 48) sprite[i].cstat &= ~48;
558
        Bassert((unsigned)sprite[i].sectnum < (unsigned)numsectors
559
                   && (unsigned)sprite[i].statnum < MAXSTATUS);
560
        insertsprite(sprite[i].sectnum, sprite[i].statnum);
561
    }
562
 
8425 terminx 563
    Bassert(Numsprites == mapstate->num[UNDO_SPRITES]);
4561 hendricks2 564
 
565
#ifdef POLYMER
6829 terminx 566
    if (in3dmode() && videoGetRenderMode() == REND_POLYMER)
4561 hendricks2 567
        polymer_loadboard();
568
#endif
569
#ifdef YAX_ENABLE
570
    yax_update(0);
571
    yax_updategrays(pos.z);
572
#endif
573
    CheckMapCorruption(4, 0);
574
 
575
    return 0;
576
}
577
#endif
578
 
579
////
580
 
581
//// port of a.m32's corruptchk ////
4569 helixhorne 582
// Compile wall loop checks? 0: no, 1: partial, 2: full.
4848 helixhorne 583
#define CCHK_LOOP_CHECKS 0
4561 hendricks2 584
// returns value from 0 (all OK) to 5 (panic!)
585
#define CCHK_PANIC OSDTEXT_DARKRED "PANIC!!!^O "
586
//#define CCHKPREF OSDTEXT_RED "^O"
587
#define CCHK_CORRECTED OSDTEXT_GREEN " -> "
588
 
589
#define CORRUPTCHK_PRINT(errlev, what, fmt, ...) do  \
590
{ \
591
    bad = max(bad, errlev); \
592
    if (numcorruptthings>=MAXCORRUPTTHINGS) \
593
        goto too_many_errors; \
594
    corruptthings[numcorruptthings++] = (what); \
595
    if (errlev >= printfromlev) \
4763 hendricks2 596
        OSD_Printf("#%d: " fmt "\n", numcorruptthings, ## __VA_ARGS__); \
4561 hendricks2 597
} while (0)
4568 helixhorne 598
 
4561 hendricks2 599
#ifdef YAX_ENABLE
600
static int32_t walls_have_equal_endpoints(int32_t w1, int32_t w2)
601
{
602
    int32_t n1 = wall[w1].point2, n2 = wall[w2].point2;
603
 
604
    return (wall[w1].x==wall[w2].x && wall[w1].y==wall[w2].y &&
605
            wall[n1].x==wall[n2].x && wall[n1].y==wall[n2].y);
606
}
607
 
608
static void correct_yax_nextwall(int32_t wallnum, int32_t bunchnum, int32_t cf, int32_t tryfixingp)
609
{
610
    int32_t i, j, startwall, endwall;
6061 hendricks2 611
    int32_t nummatching=0, lastwall[2] = { -1, -1 };
4561 hendricks2 612
 
613
    for (SECTORS_OF_BUNCH(bunchnum, !cf, i))
614
        for (WALLS_OF_SECTOR(i, j))
615
        {
616
            //  v v v shouldn't happen, 'stupidity safety'
617
            if (j!=wallnum && walls_have_equal_endpoints(wallnum, j))
618
            {
619
                lastwall[nummatching++] = j;
620
                if (nummatching==2)
621
                    goto outofloop;
622
            }
623
        }
624
outofloop:
625
    if (nummatching==1)
626
    {
627
        if (!tryfixingp)
628
        {
629
            OSD_Printf("    will set wall %d's %s to %d on tryfix\n",
630
                       wallnum, yupdownwall[cf], lastwall[0]);
631
        }
632
        else
633
        {
634
            int32_t setreverse = 0;
635
            yax_setnextwall(wallnum, cf, lastwall[0]);
636
            if (yax_getnextwall(lastwall[0], !cf) < 0)
637
            {
638
                setreverse = 1;
639
                yax_setnextwall(lastwall[0], !cf, wallnum);
640
            }
641
 
642
            OSD_Printf("auto-correction: set wall %d's %s to %d%s\n",
643
                       wallnum, yupdownwall[cf], lastwall[0], setreverse?" and its reverse link":"");
644
        }
645
    }
646
    else if (!tryfixingp)
647
    {
648
        if (nummatching > 1)
649
        {
650
            OSD_Printf("    found more than one matching wall: at least %d and %d\n",
651
                       lastwall[0], lastwall[1]);
652
        }
653
        else if (nummatching == 0)
654
        {
655
            OSD_Printf("    found no matching walls!\n");
656
        }
657
    }
658
}
659
#endif
660
 
661
// in reverse orientation
662
static int32_t walls_are_consistent(int32_t w1, int32_t w2)
663
{
664
    return (wall[w2].x==POINT2(w1).x && wall[w2].y==POINT2(w1).y &&
665
            wall[w1].x==POINT2(w2).x && wall[w1].y==POINT2(w2).y);
666
}
667
 
668
static void suggest_nextsector_correction(int32_t nw, int32_t j)
669
{
670
    // wall j's nextsector is inconsistent with its nextwall... what shall we do?
671
 
672
    if (nw>=0 && nw<numwalls)
673
    {
674
        // maybe wall[j].nextwall's nextwall is right?
675
        if (wall[nw].nextwall==j && walls_are_consistent(nw, j))
676
            OSD_Printf("   suggest setting wall[%d].nextsector to %d\n",
677
                       j, sectorofwall_noquick(nw));
678
    }
679
 
680
    // alternative
681
    if (wall[j].nextsector>=0 && wall[j].nextsector<numsectors)
682
    {
683
        int32_t w, startwall, endwall;
684
        for (WALLS_OF_SECTOR(wall[j].nextsector, w))
685
        {
686
            // XXX: need clearing some others?
687
            if (walls_are_consistent(w, j))
688
            {
689
                OSD_Printf(" ? suggest setting wall[%d].nextwall to %d\n",
690
                           j, w);
691
                break;
692
            }
693
        }
694
    }
695
 
696
    OSD_Printf(" ?? suggest making wall %d white\n", j);
697
}
698
 
699
static void do_nextsector_correction(int32_t nw, int32_t j)
700
{
701
    if (corrupt_tryfix_alt==0)
702
    {
703
        if (nw>=0 && nw<numwalls)
704
            if (wall[nw].nextwall==j && walls_are_consistent(nw, j))
705
            {
706
                int32_t newns = sectorofwall_noquick(nw);
707
                wall[j].nextsector = newns;
708
                OSD_Printf(CCHK_CORRECTED "auto-correction: set wall[%d].nextsector=%d\n",
709
                           j, newns);
710
            }
711
    }
712
    else if (corrupt_tryfix_alt==1)
713
    {
714
        if (wall[j].nextsector>=0 && wall[j].nextsector<numsectors)
715
        {
716
            int32_t w, startwall, endwall;
717
            for (WALLS_OF_SECTOR(wall[j].nextsector, w))
718
                if (walls_are_consistent(w, j))
719
                {
720
                    wall[j].nextwall = w;
721
                    OSD_Printf(CCHK_CORRECTED "auto-correction: set wall[%d].nextwall=%d\n",
722
                               j, w);
723
                    break;
724
                }
725
        }
726
    }
727
    else if (corrupt_tryfix_alt==2)
728
    {
729
        wall[j].nextwall = wall[j].nextsector = -1;
730
        OSD_Printf(CCHK_CORRECTED "auto-correction: made wall %d white\n", j);
731
    }
732
}
733
 
734
 
735
static int32_t csc_s, csc_i;
736
// 1: corrupt, 0: OK
737
static int32_t check_spritelist_consistency()
738
{
5018 helixhorne 739
    int32_t ournumsprites=0;
7594 terminx 740
    static uint8_t havesprite[(MAXSPRITES+7)>>3];
4561 hendricks2 741
 
742
    csc_s = csc_i = -1;
743
 
744
    if (Numsprites < 0 || Numsprites > MAXSPRITES)
745
        return 1;
746
 
5829 terminx 747
    for (bssize_t i=0; i<MAXSPRITES; i++)
4561 hendricks2 748
    {
749
        const int32_t sectnum=sprite[i].sectnum, statnum=sprite[i].statnum;
750
 
751
        csc_i = i;
752
 
753
        if ((statnum==MAXSTATUS) != (sectnum==MAXSECTORS))
754
            return 2;  // violation of .statnum==MAXSTATUS iff .sectnum==MAXSECTORS
755
 
756
        if ((unsigned)statnum > MAXSTATUS || (sectnum!=MAXSECTORS && (unsigned)sectnum > (unsigned)numsectors))
757
            return 3;  // oob sectnum or statnum
758
 
759
        if (statnum != MAXSTATUS)
760
            ournumsprites++;
761
    }
762
 
763
    if (ournumsprites != Numsprites)
764
    {
765
        initprintf("ournumsprites=%d, Numsprites=%d\n", ournumsprites, Numsprites);
766
        return 4;  // counting sprites by statnum!=MAXSTATUS inconsistent with Numsprites
767
    }
768
 
769
    // SECTOR LIST
770
 
771
    Bmemset(havesprite, 0, (Numsprites+7)>>3);
772
 
5829 terminx 773
    for (bssize_t s=0; s<numsectors; s++)
4561 hendricks2 774
    {
5018 helixhorne 775
        int i;
4561 hendricks2 776
        csc_s = s;
777
 
778
        for (i=headspritesect[s]; i>=0; i=nextspritesect[i])
779
        {
780
            csc_i = i;
781
 
782
            if (i >= MAXSPRITES)
783
                return 5;  // oob sprite index in list, or Numsprites inconsistent
784
 
7876 terminx 785
            if (havesprite[i>>3]&pow2char[i&7])
4561 hendricks2 786
                return 6;  // have a cycle in the list
787
 
7876 terminx 788
            havesprite[i>>3] |= pow2char[i&7];
4561 hendricks2 789
 
790
            if (sprite[i].sectnum != s)
791
                return 7;  // .sectnum inconsistent with list
792
        }
793
 
794
        if (i!=-1)
795
            return 8;  // various code checks for -1 to break loop
796
    }
797
 
798
    csc_s = -1;
5829 terminx 799
    for (bssize_t i=0; i<MAXSPRITES; i++)
4561 hendricks2 800
    {
801
        csc_i = i;
802
 
7876 terminx 803
        if (sprite[i].statnum!=MAXSTATUS && !(havesprite[i>>3]&pow2char[i&7]))
4561 hendricks2 804
            return 9;  // have a sprite in the world not in sector list
805
    }
806
 
807
 
808
    // STATUS LIST -- we now clear havesprite[] bits
809
 
5829 terminx 810
    for (bssize_t s=0; s<MAXSTATUS; s++)
4561 hendricks2 811
    {
5018 helixhorne 812
        int i;
4561 hendricks2 813
        csc_s = s;
814
 
815
        for (i=headspritestat[s]; i>=0; i=nextspritestat[i])
816
        {
817
            csc_i = i;
818
 
819
            if (i >= MAXSPRITES)
820
                return 10;  // oob sprite index in list, or Numsprites inconsistent
821
 
822
            // have a cycle in the list, or status list inconsistent with
823
            // sector list (*)
7876 terminx 824
            if (!(havesprite[i>>3]&pow2char[i&7]))
4561 hendricks2 825
                return 11;
826
 
7876 terminx 827
            havesprite[i>>3] &= ~pow2char[i&7];
4561 hendricks2 828
 
829
            if (sprite[i].statnum != s)
830
                return 12;  // .statnum inconsistent with list
831
        }
832
 
833
        if (i!=-1)
834
            return 13;  // various code checks for -1 to break loop
835
    }
836
 
837
    csc_s = -1;
5829 terminx 838
    for (bssize_t i=0; i<Numsprites; i++)
4561 hendricks2 839
    {
840
        csc_i = i;
841
 
842
        // Status list contains only a proper subset of the sprites in the
843
        // sector list.  Reverse case is handled by (*)
7876 terminx 844
        if (havesprite[i>>3]&pow2char[i&7])
4561 hendricks2 845
            return 14;
846
    }
847
 
848
    return 0;
849
}
850
 
4569 helixhorne 851
#if CCHK_LOOP_CHECKS
852
// Return the least wall index of the outer loop of sector <sectnum>, or
853
//  -1 if there is none,
854
//  -2 if there is more than one.
855
static int32_t determine_outer_loop(int32_t sectnum)
856
{
857
    int32_t j, outerloopstart = -1;
858
 
859
    const int32_t startwall = sector[sectnum].wallptr;
860
    const int32_t endwall = startwall + sector[sectnum].wallnum - 1;
861
 
862
    for (j=startwall; j<=endwall; j=get_nextloopstart(j))
863
    {
864
        if (clockdir(j) == CLOCKDIR_CW)
865
        {
866
            if (outerloopstart == -1)
867
                outerloopstart = j;
868
            else if (outerloopstart >= 0)
869
                return -2;
870
        }
871
    }
872
 
873
    return outerloopstart;
874
}
875
#endif
876
 
4561 hendricks2 877
#define TRYFIX_NONE() (tryfixing == 0ull)
878
#define TRYFIX_CNUM(onumct) (onumct < MAXCORRUPTTHINGS && (tryfixing & (1ull<<onumct)))
879
 
880
int32_t CheckMapCorruption(int32_t printfromlev, uint64_t tryfixing)
881
{
4568 helixhorne 882
    int32_t i, j;
4561 hendricks2 883
    int32_t ewall=0;  // expected wall index
884
 
885
    int32_t errlevel=0, bad=0;
886
    int32_t heinumcheckstat = 0;  // 1, 2
887
 
888
    uint8_t *seen_nextwalls = NULL;
889
    int16_t *lastnextwallsource = NULL;
890
 
891
    numcorruptthings = 0;
892
 
893
    if (numsectors>MAXSECTORS)
894
        CORRUPTCHK_PRINT(5, 0, CCHK_PANIC "SECTOR LIMIT EXCEEDED (MAXSECTORS=%d)!!!", MAXSECTORS);
895
 
896
    if (numwalls>MAXWALLS)
897
        CORRUPTCHK_PRINT(5, 0, CCHK_PANIC "WALL LIMIT EXCEEDED (MAXWALLS=%d)!!!", MAXWALLS);
898
 
899
    if (numsectors>MAXSECTORS || numwalls>MAXWALLS)
900
    {
901
        corruptlevel = bad;
902
        return bad;
903
    }
904
 
905
    if (numsectors==0 || numwalls==0)
906
    {
907
        if (numsectors>0)
908
            CORRUPTCHK_PRINT(5, 0, CCHK_PANIC " Have sectors but no walls!");
909
        if (numwalls>0)
910
            CORRUPTCHK_PRINT(5, 0, CCHK_PANIC " Have walls but no sectors!");
911
        return bad;
912
    }
913
 
914
    if (!corruptcheck_noalreadyrefd)
915
    {
916
        seen_nextwalls = (uint8_t *)Xcalloc((numwalls+7)>>3,1);
917
        lastnextwallsource = (int16_t *)Xmalloc(numwalls*sizeof(lastnextwallsource[0]));
918
    }
919
 
920
    for (i=0; i<numsectors; i++)
921
    {
4568 helixhorne 922
        const int32_t w0 = sector[i].wallptr;
923
        const int32_t numw = sector[i].wallnum;
924
        const int32_t endwall = w0 + numw - 1;  // inclusive
925
 
4561 hendricks2 926
        bad = 0;
927
 
928
        if (w0 < 0 || w0 > numwalls)
929
        {
930
            if (w0 < 0 || w0 >= MAXWALLS)
931
                CORRUPTCHK_PRINT(5, CORRUPT_SECTOR|i, "SECTOR[%d].WALLPTR=%d INVALID!!!", i, w0);
932
            else
933
                CORRUPTCHK_PRINT(5, CORRUPT_SECTOR|i, "SECTOR[%d].WALLPTR=%d out of range (numwalls=%d)", i, w0, numw);
934
        }
935
 
936
        if (w0 != ewall)
937
            CORRUPTCHK_PRINT(4, CORRUPT_SECTOR|i, "SECTOR[%d].WALLPTR=%d inconsistent, expected %d", i, w0, ewall);
938
 
939
        if (numw <= 1)
940
            CORRUPTCHK_PRINT(5, CORRUPT_SECTOR|i, CCHK_PANIC "SECTOR[%d].WALLNUM=%d INVALID!!!", i, numw);
941
        else if (numw==2)
942
            CORRUPTCHK_PRINT(3, CORRUPT_SECTOR|i, "SECTOR[%d].WALLNUM=2, expected at least 3", i);
943
 
944
        ewall += numw;
945
 
4568 helixhorne 946
        if (endwall >= numwalls)
4561 hendricks2 947
            CORRUPTCHK_PRINT(5, CORRUPT_SECTOR|i, "SECTOR[%d]: wallptr+wallnum=%d out of range: numwalls=%d", i, endwall, numwalls);
948
 
949
        // inconsistent cstat&2 and heinum checker
5320 helixhorne 950
        if (corruptcheck_heinum)
4561 hendricks2 951
        {
952
            const char *cflabel[2] = {"ceiling", "floor"};
953
 
954
            for (j=0; j<2; j++)
955
            {
956
                const int32_t cs = !!(SECTORFLD(i,stat, j)&2);
957
                const int32_t hn = !!SECTORFLD(i,heinum, j);
958
 
959
                if (cs != hn && heinumcheckstat <= 1)
960
                {
961
                    if (numcorruptthings < MAXCORRUPTTHINGS &&
962
                        (heinumcheckstat==1 || (heinumcheckstat==0 && (tryfixing & (1ull<<numcorruptthings)))))
963
                    {
964
                        setslope(i, j, 0);
965
                        OSD_Printf(CCHK_CORRECTED "auto-correction: reset sector %d's %s slope\n",
966
                                   i, cflabel[j]);
967
                        heinumcheckstat = 1;
968
                    }
5320 helixhorne 969
                    else if (corruptcheck_heinum==2 && heinumcheckstat==0)
4561 hendricks2 970
                    {
971
                        CORRUPTCHK_PRINT(1, CORRUPT_SECTOR|i,
972
                                         "SECTOR[%d]: inconsistent %sstat&2 and heinum", i, cflabel[j]);
973
                    }
5320 helixhorne 974
 
4561 hendricks2 975
                    if (heinumcheckstat != 1)
976
                        heinumcheckstat = 2;
977
                }
978
            }
979
        }
980
 
981
        errlevel = max(errlevel, bad);
982
 
4568 helixhorne 983
        if (bad < 4)
4561 hendricks2 984
        {
985
            for (j=w0; j<=endwall; j++)
986
            {
4568 helixhorne 987
                const int32_t nw = wall[j].nextwall;
988
                const int32_t ns = wall[j].nextsector;
989
 
4561 hendricks2 990
                bad = 0;
991
 
4568 helixhorne 992
                // First, some basic wall sanity checks.
993
 
4561 hendricks2 994
                if (wall[j].point2 < w0 || wall[j].point2 > endwall)
995
                {
996
                    if (wall[j].point2 < 0 || wall[j].point2 >= MAXWALLS)
997
                        CORRUPTCHK_PRINT(5, CORRUPT_WALL|j, CCHK_PANIC "WALL[%d].POINT2=%d INVALID!!!",
998
                                         j, TrackerCast(wall[j].point2));
999
                    else
1000
                        CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL[%d].POINT2=%d out of range [%d, %d]",
1001
                                         j, TrackerCast(wall[j].point2), w0, endwall);
1002
                }
1003
 
1004
                if (nw >= numwalls)
1005
                {
4568 helixhorne 1006
                    const int32_t onumct = numcorruptthings;
4561 hendricks2 1007
 
1008
                    if (TRYFIX_NONE())
1009
                    {
1010
                        if (nw >= MAXWALLS)
1011
                            CORRUPTCHK_PRINT(5, CORRUPT_WALL|j, "WALL[%d].NEXTWALL=%d INVALID!!!",
1012
                                             j, nw);
1013
                        else
1014
                            CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL[%d].NEXTWALL=%d out of range: numwalls=%d",
1015
                                             j, nw, numwalls);
1016
                        OSD_Printf("    will make wall %d white on tryfix\n", j);
1017
                    }
1018
                    else if (TRYFIX_CNUM(onumct))  // CODEDUP MAKE_WALL_WHITE
1019
                    {
1020
                        wall[j].nextwall = wall[j].nextsector = -1;
1021
                        OSD_Printf(CCHK_CORRECTED "auto-correction: made wall %d white\n", j);
1022
                    }
1023
                }
1024
 
1025
                if (ns >= numsectors)
1026
                {
4568 helixhorne 1027
                    const int32_t onumct = numcorruptthings;
4561 hendricks2 1028
 
1029
                    if (TRYFIX_NONE())
1030
                    {
1031
                        if (ns >= MAXSECTORS)
1032
                            CORRUPTCHK_PRINT(5, CORRUPT_WALL|j, "WALL[%d].NEXTSECTOR=%d INVALID!!!",
1033
                                             j, ns);
1034
                        else
1035
                            CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL[%d].NEXTSECTOR=%d out of range: numsectors=%d",
1036
                                             j, ns, numsectors);
1037
                        OSD_Printf("    will make wall %d white on tryfix\n", j);
1038
                    }
1039
                    else if (TRYFIX_CNUM(onumct))  // CODEDUP MAKE_WALL_WHITE
1040
                    {
1041
                        wall[j].nextwall = wall[j].nextsector = -1;
1042
                        OSD_Printf(CCHK_CORRECTED "auto-correction: made wall %d white\n", j);
1043
                    }
1044
                }
1045
 
1046
                if (nw>=w0 && nw<=endwall)
1047
                    CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL[%d].NEXTWALL is its own sector's wall", j);
1048
 
1049
                if (wall[j].x==POINT2(j).x && wall[j].y==POINT2(j).y)
1050
                    CORRUPTCHK_PRINT(3, CORRUPT_WALL|j, "WALL[%d] has length 0", j);
1051
 
1052
#ifdef YAX_ENABLE
4568 helixhorne 1053
                // Various TROR checks.
4561 hendricks2 1054
                {
4568 helixhorne 1055
                    int32_t cf;
4561 hendricks2 1056
 
1057
                    for (cf=0; cf<2; cf++)
1058
                    {
4568 helixhorne 1059
                        const int32_t ynw = yax_getnextwall(j, cf);
1060
 
4561 hendricks2 1061
                        if (ynw >= 0)
1062
                        {
1063
                            if (ynw >= numwalls)
1064
                                CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL %d's %s=%d out of range: numwalls=%d",
1065
                                                 j, YUPDOWNWALL[cf], ynw, numwalls);
1066
                            else
1067
                            {
1068
                                int32_t ynextwallok = 1;
1069
 
1070
                                if (j == ynw)
1071
                                {
1072
                                    CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL %d's %s is itself",
1073
                                                     j, YUPDOWNWALL[cf]);
1074
                                    ynextwallok = 0;
1075
                                }
1076
                                else if (!walls_have_equal_endpoints(j, ynw))
1077
                                {
1078
                                    CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL %d's and its %s=%d's "
1079
                                                     "endpoints are inconsistent", j, YUPDOWNWALL[cf], ynw);
1080
                                    ynextwallok = 0;
1081
                                }
1082
 
1083
                                {
4568 helixhorne 1084
                                    const int16_t bunchnum = yax_getbunch(i, cf);
1085
                                    const int32_t onumct = numcorruptthings;
4561 hendricks2 1086
 
1087
                                    if (bunchnum < 0 || bunchnum >= numyaxbunches)
1088
                                    {
1089
                                        if (tryfixing == 0ull)
1090
                                        {
1091
                                            CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL %d has %s=%d, "
1092
                                                             "but its %s bunchnum=%d is invalid",
1093
                                                             j, YUPDOWNWALL[cf], ynw,
1094
                                                             cf==YAX_CEILING? "ceiling":"floor", bunchnum);
1095
                                            OSD_Printf("    will clear wall %d's %s to -1 on tryfix\n",
1096
                                                       j, yupdownwall[cf]);
1097
 
1098
                                        }
1099
                                        else if (tryfixing & (1ull<<onumct))
1100
                                        {
1101
                                            yax_setnextwall(j, cf, -1);
1102
                                            OSD_Printf(CCHK_CORRECTED "auto-correction: cleared wall %d's %s to -1\n",
1103
                                                       j, yupdownwall[cf]);
1104
                                        }
1105
                                    }
1106
                                    else if (!ynextwallok && onumct < MAXCORRUPTTHINGS)
1107
                                    {
1108
                                        if ((tryfixing & (1ull<<onumct)) || 4>=printfromlev)
1109
                                            correct_yax_nextwall(j, bunchnum, cf, tryfixing!=0ull);
1110
                                    }
1111
                                }
1112
 
1113
                                if (ynextwallok)
1114
                                {
4568 helixhorne 1115
                                    const int32_t onumct = numcorruptthings;
1116
                                    const int32_t ynwp2 = yax_getnextwall(ynw, !cf);
4561 hendricks2 1117
 
1118
                                    if (ynwp2 != j)
1119
                                    {
1120
                                        CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL %d's %s=%d's reverse link wrong"
1121
                                                         " (expected %d, have %d)", j, YUPDOWNWALL[cf], ynw, j, ynwp2);
1122
                                        if (onumct < MAXCORRUPTTHINGS)
1123
                                        {
1124
                                            if (tryfixing & (1ull<<onumct))
1125
                                            {
1126
                                                yax_setnextwall(ynw, !cf, j);
1127
                                                OSD_Printf(CCHK_CORRECTED "auto-correction: set wall %d's %s=%d's %s to %d\n",
1128
                                                           j, yupdownwall[cf], ynw, yupdownwall[!cf], j);
1129
                                            }
1130
                                            else if (4>=printfromlev)
1131
                                            {
1132
                                                OSD_Printf("   will set wall %d's %s=%d's %s to %d on tryfix\n",
1133
                                                           j, yupdownwall[cf], ynw, yupdownwall[!cf], j);
1134
                                            }
1135
                                        }
1136
                                    }
4568 helixhorne 1137
                                }   // brace woot!
4561 hendricks2 1138
                            }
1139
                        }
1140
                    }
1141
                }
1142
#endif
4568 helixhorne 1143
                // Check for ".nextsector is its own sector"
4561 hendricks2 1144
                if (ns == i)
1145
                {
1146
                    if (!bad)
1147
                    {
4568 helixhorne 1148
                        const int32_t onumct = numcorruptthings;
1149
                        const int32_t safetoclear = (nw==j || (wall[nw].nextwall==-1 && wall[nw].nextsector==-1));
4561 hendricks2 1150
 
1151
                        CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL[%d].NEXTSECTOR is its own sector", j);
1152
                        if (onumct < MAXCORRUPTTHINGS)
1153
                        {
1154
                            if (tryfixing & (1ull<<onumct))
1155
                            {
1156
                                if (safetoclear)
1157
                                {
1158
                                    wall[j].nextwall = wall[j].nextsector = -1;
1159
                                    OSD_Printf(CCHK_CORRECTED "auto-correction: cleared wall %d's nextwall"
1160
                                               " and nextsector\n", j);
1161
                                }
1162
                                else
1163
                                    do_nextsector_correction(nw, j);
1164
                            }
1165
                            else if (4>=printfromlev)
1166
                            {
1167
                                if (safetoclear)
1168
                                    OSD_Printf("   will clear wall %d's nextwall and nextsector on tryfix\n", j);
1169
                                else
1170
                                    suggest_nextsector_correction(nw, j);
1171
                            }
1172
                        }
1173
                    }
1174
                }
1175
 
4568 helixhorne 1176
                // Check for ".nextwall already referenced from wall ..."
4561 hendricks2 1177
                if (!corruptcheck_noalreadyrefd && nw>=0 && nw<numwalls)
1178
                {
7876 terminx 1179
                    if (seen_nextwalls[nw>>3]&pow2char[nw&7])
4561 hendricks2 1180
                    {
4568 helixhorne 1181
                        const int32_t onumct = numcorruptthings;
4561 hendricks2 1182
 
4568 helixhorne 1183
                        const int16_t lnws = lastnextwallsource[nw];
1184
                        const int16_t nwnw = wall[nw].nextwall;
1185
 
4561 hendricks2 1186
                        CORRUPTCHK_PRINT(3, CORRUPT_WALL|j, "WALL[%d].NEXTWALL=%d already referenced from wall %d",
1187
                                         j, nw, lnws);
4568 helixhorne 1188
 
4561 hendricks2 1189
                        if (onumct < MAXCORRUPTTHINGS && (nwnw==j || nwnw==lnws))
1190
                        {
4568 helixhorne 1191
                            const int32_t walltoclear = nwnw==j ? lnws : j;
1192
 
4561 hendricks2 1193
                            if (tryfixing & (1ull<<onumct))
1194
                            {
1195
                                wall[walltoclear].nextsector = wall[walltoclear].nextwall = -1;
1196
                                OSD_Printf(CCHK_CORRECTED "auto-correction: cleared wall %d's nextwall and nextsector tags to -1\n",
1197
                                           walltoclear);
1198
                            }
1199
                            else if (3 >= printfromlev)
1200
                                OSD_Printf("    wall[%d].nextwall=%d, suggest clearing wall %d's nextwall and nextsector tags to -1\n",
1201
                                           nw, nwnw, walltoclear);
1202
                        }
1203
                    }
1204
                    else
1205
                    {
1206
                        seen_nextwalls[nw>>3] |= 1<<(nw&7);
1207
                        lastnextwallsource[nw] = j;
1208
                    }
1209
                }
1210
 
4568 helixhorne 1211
                // Various checks of .nextsector and .nextwall
1212
                if (bad < 4)
4561 hendricks2 1213
                {
4568 helixhorne 1214
                    const int32_t onumct = numcorruptthings;
1215
 
4561 hendricks2 1216
                    if ((ns^nw)<0)
1217
                    {
1218
                        CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL[%d].NEXTSECTOR=%d and .NEXTWALL=%d inconsistent:"
1219
                                         " missing one next pointer", j, ns, nw);
1220
                        if (onumct < MAXCORRUPTTHINGS)
1221
                        {
1222
                            if (tryfixing & (1ull<<onumct))
1223
                                do_nextsector_correction(nw, j);
1224
                            else if (4>=printfromlev)
1225
                                suggest_nextsector_correction(nw, j);
1226
                        }
1227
                    }
1228
                    else if (ns>=0)
1229
                    {
1230
                        if (nw<sector[ns].wallptr || nw>=sector[ns].wallptr+sector[ns].wallnum)
1231
                        {
1232
                            CORRUPTCHK_PRINT(4, CORRUPT_WALL|j, "WALL[%d].NEXTWALL=%d out of .NEXTSECTOR=%d's bounds [%d .. %d]",
1233
                                             j, nw, ns, TrackerCast(sector[ns].wallptr), sector[ns].wallptr+sector[ns].wallnum-1);
1234
                            if (onumct < MAXCORRUPTTHINGS)
1235
                            {
1236
                                if (tryfixing & (1ull<<onumct))
1237
                                    do_nextsector_correction(nw, j);
1238
                                else if (4 >= printfromlev)
1239
                                    suggest_nextsector_correction(nw, j);
1240
                            }
1241
                        }
1242
#if 0
1243
                        // this one usually appears together with the "already referenced" corruption
1244
                        else if (wall[nw].nextsector != i || wall[nw].nextwall != j)
1245
                        {
1246
                            CORRUPTCHK_PRINT(4, CORRUPT_WALL|nw, "WALL %d nextwall's backreferences inconsistent. Expected nw=%d, ns=%d; got nw=%d, ns=%d",
1247
                                             nw, i, j, wall[nw].nextsector, wall[nw].nextwall);
1248
                        }
1249
#endif
1250
                    }
1251
                }
1252
 
1253
                errlevel = max(errlevel, bad);
1254
            }
1255
        }
4569 helixhorne 1256
#if CCHK_LOOP_CHECKS
1257
        // Wall loop checks.
1258
        if (bad < 4 && numw >= 4)
1259
        {
1260
            const int32_t outerloopstart = determine_outer_loop(i);
1261
 
1262
            if (outerloopstart == -1)
4580 helixhorne 1263
                CORRUPTCHK_PRINT(2, CORRUPT_SECTOR|i, "SECTOR %d contains no outer (clockwise) loop", i);
4569 helixhorne 1264
            else if (outerloopstart == -2)
4580 helixhorne 1265
                CORRUPTCHK_PRINT(2, CORRUPT_SECTOR|i, "SECTOR %d contains more than one outer (clockwise) loops", i);
4569 helixhorne 1266
# if CCHK_LOOP_CHECKS >= 2
1267
            else
1268
            {
1269
                // Now, check for whether every wall-point of every inner loop of
1270
                // this sector (<i>) is inside the outer one.
1271
 
1272
                for (j=w0; j<=endwall; /* will step by loops */)
1273
                {
1274
                    const int32_t nextloopstart = get_nextloopstart(j);
1275
 
1276
                    if (j != outerloopstart)
1277
                    {
1278
                        int32_t k;
1279
 
1280
                        for (k=j; k<nextloopstart; k++)
1281
                            if (!loopinside(wall[k].x, wall[k].y, outerloopstart))
1282
                            {
1283
                                CORRUPTCHK_PRINT(4, CORRUPT_WALL|k, "WALL %d (of sector %d) is outside the outer loop", k, i);
1284
                                goto end_wall_loop_checks;
1285
                            }
1286
                    }
1287
 
1288
                    j = nextloopstart;
1289
                }
1290
end_wall_loop_checks:
1291
                ;
1292
            }
1293
# endif
1294
            errlevel = max(errlevel, bad);
1295
        }
1296
#endif
4561 hendricks2 1297
    }
1298
 
1299
    bad = 0;
1300
    for (i=0; i<MAXSPRITES; i++)
1301
    {
1302
        if (sprite[i].statnum==MAXSTATUS)
1303
            continue;
1304
 
1305
        if (sprite[i].sectnum<0 || sprite[i].sectnum>=numsectors)
1306
            CORRUPTCHK_PRINT(4, CORRUPT_SPRITE|i, "SPRITE[%d].SECTNUM=%d. Expect problems!", i, TrackerCast(sprite[i].sectnum));
1307
 
1308
        if (sprite[i].statnum<0 || sprite[i].statnum>MAXSTATUS)
1309
            CORRUPTCHK_PRINT(4, CORRUPT_SPRITE|i, "SPRITE[%d].STATNUM=%d. Expect problems!", i, TrackerCast(sprite[i].statnum));
1310
 
1311
        if (sprite[i].picnum<0 || sprite[i].picnum>=MAXTILES)
1312
        {
1313
            sprite[i].picnum = 0;
1314
            CORRUPTCHK_PRINT(0, CORRUPT_SPRITE|i, "SPRITE[%d].PICNUM=%d out of range, resetting to 0", i, TrackerCast(sprite[i].picnum));
1315
        }
1316
 
5420 helixhorne 1317
        if (corruptcheck_game_duke3d)
1318
        {
1319
            const int32_t tilenum = sprite[i].picnum;
1320
 
1321
            if (tilenum >= 1 && tilenum <= 9 && (sprite[i].cstat&48))
1322
            {
1323
                const int32_t onumct = numcorruptthings;
1324
 
1325
                CORRUPTCHK_PRINT(1, CORRUPT_SPRITE|i, "%s sprite %d is not face-aligned",
1326
                                 names[tilenum], i);
1327
 
1328
                if (onumct < MAXCORRUPTTHINGS)
1329
                {
1330
                    if (tryfixing & (1ull<<onumct))
1331
                    {
1332
                        sprite[i].cstat &= ~(32+16);
1333
                        OSD_Printf(CCHK_CORRECTED "auto-correction: cleared sprite[%d].cstat bits 16 and 32\n", i);
1334
                    }
1335
                    else if (1 >= printfromlev)
1336
                        OSD_Printf("   suggest clearing sprite[%d].cstat bits 16 and 32\n", i);
1337
                }
1338
            }
1339
        }
1340
 
4561 hendricks2 1341
        if (klabs(sprite[i].x) > BXY_MAX || klabs(sprite[i].y) > BXY_MAX)
1342
        {
4568 helixhorne 1343
            const int32_t onumct = numcorruptthings;
4561 hendricks2 1344
 
1345
            CORRUPTCHK_PRINT(3, CORRUPT_SPRITE|i, "SPRITE %d at [%d, %d] is outside the maximal grid range [%d, %d]",
1346
                             i, TrackerCast(sprite[i].x), TrackerCast(sprite[i].y), -BXY_MAX, BXY_MAX);
1347
 
1348
            if (onumct < MAXCORRUPTTHINGS)
1349
            {
4568 helixhorne 1350
                int32_t x=0, y=0, ok=0;
1351
                const int32_t sect = sprite[i].sectnum;
4561 hendricks2 1352
 
1353
                if ((unsigned)sect < (unsigned)numsectors)
1354
                {
4568 helixhorne 1355
                    const int32_t firstwall = sector[sect].wallptr;
4561 hendricks2 1356
 
1357
                    if ((unsigned)firstwall < (unsigned)numwalls)
1358
                    {
1359
                        x = wall[firstwall].x;
1360
                        y = wall[firstwall].y;
1361
                        ok = 1;
1362
                    }
1363
                }
1364
 
1365
                if (!(tryfixing & (1ull<<onumct)))
1366
                {
1367
                    if (ok && 3 >= printfromlev)
1368
                        OSD_Printf("   will reposition to its sector's (%d) first"
1369
                                   " point [%d,%d] on tryfix\n", sect, x, y);
1370
                }
1371
                else
1372
                {
1373
                    if (ok)
1374
                    {
1375
                        sprite[i].x = x;
1376
                        sprite[i].y = y;
1377
                        OSD_Printf(CCHK_CORRECTED "auto-correction: repositioned sprite %d to "
1378
                                   "its sector's (%d) first point [%d,%d]\n", i, sect, x, y);
1379
                    }
1380
                }
1381
            }
1382
        }
1383
    }
1384
 
1385
    i = check_spritelist_consistency();
1386
    if (i)
1387
        CORRUPTCHK_PRINT(5, i<0?0:(CORRUPT_SPRITE|i), CCHK_PANIC "SPRITE LISTS CORRUPTED: error code %d, s=%d, i=%d!",
1388
                         i, csc_s, csc_i);
1389
 
1390
    if (0)
1391
    {
1392
too_many_errors:
1393
        if (printfromlev<=errlevel)
1394
            OSD_Printf("!! too many errors, stopping. !!\n");
1395
    }
1396
 
1397
    errlevel = max(errlevel, bad);
1398
 
1399
    if (errlevel)
1400
    {
1401
        if (printfromlev<=errlevel)
1402
            OSD_Printf("-- corruption level: %d\n", errlevel);
1403
        if (tryfixing)
1404
            OSD_Printf("--\n");
1405
    }
1406
 
1407
    if (seen_nextwalls)
1408
    {
7705 terminx 1409
        Xfree(seen_nextwalls);
1410
        Xfree(lastnextwallsource);
4561 hendricks2 1411
    }
1412
 
1413
    corruptlevel = errlevel;
1414
 
1415
    return errlevel;
1416
}
1417
////
1418
 
1419
 
1420
////////// STATUS BAR MENU "class" //////////
1421
 
1422
#define MENU_MAX_ENTRIES (8*3)
1423
#define MENU_ENTRY_SIZE 25  // max. length of label (including terminating NUL)
1424
 
1425
#define MENU_Y_SPACING 8
1426
#define MENU_BASE_Y (ydim-STATUS2DSIZ+32)
1427
 
1428
#define MENU_FG_COLOR editorcolors[11]
1429
#define MENU_BG_COLOR editorcolors[0]
1430
#define MENU_BG_COLOR_SEL editorcolors[1]
1431
 
1432
#ifdef LUNATIC
1433
# define MENU_HAVE_DESCRIPTION 1
1434
#else
1435
# define MENU_HAVE_DESCRIPTION 0
1436
#endif
1437
 
1438
typedef struct StatusBarMenu_ {
1439
    const char *const menuname;
1440
    const int32_t custom_start_index;
1441
    int32_t numentries;
1442
 
1443
    void (*process_func)(const struct StatusBarMenu_ *m, int32_t col, int32_t row);
1444
 
1445
    intptr_t auxdata[MENU_MAX_ENTRIES];
1446
    char *description[MENU_MAX_ENTRIES];  // strdup'd description string, NULL if non
1447
    char name[MENU_MAX_ENTRIES][MENU_ENTRY_SIZE];
1448
} StatusBarMenu;
1449
 
1450
#define MENU_INITIALIZER_EMPTY(MenuName, ProcessFunc) \
1451
    { MenuName, 0, 0, ProcessFunc, {}, {}, {} }
1452
#define MENU_INITIALIZER(MenuName, CustomStartIndex, ProcessFunc, ...) \
1453
    { MenuName, CustomStartIndex, CustomStartIndex, ProcessFunc, {}, {}, ## __VA_ARGS__ }
1454
 
1455
#ifdef LUNATIC
1456
static void M_Clear(StatusBarMenu *m)
1457
{
1458
    int32_t i;
1459
 
1460
    m->numentries = 0;
1461
    Bmemset(m->auxdata, 0, sizeof(m->auxdata));
1462
    Bmemset(m->name, 0, sizeof(m->name));
1463
 
1464
    for (i=0; i<MENU_MAX_ENTRIES; i++)
5352 hendricks2 1465
        DO_FREE_AND_NULL(m->description[i]);
4561 hendricks2 1466
}
1467
 
1468
static int32_t M_HaveDescription(StatusBarMenu *m)
1469
{
1470
    int32_t i;
1471
 
1472
    for (i=0; i<MENU_MAX_ENTRIES; i++)
1473
        if (m->description[i] != NULL)
1474
            return 1;
1475
 
1476
    return 0;
1477
}
1478
#endif
1479
 
1480
// NOTE: Does not handle description strings! (Only the Lua menu uses them.)
1481
static void M_UnregisterFunction(StatusBarMenu *m, intptr_t auxdata)
1482
{
1483
    int32_t i, j;
1484
 
1485
    for (i=m->custom_start_index; i<m->numentries; i++)
1486
        if (m->auxdata[i]==auxdata)
1487
        {
1488
            for (j=i; j<m->numentries-1; j++)
1489
            {
1490
                m->auxdata[j] = m->auxdata[j+1];
1491
                Bmemcpy(m->name[j], m->name[j+1], MENU_ENTRY_SIZE);
1492
            }
1493
 
1494
            m->auxdata[j] = 0;
1495
            Bmemset(m->name[j], 0, MENU_ENTRY_SIZE);
1496
 
1497
            m->numentries--;
1498
 
1499
            break;
1500
        }
1501
}
1502
 
1503
static void M_RegisterFunction(StatusBarMenu *m, const char *name, intptr_t auxdata, const char *description)
1504
{
1505
    int32_t i;
1506
 
1507
    for (i=8; i<m->numentries; i++)
1508
    {
1509
        if (m->auxdata[i]==auxdata)
1510
        {
1511
            // same auxdata, different name
1512
            Bstrncpyz(m->name[i], name, MENU_ENTRY_SIZE);
1513
            return;
1514
        }
1515
        else if (!Bstrncmp(m->name[i], name, MENU_ENTRY_SIZE))
1516
        {
1517
            // same name, different auxdata
1518
            m->auxdata[i] = auxdata;
1519
            return;
1520
        }
1521
    }
1522
 
1523
    if (m->numentries == MENU_MAX_ENTRIES)
1524
        return;  // max reached
1525
 
1526
    Bstrncpyz(m->name[m->numentries], name, MENU_ENTRY_SIZE);
1527
    m->auxdata[m->numentries] = auxdata;
1528
 
1529
#if MENU_HAVE_DESCRIPTION
1530
    // NOTE: description only handled here (not above).
1531
    if (description)
1532
        m->description[m->numentries] = Xstrdup(description);
1533
#else
1534
    UNREFERENCED_PARAMETER(description);
1535
#endif
1536
 
1537
    m->numentries++;
1538
}
1539
 
1540
static void M_DisplayInitial(const StatusBarMenu *m)
1541
{
1542
    int32_t x = 8, y = MENU_BASE_Y+16;
1543
    int32_t i;
1544
 
1545
    for (i=0; i<m->numentries; i++)
1546
    {
1547
        if (i==8 || i==16)
1548
        {
1549
            x += 208;
1550
            y = MENU_BASE_Y+16;
1551
        }
1552
 
1553
        printext16(x,y, MENU_FG_COLOR, MENU_BG_COLOR, m->name[i], 0);
1554
        y += MENU_Y_SPACING;
1555
    }
1556
 
1557
    printext16(m->numentries>8 ? 216 : 8, MENU_BASE_Y, MENU_FG_COLOR, -1, m->menuname, 0);
1558
 
1559
    clearkeys();
1560
}
1561
 
1562
static void M_EnterMainLoop(StatusBarMenu *m)
1563
{
1564
    char disptext[80];
1565
    const int32_t dispwidth = MENU_ENTRY_SIZE-1;
1566
 
1567
    int32_t i, col=0, row=0;
1568
    int32_t crowmax[3] = {-1, -1, -1};
1569
    int32_t xpos = 8, ypos = MENU_BASE_Y+16;
1570
 
1571
    if (m->numentries == 0)
1572
    {
1573
        printmessage16("%s menu has no entries", m->menuname);
1574
        return;
1575
    }
1576
 
1577
    Bmemset(disptext, 0, sizeof(disptext));
1578
 
1579
    Bassert((unsigned)m->numentries <= MENU_MAX_ENTRIES);
1580
    for (i=0; i<=(m->numentries-1)/8; i++)
1581
        crowmax[i] = (m->numentries >= (i+1)*8) ? 7 : (m->numentries-1)&7;
1582
 
1583
    drawgradient();
1584
    M_DisplayInitial(m);
1585
 
1586
    while (keystatus[KEYSC_ESC] == 0)
1587
    {
1588
        idle_waitevent();
1589
        if (handleevents())
1590
            quitevent = 0;
1591
 
1592
        _printmessage16("Select an option, press <Esc> to exit");
1593
 
1594
        if (PRESSED_KEYSC(DOWN))
1595
        {
1596
            if (row < crowmax[col])
1597
            {
1598
                printext16(xpos, ypos+row*MENU_Y_SPACING, MENU_FG_COLOR, MENU_BG_COLOR, disptext, 0);
1599
                row++;
1600
            }
1601
        }
1602
        else if (PRESSED_KEYSC(UP))
1603
        {
1604
            if (row > 0)
1605
            {
1606
                printext16(xpos, ypos+row*MENU_Y_SPACING, MENU_FG_COLOR, MENU_BG_COLOR, disptext, 0);
1607
                row--;
1608
            }
1609
        }
1610
        else if (PRESSED_KEYSC(LEFT))
1611
        {
1612
            if (col > 0)
1613
            {
1614
                printext16(xpos, ypos+row*8, MENU_FG_COLOR, 0, disptext, 0);
1615
                col--;
1616
                xpos -= 208;
1617
                disptext[dispwidth] = 0;
1618
                row = min(crowmax[col], row);
1619
            }
1620
        }
1621
        else if (PRESSED_KEYSC(RIGHT))
1622
        {
1623
            if (col < 2 && crowmax[col+1]>=0)
1624
            {
1625
                printext16(xpos, ypos+row*8, MENU_FG_COLOR, 0, disptext, 0);
1626
                col++;
1627
                xpos += 208;
1628
                disptext[dispwidth] = 0;
1629
                row = min(crowmax[col], row);
1630
            }
1631
        }
1632
 
1633
        for (i=Bsnprintf(disptext, dispwidth, "%s", m->name[col*8 + row]); i < dispwidth; i++)
1634
            disptext[i] = ' ';
1635
 
1636
        if (PRESSED_KEYSC(ENTER))
1637
        {
1638
            Bassert(m->process_func != NULL);
1639
            m->process_func(m, col, row);
1640
            break;
1641
        }
1642
 
1643
#if MENU_HAVE_DESCRIPTION
1644
        if (M_HaveDescription(m))
1645
        {
1646
            const int32_t maxrows = 20;
1647
            int32_t r;
1648
 
1649
            for (r=0; r<maxrows+1; r++)
1650
                printext16(16-4, 16-4 + r*8, 0, MENU_BG_COLOR,  // 71 blanks:
1651
                           "                                                                       ", 0);
1652
            if (m->description[col*8 + row] != NULL)
1653
                printext16(16, 16, MENU_FG_COLOR, MENU_BG_COLOR, m->description[col*8 + row], 0);
1654
        }
1655
#endif
1656
        printext16(xpos, ypos+row*MENU_Y_SPACING, MENU_FG_COLOR, MENU_BG_COLOR_SEL, disptext, 0);
6828 terminx 1657
        videoShowFrame(1);
4561 hendricks2 1658
    }
1659
 
1660
    printext16(xpos, ypos+row*MENU_Y_SPACING, MENU_FG_COLOR, MENU_BG_COLOR, disptext, 0);
6828 terminx 1661
    videoShowFrame(1);
4561 hendricks2 1662
 
1663
    keystatus[KEYSC_ESC] = 0;
1664
}
1665
 
1666
////////// SPECIAL FUNCTIONS MENU //////////
1667
 
1668
static void FuncMenu_Process(const StatusBarMenu *m, int32_t col, int32_t row);
1669
 
1670
static StatusBarMenu g_specialFuncMenu = MENU_INITIALIZER(
1671
    "Special functions", 8, FuncMenu_Process,
1672
 
1673
    {
1674
        "Replace invalid tiles",
1675
        "Delete all spr of tile #",
1676
        "Set map sky shade",
1677
        "Set map sky height",
1678
        "Global Z coord shift",
1679
        "Resize selection",
1680
        "Global shade divide",
1681
        "Global visibility divide"
1682
    }
1683
);
1684
 
1685
// "External functions":
1686
 
1687
void FuncMenu(void)
1688
{
1689
    M_EnterMainLoop(&g_specialFuncMenu);
1690
}
1691
 
1692
void registerMenuFunction(const char *funcname, int32_t stateidx)
1693
{
1694
    if (funcname)
1695
        M_RegisterFunction(&g_specialFuncMenu, funcname, stateidx, NULL);
1696
    else
1697
        M_UnregisterFunction(&g_specialFuncMenu, stateidx);
1698
}
1699
 
1700
// The processing function...
1701
 
1702
static int32_t correct_picnum(int16_t *picnumptr)
1703
{
1704
    int32_t picnum = *picnumptr;
1705
 
4623 terminx 1706
    if ((unsigned)picnum >= MAXTILES || tilesiz[picnum].x <= 0)
4561 hendricks2 1707
    {
1708
        *picnumptr = 0;
1709
        return 1;
1710
    }
1711
 
1712
    return 0;
1713
}
1714
 
1715
static void FuncMenu_Process(const StatusBarMenu *m, int32_t col, int32_t row)
1716
{
1717
    int32_t i, j;
1718
 
1719
    switch (col)
1720
    {
1721
 
1722
    case 1:
1723
    case 2:
1724
    {
1725
        const int32_t stateidx = (Bassert(m->auxdata[col*8 + row] < g_stateCount),
1726
                                  m->auxdata[col*8 + row]);
1727
 
1728
        const char *statename = statesinfo[stateidx].name;
1729
        int32_t snlen = Bstrlen(statename);
1730
        char *tmpscript = (char *)Xmalloc(1+5+1+snlen+1);
1731
 
1732
        tmpscript[0] = ' ';  // don't save in history
1733
        Bmemcpy(&tmpscript[1], "state", 5);
1734
        tmpscript[1+5] = ' ';
1735
        Bmemcpy(&tmpscript[1+5+1], statename, snlen);
1736
        tmpscript[1+5+1+snlen] = 0;
1737
 
1738
        M32RunScript(tmpscript);
7705 terminx 1739
        Xfree(tmpscript);
4561 hendricks2 1740
 
1741
        if (vm.flags&VMFLAG_ERROR)
1742
            printmessage16("There were errors while executing the menu function");
1743
        else if (lastpm16time != totalclock)
1744
            printmessage16("Menu function executed successfully");
1745
    }
1746
    break;
1747
 
1748
    case 0:
1749
    {
1750
        switch (row)
1751
        {
1752
 
1753
        case 0:
1754
        {
1755
            j = 0;
1756
 
1757
            for (i=0; i<MAXSECTORS; i++)
1758
            {
1759
                j += correct_picnum(&sector[i].ceilingpicnum);
1760
                j += correct_picnum(&sector[i].floorpicnum);
1761
            }
1762
 
1763
            for (i=0; i<MAXWALLS; i++)
1764
            {
1765
                j += correct_picnum(&wall[i].picnum);
1766
                j += correct_picnum(&wall[i].overpicnum);
1767
            }
1768
            for (i=0; i<MAXSPRITES; i++)
1769
                j += correct_picnum(&sprite[i].picnum);
1770
 
5768 hendricks2 1771
            printmessage16("Replaced %d invalid tiles",j);
4561 hendricks2 1772
        }
1773
        break;
1774
 
1775
        case 1:
1776
        {
1777
            char tempbuf[64];
1778
            Bsprintf(tempbuf,"Delete all sprites of tile #: ");
1779
            i = getnumber16(tempbuf,-1,MAXSPRITES-1,1);
1780
            if (i >= 0)
1781
            {
1782
                int32_t k = 0;
1783
                for (j=0; j<MAXSPRITES; j++)
1784
                    if (sprite[j].picnum == i)
1785
                        deletesprite(j), k++;
1786
                printmessage16("%d sprite(s) deleted",k);
1787
            }
1788
            else printmessage16("Aborted");
1789
        }
1790
        break;
1791
 
1792
        case 2:
1793
        {
1794
            j=getnumber16("Set map sky shade:    ",0,128,1);
1795
 
1796
            for (i=0; i<numsectors; i++)
1797
            {
1798
                if (sector[i].ceilingstat&1)
1799
                    sector[i].ceilingshade = j;
1800
            }
1801
            printmessage16("All parallax skies adjusted");
1802
        }
1803
        break;
1804
 
1805
        case 3:
1806
        {
1807
            j=getnumber16("Set map sky height:    ",0,16777216,1);
1808
            if (j != 0)
1809
            {
1810
                for (i=0; i<numsectors; i++)
1811
                {
1812
                    if (sector[i].ceilingstat&1)
1813
                        sector[i].ceilingz = j;
1814
                }
1815
                printmessage16("All parallax skies adjusted");
1816
            }
1817
            else printmessage16("Aborted");
1818
        }
1819
        break;
1820
 
1821
        case 4:
1822
        {
1823
            j=getnumber16("Z offset:    ",0,16777216,1);
1824
            if (j!=0)
1825
            {
1826
                for (i=0; i<numsectors; i++)
1827
                {
1828
                    sector[i].ceilingz += j;
1829
                    sector[i].floorz += j;
1830
                }
1831
                for (i=0; i<MAXSPRITES; i++)
1832
                    sprite[i].z += j;
1833
                printmessage16("Map adjusted");
1834
            }
1835
            else printmessage16("Aborted");
1836
        }
1837
        break;
1838
 
1839
        case 5:
1840
        {
1841
            j=getnumber16("Percentage of original:    ",100,1000,0);
1842
 
1843
            if (j!=100 && j!=0)
1844
            {
1845
                int32_t w, currsector, start_wall, end_wall;
1846
                double size = (j/100.f);
1847
 
1848
                for (i = 0; i < highlightsectorcnt; i++)
1849
                {
1850
                    currsector = highlightsector[i];
1851
                    sector[currsector].ceilingz = (int32_t)(sector[currsector].ceilingz*size);
1852
                    sector[currsector].floorz = (int32_t)(sector[currsector].floorz*size);
1853
 
1854
                    // Do all the walls in the sector
1855
                    start_wall = sector[currsector].wallptr;
1856
                    end_wall = start_wall + sector[currsector].wallnum;
1857
 
1858
                    for (w = start_wall; w < end_wall; w++)
1859
                    {
1860
                        wall[w].x = (int32_t)(wall[w].x*size);
1861
                        wall[w].y = (int32_t)(wall[w].y*size);
1862
                        wall[w].yrepeat = min((int32_t)(wall[w].yrepeat/size),255);
1863
                    }
1864
 
1865
                    w = headspritesect[highlightsector[i]];
1866
                    while (w >= 0)
1867
                    {
1868
                        sprite[w].x = (int32_t)(sprite[w].x*size);
1869
                        sprite[w].y = (int32_t)(sprite[w].y*size);
1870
                        sprite[w].z = (int32_t)(sprite[w].z*size);
1871
                        sprite[w].xrepeat = min(max((int32_t)(sprite[w].xrepeat*size),1),255);
1872
                        sprite[w].yrepeat = min(max((int32_t)(sprite[w].yrepeat*size),1),255);
1873
                        w = nextspritesect[w];
1874
                    }
1875
                }
1876
                printmessage16("Map scaled");
1877
            }
1878
            else printmessage16("Aborted");
1879
        }
1880
        break;
1881
 
1882
        case 6:
1883
        {
1884
            j=getnumber16("Shade divisor:    ",1,128,1);
1885
            if (j > 1)
1886
            {
1887
                for (i=0; i<numsectors; i++)
1888
                {
1889
                    sector[i].ceilingshade /= j;
1890
                    sector[i].floorshade /= j;
1891
                }
1892
                for (i=0; i<numwalls; i++)
1893
                    wall[i].shade /= j;
1894
                for (i=0; i<MAXSPRITES; i++)
1895
                    sprite[i].shade /= j;
1896
                printmessage16("Shades adjusted");
1897
            }
1898
            else printmessage16("Aborted");
1899
        }
1900
        break;
1901
 
1902
        case 7:
1903
        {
1904
            j=getnumber16("Visibility divisor:    ",1,128,0);
1905
            if (j > 1)
1906
            {
1907
                for (i=0; i<numsectors; i++)
1908
                {
1909
                    if (sector[i].visibility < 240)
1910
                        sector[i].visibility /= j;
1911
                    else sector[i].visibility = 240 + (sector[i].visibility>>4)/j;
1912
                }
1913
                printmessage16("Visibility adjusted");
1914
            }
1915
            else printmessage16("Aborted");
1916
        }
1917
        break;
1918
 
1919
        }  // switch (row)
1920
    }
1921
    break;  // switch (col) / case 0
1922
 
1923
    }  // switch (col)
1924
}
1925
 
8484 hendricks2 1926
void m32_showmouse()
1927
{
1928
    int32_t i, col;
1929
 
1930
    int32_t mousecol = M32_THROB;
1931
 
1932
    if (whitecol > editorcolors[0])
1933
        col = whitecol - mousecol;
1934
    else col = whitecol + mousecol;
1935
 
1936
#ifdef USE_OPENGL
1937
    if (videoGetRenderMode() >= REND_POLYMOST)
1938
    {
1939
        renderDisableFog();
1940
        polymost_useColorOnly(true);
1941
    }
1942
#endif
1943
 
1944
    int const lores = !!(xdim <= 640);
1945
 
1946
    for (i = (3 - lores); i <= (7 >> lores); i++)
1947
    {
1948
        plotpixel(searchx+i,searchy,col);
1949
        plotpixel(searchx-i,searchy,col);
1950
        plotpixel(searchx,searchy-i,col);
1951
        plotpixel(searchx,searchy+i,col);
1952
    }
1953
 
1954
    for (i=1; i<=(2 >> lores); i++)
1955
    {
1956
        plotpixel(searchx+i,searchy,whitecol);
1957
        plotpixel(searchx-i,searchy,whitecol);
1958
        plotpixel(searchx,searchy-i,whitecol);
1959
        plotpixel(searchx,searchy+i,whitecol);
1960
    }
1961
 
1962
    i = (8 >> lores);
1963
 
1964
    plotpixel(searchx+i,searchy,editorcolors[0]);
1965
    plotpixel(searchx-i,searchy,editorcolors[0]);
1966
    plotpixel(searchx,searchy-i,editorcolors[0]);
1967
    plotpixel(searchx,searchy+i,editorcolors[0]);
1968
 
1969
    if (!lores)
1970
    {
1971
        for (i=1; i<=4; i++)
1972
        {
1973
            plotpixel(searchx+i,searchy,whitecol);
1974
            plotpixel(searchx-i,searchy,whitecol);
1975
            plotpixel(searchx,searchy-i,whitecol);
1976
            plotpixel(searchx,searchy+i,whitecol);
1977
        }
1978
    }
1979
 
1980
#ifdef USE_OPENGL
1981
    if (videoGetRenderMode() >= REND_POLYMOST)
1982
    {
1983
        renderEnableFog();
1984
        polymost_useColorOnly(false);
1985
    }
1986
#endif
1987
}
1988
 
4561 hendricks2 1989
#ifdef LUNATIC
1990
typedef const char *(*luamenufunc_t)(void);
1991
 
4766 hendricks2 1992
#ifdef __cplusplus
1993
extern "C" {
1994
#endif
1995
extern void LM_Register(const char *name, luamenufunc_t funcptr, const char *description);
1996
extern void LM_Clear(void);
1997
#ifdef __cplusplus
1998
}
1999
#endif
2000
 
4561 hendricks2 2001
static int32_t g_numLuaFuncs = 0;
2002
static luamenufunc_t g_LuaFuncPtrs[MENU_MAX_ENTRIES];
2003
 
2004
static void LuaFuncMenu_Process(const StatusBarMenu *m, int32_t col, int32_t row)
2005
{
2006
    luamenufunc_t func = g_LuaFuncPtrs[col*8 + row];
2007
    const char *errmsg;
2008
 
2009
    Bassert(func != NULL);
2010
    errmsg = func();
2011
 
2012
    if (errmsg == NULL)
2013
    {
2014
        printmessage16("Lua function executed successfully");
2015
    }
2016
    else
2017
    {
2018
        printmessage16("There were errors executing the Lua function, see OSD");
2019
        OSD_Printf("Errors executing Lua function \"%s\": %s\n", m->name[col*8 + row], errmsg);
2020
    }
2021
}
2022
 
2023
static StatusBarMenu g_LuaFuncMenu = MENU_INITIALIZER_EMPTY("Lua functions", LuaFuncMenu_Process);
2024
 
2025
void LuaFuncMenu(void)
2026
{
2027
    M_EnterMainLoop(&g_LuaFuncMenu);
2028
}
2029
 
2030
LUNATIC_EXTERN void LM_Register(const char *name, luamenufunc_t funcptr, const char *description)
2031
{
2032
    if (name == NULL || g_numLuaFuncs == MENU_MAX_ENTRIES)
2033
        return;
2034
 
2035
    g_LuaFuncPtrs[g_numLuaFuncs] = funcptr;
2036
    M_RegisterFunction(&g_LuaFuncMenu, name, g_numLuaFuncs, description);
2037
    g_numLuaFuncs++;
2038
}
2039
 
2040
LUNATIC_EXTERN void LM_Clear(void)
2041
{
2042
    M_Clear(&g_LuaFuncMenu);
2043
 
2044
    g_numLuaFuncs = 0;
2045
    Bmemset(g_LuaFuncPtrs, 0, sizeof(g_LuaFuncPtrs));
2046
}
2047
#endif