Subversion Repositories eduke32

Rev

Rev 4848 | Details | Compare with Previous | Last modification | View Log | RSS feed

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