Subversion Repositories eduke32

Rev

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

Rev Author Line No. Line
5 Plagman 1
//-------------------------------------------------------------------------
2
/*
1652 terminx 3
Copyright (C) 2010 EDuke32 developers and contributors
5 Plagman 4
 
1652 terminx 5
This file is part of EDuke32.
5 Plagman 6
 
7
EDuke32 is free software; you can redistribute it and/or
8
modify it under the terms of the GNU General Public License version 2
9
as published by the Free Software Foundation.
10
 
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
 
15
See the GNU General Public License for more details.
16
 
17
You should have received a copy of the GNU General Public License
18
along with this program; if not, write to the Free Software
4541 hendricks2 19
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
5 Plagman 20
*/
21
//-------------------------------------------------------------------------
22
 
23
#include "duke3d.h"
1677 terminx 24
#include "premap.h"
2207 helixhorne 25
#include "menus.h"  // menutext
1660 terminx 26
#include "prlights.h"
1950 helixhorne 27
#include "savegame.h"
3796 helixhorne 28
#ifdef LUNATIC
29
# include "lunatic_game.h"
30
static int32_t g_savedOK;
31
const char *g_failedVarname;
32
#endif
5 Plagman 33
 
983 hnt_ts 34
extern char *bitptr;
35
 
4472 hendricks2 36
uint8_t g_oldverSavegame[MAXSAVEGAMES];
3156 helixhorne 37
 
984 terminx 38
#define BITPTR_POINTER 1
39
 
1950 helixhorne 40
// For storing pointers in files.
41
//  back_p==0: ptr -> "small int"
42
//  back_p==1: "small int" -> ptr
43
//
44
//  mode: see enum in savegame.h
3150 helixhorne 45
void G_Util_PtrToIdx(void *ptr, int32_t count, const void *base, int32_t mode)
1950 helixhorne 46
{
3150 helixhorne 47
    int32_t i;
3177 helixhorne 48
    intptr_t *iptr = (intptr_t *)ptr;
3150 helixhorne 49
    intptr_t ibase = (intptr_t)base;
1950 helixhorne 50
    int32_t back_p = mode&P2I_BACK_BIT;
51
    int32_t onlynon0_p = mode&P2I_ONLYNON0_BIT;
52
 
53
    // TODO: convert to proper offsets/indices for (a step towards) cross-
54
    //       compatibility between 32- and 64-bit systems in the netplay.
55
    //       REMEMBER to bump BYTEVERSION then.
56
 
3150 helixhorne 57
    for (i=0; i<count; i++)
1950 helixhorne 58
        // WARNING: C std doesn't say that bit pattern of NULL is necessarily 0!
59
        if (!onlynon0_p || iptr[i])
60
        {
61
            if (!back_p)
62
                iptr[i] -= ibase;
63
            else
64
                iptr[i] += ibase;
65
        }
66
}
67
 
3150 helixhorne 68
void G_Util_PtrToIdx2(void *ptr, int32_t count, size_t stride, const void *base, int32_t mode)
3102 terminx 69
{
3150 helixhorne 70
    int32_t i;
71
    uint8_t *iptr = (uint8_t *)ptr;
3102 terminx 72
    intptr_t ibase = (intptr_t)base;
73
    int32_t back_p = mode&P2I_BACK_BIT;
74
    int32_t onlynon0_p = mode&P2I_ONLYNON0_BIT;
75
 
3150 helixhorne 76
    for (i=0; i<count; i++)
3102 terminx 77
    {
78
        if (!onlynon0_p || *(intptr_t *)iptr)
79
        {
80
            if (!back_p)
81
                *(intptr_t *)iptr -= ibase;
82
            else
83
                *(intptr_t *)iptr += ibase;
84
        }
85
 
3150 helixhorne 86
        iptr += stride;
3102 terminx 87
    }
88
}
89
 
1949 helixhorne 90
// TODO: sync with TROR special interpolations? (e.g. upper floor of subway)
91
void G_ResetInterpolations(void)
92
{
93
    int32_t k, i;
94
 
95
    g_numInterpolations = 0;
96
    startofdynamicinterpolations = 0;
97
 
98
    k = headspritestat[STAT_EFFECTOR];
99
    while (k >= 0)
100
    {
101
        switch (sprite[k].lotag)
102
        {
2970 helixhorne 103
        case SE_31_FLOOR_RISE_FALL:
1949 helixhorne 104
            G_SetInterpolation(&sector[sprite[k].sectnum].floorz);
105
            break;
2970 helixhorne 106
        case SE_32_CEILING_RISE_FALL:
1949 helixhorne 107
            G_SetInterpolation(&sector[sprite[k].sectnum].ceilingz);
108
            break;
2970 helixhorne 109
        case SE_17_WARP_ELEVATOR:
110
        case SE_25_PISTON:
1949 helixhorne 111
            G_SetInterpolation(&sector[sprite[k].sectnum].floorz);
112
            G_SetInterpolation(&sector[sprite[k].sectnum].ceilingz);
113
            break;
3008 helixhorne 114
        case SE_0_ROTATING_SECTOR:
115
        case SE_5:
116
        case SE_6_SUBWAY:
117
        case SE_11_SWINGING_DOOR:
118
        case SE_14_SUBWAY_CAR:
119
        case SE_15_SLIDING_DOOR:
2970 helixhorne 120
        case SE_16_REACTOR:
121
        case SE_26:
122
        case SE_30_TWO_WAY_TRAIN:
1949 helixhorne 123
            Sect_SetInterpolation(sprite[k].sectnum);
124
            break;
125
        }
126
 
127
        k = nextspritestat[k];
128
    }
129
 
130
    for (i=g_numInterpolations-1; i>=0; i--) bakipos[i] = *curipos[i];
131
    for (i = g_animateCount-1; i>=0; i--)
132
        G_SetInterpolation(animateptr[i]);
133
}
134
 
1143 terminx 135
void ReadSaveGameHeaders(void)
398 terminx 136
{
2207 helixhorne 137
    char fn[16];
138
    int32_t fil, i;
139
 
140
    savehead_t h;
141
 
3975 helixhorne 142
    EDUKE32_STATIC_ASSERT(sizeof(h.savename) == sizeof(ud.savegame[0]));
143
 
2207 helixhorne 144
    Bstrcpy(fn, "dukesav0.esv");
145
 
4472 hendricks2 146
    for (i=0; i<MAXSAVEGAMES; i++)
2207 helixhorne 147
    {
148
        int32_t k;
149
 
150
        fn[7] = i + '0';
151
        fil = kopen4loadfrommod(fn, 0);
152
        if (fil == -1)
153
        {
154
            Bmemset(ud.savegame[i], 0, sizeof(ud.savegame[i]));
155
            continue;
156
        }
157
 
158
        k = sv_loadheader(fil, i, &h);
159
        if (k)
160
        {
3156 helixhorne 161
            // old version, signal to menu code
2207 helixhorne 162
            if (k==2 || k==3)
3156 helixhorne 163
                g_oldverSavegame[i] = 1;
2207 helixhorne 164
            // else h.savename is all zeros (fatal failure, like wrong header
165
            // magic or too short header)
166
        }
167
 
168
        Bmemcpy(ud.savegame[i], h.savename, sizeof(ud.savegame[i]));
169
 
170
        kclose(fil);
171
    }
172
}
173
 
174
int32_t G_LoadSaveHeaderNew(int32_t spot, savehead_t *saveh)
175
{
176
    char fn[16];
2210 helixhorne 177
    int32_t fil, screenshotofs, i;
2207 helixhorne 178
 
4472 hendricks2 179
    Bassert(spot < MAXSAVEGAMES);
180
 
2207 helixhorne 181
    Bstrcpy(fn, "dukesav0.esv");
182
    fn[7] = spot + '0';
183
 
184
    fil = kopen4loadfrommod(fn, 0);
185
    if (fil == -1)
186
        return -1;
187
 
2210 helixhorne 188
    i = sv_loadheader(fil, spot, saveh);
189
    if (i && (i != 2 && i != 3))
2207 helixhorne 190
        goto corrupt;
191
 
192
    if (kread(fil, &screenshotofs, 4) != 4)
193
        goto corrupt;
194
 
195
    walock[TILE_LOADSHOT] = 255;
196
    if (waloff[TILE_LOADSHOT] == 0)
197
        allocache(&waloff[TILE_LOADSHOT], 320*200, &walock[TILE_LOADSHOT]);
4623 terminx 198
    tilesiz[TILE_LOADSHOT].x = 200;
199
    tilesiz[TILE_LOADSHOT].y = 320;
2207 helixhorne 200
    if (screenshotofs)
201
    {
202
        if (kdfread((char *)waloff[TILE_LOADSHOT], 320, 200, fil) != 200)
2210 helixhorne 203
        {
204
            OSD_Printf("G_LoadSaveHeaderNew(%d): failed reading screenshot\n", spot);
2207 helixhorne 205
            goto corrupt;
2210 helixhorne 206
        }
2207 helixhorne 207
    }
208
    else
209
    {
210
        Bmemset((char *)waloff[TILE_LOADSHOT], 0, 320*200);
211
    }
212
    invalidatetile(TILE_LOADSHOT, 0, 255);
213
 
214
    kclose(fil);
215
    return 0;
216
 
217
corrupt:
218
    kclose(fil);
219
    return 1;
220
}
221
 
5 Plagman 222
 
2207 helixhorne 223
static void sv_postudload();
224
 
3789 helixhorne 225
// XXX: keyboard input 'blocked' after load fail? (at least ESC?)
1205 terminx 226
int32_t G_LoadPlayer(int32_t spot)
5 Plagman 227
{
2207 helixhorne 228
    char fn[16];
229
    int32_t fil, i;
230
 
231
    savehead_t h;
232
 
4472 hendricks2 233
    Bassert(spot < MAXSAVEGAMES);
234
 
2207 helixhorne 235
    Bstrcpy(fn, "dukesav0.esv");
236
    fn[7] = spot + '0';
237
 
238
    fil = kopen4loadfrommod(fn, 0);
239
    if (fil == -1)
240
        return -1;
241
 
242
    ready2send = 0;
243
 
244
    i = sv_loadheader(fil, spot, &h);
4610 terminx 245
    if ((i && i != 2) || h.numplayers != ud.multimode)
2207 helixhorne 246
    {
247
        if (i == 2 || i == 3)
248
            P_DoQuote(QUOTE_SAVE_BAD_VERSION, g_player[myconnectindex].ps);
249
        else if (h.numplayers!=ud.multimode)
250
            P_DoQuote(QUOTE_SAVE_BAD_PLAYERS, g_player[myconnectindex].ps);
251
 
252
        kclose(fil);
253
        ototalclock = totalclock;
254
        ready2send = 1;
255
 
256
        return 1;
257
    }
258
 
259
    // some setup first
260
    ud.multimode = h.numplayers;
261
 
262
    if (numplayers > 1)
263
    {
264
        pub = NUMPAGES;
265
        pus = NUMPAGES;
266
        G_UpdateScreenArea();
267
        G_DrawBackground();
268
        menutext(160,100, 0,0, "LOADING...");
269
        nextpage();
270
    }
271
 
272
    Net_WaitForServer();
273
 
274
    FX_StopAllSounds();
275
    S_ClearSoundLocks();
276
 
277
    if (spot >= 0 && numplayers==1)
278
    {
279
        Bmemcpy(ud.savegame[spot], h.savename, sizeof(ud.savegame[0]));
280
        ud.savegame[spot][sizeof(ud.savegame[0])-1] = 0;
281
    }
282
 
283
    // non-"m_" fields will be loaded from svgm_udnetw
284
    ud.m_volume_number = h.volnum;
285
    ud.m_level_number = h.levnum;
286
    ud.m_player_skill = h.skill;
287
 
4589 helixhorne 288
    {
289
        // NOTE: Bmemcpy needed for SAVEGAME_MUSIC.
290
        EDUKE32_STATIC_ASSERT(sizeof(boardfilename) == sizeof(h.boardfn));
291
        Bmemcpy(boardfilename, h.boardfn, sizeof(boardfilename));
292
    }
293
 
4257 helixhorne 294
    E_MapArt_Setup(h.boardfn);  // XXX: Better after the following filename tweaking?
2207 helixhorne 295
 
296
    if (boardfilename[0])
297
        Bstrcpy(currentboardfilename, boardfilename);
298
    else if (MapInfo[h.volnum*MAXLEVELS + h.levnum].filename)
299
        Bstrcpy(currentboardfilename, MapInfo[h.volnum*MAXLEVELS + h.levnum].filename);
300
 
301
    if (currentboardfilename[0])
302
    {
3735 helixhorne 303
        append_ext_UNSAFE(currentboardfilename, ".mhk");
2207 helixhorne 304
        loadmaphack(currentboardfilename);
305
    }
306
 
307
    Bmemcpy(currentboardfilename, boardfilename, BMAX_PATH);
308
 
4596 terminx 309
    if (i == 2)
2207 helixhorne 310
    {
4596 terminx 311
        G_NewGame_EnterLevel();
2207 helixhorne 312
    }
4596 terminx 313
    else
314
    {
315
        // read the rest...
316
        i = sv_loadsnapshot(fil, spot, &h);
317
        if (i)
318
        {
319
            // in theory, we could load into an initial dump first and trivially
320
            // recover if things go wrong...
321
            Bsprintf(tempbuf, "Loading save game file \"%s\" failed (code %d), cannot recover.", fn, i);
322
            G_GameExit(tempbuf);
323
        }
324
    }
2207 helixhorne 325
    sv_postudload();  // ud.m_XXX = ud.XXX
326
 
4745 terminx 327
    VM_OnEvent(EVENT_LOADGAME, g_player[myconnectindex].ps->i, myconnectindex);
2688 hendricks2 328
 
2207 helixhorne 329
    return 0;
330
}
331
 
4450 helixhorne 332
////////// TIMER SAVING/RESTORING //////////
5 Plagman 333
 
4450 helixhorne 334
static struct {
335
    int32_t totalclock, totalclocklock;  // engine
336
    int32_t ototalclock, lockclock;  // game
337
} g_timers;
338
 
339
static void G_SaveTimers(void)
340
{
341
    g_timers.totalclock = totalclock;
342
    g_timers.totalclocklock = totalclocklock;
343
    g_timers.ototalclock = ototalclock;
344
    g_timers.lockclock = lockclock;
345
}
346
 
347
static void G_RestoreTimers(void)
348
{
349
    sampletimer();
350
 
351
    totalclock = g_timers.totalclock;
352
    totalclocklock = g_timers.totalclocklock;
353
    ototalclock = g_timers.ototalclock;
354
    lockclock = g_timers.lockclock;
355
}
356
 
357
//////////
358
 
1205 terminx 359
int32_t G_SavePlayer(int32_t spot)
5 Plagman 360
{
2207 helixhorne 361
    char fn[16];
362
//    char mpfn[16];
363
    FILE *fil;
364
 
4472 hendricks2 365
    Bassert(spot < MAXSAVEGAMES);
366
 
4450 helixhorne 367
    G_SaveTimers();
368
 
2207 helixhorne 369
    Bstrcpy(fn, "dukesav0.esv");
370
    fn[7] = spot + '0';
371
 
372
//    Bstrcpy(mpfn, "edukA_00.esv");
373
 
374
    Net_WaitForServer();
375
    ready2send = 0;
376
 
377
    {
378
        char temp[BMAX_PATH];
379
 
2997 helixhorne 380
        if (G_ModDirSnprintf(temp, sizeof(temp), "%s", fn))
381
        {
382
            OSD_Printf("G_SavePlayer: file name \"%s\" too long\n", fn);
383
            return -1;
384
        }
2207 helixhorne 385
 
2393 helixhorne 386
        errno = 0;
2207 helixhorne 387
        fil = fopen(temp, "wb");
388
        if (!fil)
2393 helixhorne 389
        {
2538 hendricks2 390
            OSD_Printf("G_SavePlayer: failed opening \"%s\" for writing: %s\n",
2393 helixhorne 391
                       temp, strerror(errno));
2998 helixhorne 392
            return -1;
2393 helixhorne 393
        }
2207 helixhorne 394
    }
395
 
2236 helixhorne 396
#ifdef POLYMER
3784 terminx 397
    if (getrendermode() == REND_POLYMER)
2236 helixhorne 398
        polymer_resetlights();
399
#endif
400
 
4745 terminx 401
    VM_OnEvent(EVENT_SAVEGAME, g_player[myconnectindex].ps->i, myconnectindex);
2688 hendricks2 402
 
2207 helixhorne 403
    // SAVE!
2218 helixhorne 404
    sv_saveandmakesnapshot(fil, spot, 0, 0, 0);
2207 helixhorne 405
 
406
    fclose(fil);
407
 
408
    if (!g_netServer && ud.multimode < 2)
409
    {
3796 helixhorne 410
#ifdef LUNATIC
411
        if (!g_savedOK)
412
            Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "^10Failed Saving Game");
413
        else
414
#endif
415
            Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "Game Saved");
2207 helixhorne 416
        P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
417
    }
418
 
419
    ready2send = 1;
420
    Net_WaitForServer();
421
 
4450 helixhorne 422
    G_RestoreTimers();
2207 helixhorne 423
    ototalclock = totalclock;
424
 
425
    return 0;
426
}
427
 
2999 helixhorne 428
void G_LoadPlayerMaybeMulti(int32_t slot)
429
{
430
    if (g_netServer || ud.multimode > 1)
431
    {
432
        Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "Multiplayer Loading Not Yet Supported");
433
        P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
5 Plagman 434
 
2999 helixhorne 435
//        G_LoadPlayer(-1-g_lastSaveSlot);
436
//        g_player[myconnectindex].ps->gm = MODE_GAME;
437
    }
438
    else
439
    {
440
        int32_t c = G_LoadPlayer(slot);
441
        if (c == 0)
442
            g_player[myconnectindex].ps->gm = MODE_GAME;
443
    }
444
}
445
 
446
void G_SavePlayerMaybeMulti(int32_t slot)
447
{
448
    Bassert(slot >= 0);
449
 
3636 terminx 450
    CONFIG_WriteSetup(2);
3579 hendricks2 451
 
2999 helixhorne 452
    if (g_netServer || ud.multimode > 1)
453
    {
454
        Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "Multiplayer Saving Not Yet Supported");
455
        P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
456
//        G_SavePlayer(-1-slot);
457
    }
458
    else
459
    {
460
        G_SavePlayer(slot);
461
    }
462
}
463
 
1595 helixhorne 464
////////// GENERIC SAVING/LOADING SYSTEM //////////
465
 
466
typedef struct dataspec_
467
{
468
    uint32_t flags;
469
    void *ptr;
470
    uint32_t size;
471
    intptr_t cnt;
472
} dataspec_t;
473
 
474
#define SV_DEFAULTCOMPRTHRES 8
475
static uint8_t savegame_diffcompress;  // 0:none, 1:Ken's LZW in cache1d.c
476
static uint8_t savegame_comprthres;
477
 
478
 
479
#define DS_DYNAMIC 1  // dereference .ptr one more time
480
#define DS_STRING 2
481
#define DS_CMP 4
482
// 8
483
#define DS_CNT(x) ((sizeof(x))<<3)  // .cnt is pointer to...
484
#define DS_CNT16 16
485
#define DS_CNT32 32
486
#define DS_CNTMASK (8|DS_CNT16|DS_CNT32|64)
487
// 64
488
#define DS_LOADFN 128  // .ptr is function that is run when loading
489
#define DS_SAVEFN 256  // .ptr is function that is run when saving
1799 helixhorne 490
#define DS_NOCHK 1024  // don't check for diffs (and don't write out in dump) since assumed constant throughout demo
1882 helixhorne 491
#define DS_PROTECTFN 512
1595 helixhorne 492
#define DS_END (0x70000000)
493
 
494
static int32_t ds_getcnt(const dataspec_t *sp)
495
{
496
    switch (sp->flags&DS_CNTMASK)
497
    {
1599 terminx 498
    case 0: return sp->cnt;
1595 helixhorne 499
    case DS_CNT16: return *((int16_t *)sp->cnt);
500
    case DS_CNT32: return *((int32_t *)sp->cnt);
501
    default: return -1;
502
    }
503
}
504
 
2234 helixhorne 505
static void ds_get(const dataspec_t *sp, void **ptr, int32_t *cnt)
1595 helixhorne 506
{
507
    *cnt = ds_getcnt(sp);
508
 
509
    if (sp->flags&DS_DYNAMIC)
510
        *ptr = *((void **)sp->ptr);
511
    else
512
        *ptr = sp->ptr;
513
}
514
 
515
// write state to file and/or to dump
516
static uint8_t *writespecdata(const dataspec_t *spec, FILE *fil, uint8_t *dump)
517
{
518
    int32_t cnt;
2234 helixhorne 519
    void *ptr;
1595 helixhorne 520
    const dataspec_t *sp=spec;
521
 
522
    for (; sp->flags!=DS_END; sp++)
523
    {
524
        if (sp->flags&(DS_SAVEFN|DS_LOADFN))
525
        {
526
            if (sp->flags&DS_SAVEFN)
2207 helixhorne 527
                (*(void (*)(void))sp->ptr)();
1595 helixhorne 528
            continue;
529
        }
530
 
531
        if (!fil && (sp->flags&(DS_NOCHK|DS_CMP|DS_STRING)))
532
            continue;
533
 
534
        if (sp->flags&DS_STRING)
535
        {
3177 helixhorne 536
            fwrite(sp->ptr, Bstrlen((const char *)sp->ptr), 1, fil);  // not null-terminated!
1595 helixhorne 537
            continue;
538
        }
539
 
540
        ds_get(sp, &ptr, &cnt);
541
        if (cnt < 0) { OSD_Printf("wsd: cnt=%d, f=0x%x.\n",cnt,sp->flags); continue; }
542
 
543
        if (fil)
544
        {
2158 helixhorne 545
            if (((sp->flags&DS_CNTMASK)==0 && sp->size*cnt<=savegame_comprthres)
1599 terminx 546
                    || (sp->flags&DS_CMP))
1595 helixhorne 547
                fwrite(ptr, sp->size, cnt, fil);
548
            else
549
                dfwrite((void *)ptr, sp->size, cnt, fil);
550
        }
551
 
552
        if (dump && (sp->flags&(DS_NOCHK|DS_CMP))==0)
553
        {
554
            Bmemcpy(dump, ptr, sp->size*cnt);
555
            dump += sp->size*cnt;
556
        }
557
    }
558
    return dump;
559
}
560
 
561
// let havedump=dumpvar&&*dumpvar
562
// (fil>=0 && havedump): first restore dump from file, then restore state from dump
563
// (fil<0 && havedump): only restore state from dump
564
// (fil>=0 && !havedump): only restore state from file
565
static int32_t readspecdata(const dataspec_t *spec, int32_t fil, uint8_t **dumpvar)
566
{
567
    int32_t cnt, i, j;
568
    void *ptr;
569
    uint8_t *dump=dumpvar?*dumpvar:NULL, *mem;
570
    const dataspec_t *sp=spec;
571
    static char cmpstrbuf[32];
572
 
573
    for (; sp->flags!=DS_END; sp++)
574
    {
575
        if (fil < 0 && sp->flags&(DS_NOCHK|DS_STRING|DS_CMP))  // we're updating
576
            continue;
577
 
578
        if (sp->flags&(DS_LOADFN|DS_SAVEFN))
579
        {
580
            if (sp->flags&DS_LOADFN)
2566 helixhorne 581
                (*(void (*)())sp->ptr)();
1595 helixhorne 582
            continue;
583
        }
584
 
585
        if (sp->flags&(DS_STRING|DS_CMP))  // DS_STRING and DS_CMP is for static data only
586
        {
587
            if (sp->flags&(DS_STRING))
3177 helixhorne 588
                i = Bstrlen((const char *)sp->ptr);
1595 helixhorne 589
            else
590
                i = sp->size*sp->cnt;
591
 
592
            j=kread(fil, cmpstrbuf, i);
593
            if (j!=i || Bmemcmp(sp->ptr, cmpstrbuf, i))
594
            {
2212 helixhorne 595
                OSD_Printf("rsd: spec=%s, idx=%d:\n", (char *)spec->ptr, (int32_t)(sp-spec));
1595 helixhorne 596
                if (j!=i)
2207 helixhorne 597
                    OSD_Printf("    kread returned %d, expected %d.\n", j, i);
1595 helixhorne 598
                else
2207 helixhorne 599
                    OSD_Printf("    sp->ptr and cmpstrbuf not identical!\n");
1595 helixhorne 600
                return -1;
601
            }
602
            continue;
603
        }
604
 
2234 helixhorne 605
        ds_get(sp, &ptr, &cnt);
1595 helixhorne 606
        if (cnt < 0) { OSD_Printf("rsd: cnt<0... wtf?\n"); return -1; }
607
 
608
        if (fil>=0)
609
        {
3177 helixhorne 610
            mem = (dump && (sp->flags&DS_NOCHK)==0) ? dump : (uint8_t *)ptr;
1595 helixhorne 611
 
2158 helixhorne 612
            if ((sp->flags&DS_CNTMASK)==0 && sp->size*cnt<=savegame_comprthres)
1595 helixhorne 613
            {
614
                i = kread(fil, mem, cnt*sp->size);
615
                j = cnt*sp->size;
616
            }
617
            else
618
            {
619
                i = kdfread(mem, sp->size, cnt, fil);
620
                j = cnt;
621
            }
622
            if (i!=j)
623
            {
2212 helixhorne 624
                OSD_Printf("rsd: spec=%s, idx=%d, mem=%p\n", (char *)spec->ptr, (int32_t)(sp-spec), mem);
2207 helixhorne 625
                OSD_Printf("     (%s): read %d, expected %d!\n",
2158 helixhorne 626
                           ((sp->flags&DS_CNTMASK)==0 && sp->size*cnt<=savegame_comprthres)?
2207 helixhorne 627
                           "uncompressed":"compressed", i, j);
1599 terminx 628
 
1595 helixhorne 629
                if (i==-1)
2190 helixhorne 630
                    OSD_Printf("     read: %s\n", strerror(errno));
1595 helixhorne 631
                return -1;
632
            }
633
        }
634
 
635
        if (dump && (sp->flags&DS_NOCHK)==0)
636
        {
637
            Bmemcpy(ptr, dump, sp->size*cnt);
638
            dump += sp->size*cnt;
639
        }
640
    }
641
 
642
    if (dumpvar)
643
        *dumpvar = dump;
644
    return 0;
645
}
646
 
647
#define UINT(bits) uint##bits##_t
648
#define BYTES(bits) (bits>>3)
649
#define VAL(bits,p) (*(UINT(bits) *)(p))
650
 
651
static void docmpsd(const void *ptr, void *dump, uint32_t size, uint32_t cnt, uint8_t **diffvar)
652
{
653
    uint8_t *retdiff = *diffvar;
654
 
655
    // Hail to the C preprocessor, baby!
1599 terminx 656
#define CPSINGLEVAL(Datbits) \
1595 helixhorne 657
        if (VAL(Datbits, ptr) != VAL(Datbits, dump))  \
658
        {                                             \
659
            VAL(Datbits, retdiff) = VAL(Datbits, dump) = VAL(Datbits, ptr); \
660
            *diffvar = retdiff+BYTES(Datbits);        \
661
        }
662
 
663
    if (cnt==1)
664
        switch (size)
665
        {
666
        case 8: CPSINGLEVAL(64); return;
667
        case 4: CPSINGLEVAL(32); return;
668
        case 2: CPSINGLEVAL(16); return;
669
        case 1: CPSINGLEVAL(8); return;
670
        }
671
 
1599 terminx 672
#define CPELTS(Idxbits, Datbits) do \
1595 helixhorne 673
    {                                         \
674
        for (i=0; i<nelts; i++)               \
675
        {                                     \
676
            if (*p!=*op)                      \
677
            {                                 \
3180 helixhorne 678
                *op = *p;      \
1595 helixhorne 679
                VAL(Idxbits, retdiff) = i;    \
680
                retdiff += BYTES(Idxbits);    \
681
                VAL(Datbits, retdiff) = *p;   \
682
                retdiff += BYTES(Datbits);    \
683
            }                                 \
684
            p++;                              \
685
            op++;                             \
686
        }                                     \
687
        VAL(Idxbits, retdiff) = -1;           \
688
        retdiff += BYTES(Idxbits);            \
689
    } while (0)
690
 
1599 terminx 691
#define CPDATA(Datbits) do \
1595 helixhorne 692
    { \
3177 helixhorne 693
        const UINT(Datbits) *p=(UINT(Datbits) *)ptr;    \
694
        UINT(Datbits) *op=(UINT(Datbits) *)dump;        \
4658 terminx 695
        uint32_t i, nelts=tabledivide32_noinline(size*cnt, BYTES(Datbits));    \
3177 helixhorne 696
        if (nelts>65536)                                \
697
            CPELTS(32,Datbits);                         \
698
        else if (nelts>256)                             \
699
            CPELTS(16,Datbits);                         \
700
        else                                            \
701
            CPELTS(8,Datbits);                          \
1595 helixhorne 702
    } while (0)
703
 
704
    if (size==8)
705
        CPDATA(64);
706
    else if ((size&3)==0)
707
        CPDATA(32);
708
    else if ((size&1)==0)
709
        CPDATA(16);
710
    else
711
        CPDATA(8);
712
 
713
    *diffvar = retdiff;
714
    return;
715
 
1599 terminx 716
#undef CPELTS
717
#undef CPSINGLEVAL
718
#undef CPDATA
1595 helixhorne 719
}
720
 
721
// get the number of elements to be monitored for changes
722
static int32_t getnumvar(const dataspec_t *spec)
723
{
724
    int32_t n=0;
1599 terminx 725
    for (; spec->flags!=DS_END; spec++)
1595 helixhorne 726
        n += (spec->flags&(DS_STRING|DS_CMP|DS_NOCHK|DS_SAVEFN|DS_LOADFN) ? 0 : 1);
727
    return n;
728
}
729
 
730
// update dump at *dumpvar with new state and write diff to *diffvar
731
static void cmpspecdata(const dataspec_t *spec, uint8_t **dumpvar, uint8_t **diffvar)
732
{
2234 helixhorne 733
    void *ptr;
1595 helixhorne 734
    uint8_t *dump=*dumpvar, *diff=*diffvar, *tmptr;
735
    const dataspec_t *sp=spec;
3177 helixhorne 736
    int32_t cnt, eltnum=0, nbytes=(getnumvar(spec)+7)>>3, l=Bstrlen((const char *)spec->ptr);
1595 helixhorne 737
 
738
    Bmemcpy(diff, spec->ptr, l);
739
    diff+=l;
740
 
741
    while (nbytes--)
742
        *(diff++) = 0;  // the bitmap of indices which elements of spec have changed go here
743
 
744
    for (sp++; sp->flags!=DS_END; sp++)
745
    {
746
        if ((sp->flags&(DS_NOCHK|DS_STRING|DS_CMP)))
747
            continue;
748
 
1927 helixhorne 749
        if (sp->flags&(DS_LOADFN|DS_SAVEFN))
1595 helixhorne 750
        {
1927 helixhorne 751
            if ((sp->flags&(DS_PROTECTFN))==0)
2207 helixhorne 752
                (*(void (*)())sp->ptr)();
1595 helixhorne 753
            continue;
754
        }
755
 
756
        ds_get(sp, &ptr, &cnt);
757
        if (cnt < 0) { OSD_Printf("csd: cnt=%d, f=0x%x\n", cnt, sp->flags); continue; }
758
 
759
        tmptr = diff;
760
        docmpsd(ptr, dump, sp->size, cnt, &diff);
761
        if (diff != tmptr)
762
            (*diffvar + l)[eltnum>>3] |= 1<<(eltnum&7);
763
        dump += sp->size*cnt;
764
        eltnum++;
765
    }
766
 
767
    *diffvar = diff;
768
    *dumpvar = dump;
769
    return;
770
}
771
 
772
#define VALOFS(bits,p,ofs) (*(((UINT(bits) *)(p)) + (ofs)))
773
 
774
// apply diff to dump, not to state! state is restored from dump afterwards.
775
static int32_t applydiff(const dataspec_t *spec, uint8_t **dumpvar, uint8_t **diffvar)
776
{
777
    uint8_t *dumptr=*dumpvar, *diffptr=*diffvar;
778
    const dataspec_t *sp=spec;
3177 helixhorne 779
    int32_t cnt, eltnum=-1, nbytes=(getnumvar(spec)+7)>>3, l=Bstrlen((const char *)spec->ptr);
1595 helixhorne 780
 
781
    if (Bmemcmp(diffptr, spec->ptr, l))  // check STRING magic (sync check)
782
        return 1;
2185 helixhorne 783
 
1595 helixhorne 784
    diffptr += l+nbytes;
785
 
786
    for (sp++; sp->flags!=DS_END; sp++)
787
    {
788
        if ((sp->flags&(DS_NOCHK|DS_STRING|DS_CMP|DS_LOADFN|DS_SAVEFN)))
789
            continue;
790
 
791
        cnt = ds_getcnt(sp);
792
        if (cnt < 0) return 1;
793
 
794
        eltnum++;
795
        if (((*diffvar + l)[eltnum>>3]&(1<<(eltnum&7))) == 0)
796
        {
797
            dumptr += sp->size*cnt;
798
            continue;
799
        }
800
 
801
// ----------
1599 terminx 802
#define CPSINGLEVAL(Datbits) \
1595 helixhorne 803
            VAL(Datbits, dumptr) = VAL(Datbits, diffptr); \
804
            diffptr += BYTES(Datbits); \
805
            dumptr += BYTES(Datbits)
806
 
807
        if (cnt==1)
808
        {
809
            switch (sp->size)
810
            {
811
            case 8: CPSINGLEVAL(64); continue;
812
            case 4: CPSINGLEVAL(32); continue;
813
            case 2: CPSINGLEVAL(16); continue;
814
            case 1: CPSINGLEVAL(8); continue;
815
            }
816
        }
817
 
1599 terminx 818
#define CPELTS(Idxbits, Datbits) do \
1595 helixhorne 819
        {                                    \
820
            UINT(Idxbits) idx;               \
821
            goto readidx_##Idxbits##_##Datbits; \
822
            do                               \
823
            {                                \
824
                VALOFS(Datbits, dumptr, idx) = VAL(Datbits, diffptr); \
825
                diffptr += BYTES(Datbits);   \
826
readidx_##Idxbits##_##Datbits:               \
827
                idx = VAL(Idxbits, diffptr); \
828
                diffptr += BYTES(Idxbits);   \
829
            } while ((int##Idxbits##_t)idx != -1);  \
830
        } while (0)
831
 
1599 terminx 832
#define CPDATA(Datbits) do \
1595 helixhorne 833
        {                             \
4658 terminx 834
            uint32_t nelts=tabledivide32_noinline(sp->size*cnt, BYTES(Datbits)); \
1595 helixhorne 835
            if (nelts>65536)          \
836
                CPELTS(32,Datbits);   \
837
            else if (nelts>256)       \
838
                CPELTS(16,Datbits);   \
839
            else                      \
840
                CPELTS(8,Datbits);    \
841
        } while (0)
842
 
843
        if (sp->size==8)
844
            CPDATA(64);
845
        else if ((sp->size&3)==0)
846
            CPDATA(32);
847
        else if ((sp->size&1)==0)
848
            CPDATA(16);
849
        else
850
            CPDATA(8);
851
        dumptr += sp->size*cnt;
852
// ----------
853
 
1599 terminx 854
#undef CPELTS
855
#undef CPSINGLEVAL
856
#undef CPDATA
1595 helixhorne 857
    }
858
 
859
    *diffvar = diffptr;
860
    *dumpvar = dumptr;
861
    return 0;
862
}
863
 
864
#undef VAL
865
#undef VALOFS
866
#undef BYTES
867
#undef UINT
868
 
869
// calculate size needed for dump
870
static uint32_t calcsz(const dataspec_t *spec)
871
{
872
    const dataspec_t *sp=spec;
873
    int32_t cnt;
874
    uint32_t dasiz=0;
875
 
876
    for (; sp->flags!=DS_END; sp++)
877
    {
878
        // DS_STRINGs are used as sync checks in the diffs but not in the dump
879
        if ((sp->flags&(DS_CMP|DS_NOCHK|DS_SAVEFN|DS_LOADFN|DS_STRING)))
880
            continue;
881
 
882
        cnt = ds_getcnt(sp);
883
        if (cnt<=0) continue;
884
 
885
        dasiz += cnt*sp->size;
886
    }
887
 
888
    return dasiz;
889
}
890
 
1820 terminx 891
#ifdef USE_OPENGL
1596 plagman 892
static void sv_prespriteextsave();
893
static void sv_postspriteext();
1810 plagman 894
#endif
3456 helixhorne 895
#if !defined LUNATIC
1596 plagman 896
static void sv_calcbitptrsize();
897
static void sv_prescriptsave_once();
1598 helixhorne 898
static void sv_prescriptload_once();
1596 plagman 899
static void sv_postscript_once();
3857 helixhorne 900
#else
901
// Recreate Lua state.
902
// XXX: It may matter a great deal when this is run from if the Lua code refers
903
// to C-side data at file scope. Such usage is strongly discouraged though.
904
static void sv_create_lua_state(void)
905
{
906
    El_CreateGameState();
907
    G_PostCreateGameState();
908
}
3456 helixhorne 909
#endif
1596 plagman 910
static void sv_preactordatasave();
911
static void sv_postactordata();
912
static void sv_preanimateptrsave();
913
static void sv_postanimateptr();
914
static void sv_prequote();
915
static void sv_quotesave();
916
static void sv_quoteload();
917
static void sv_prequoteredef();
918
static void sv_quoteredefsave();
919
static void sv_quoteredefload();
920
static void sv_postquoteredef();
921
static void sv_restsave();
922
static void sv_restload();
923
 
924
#define SVARDATALEN \
925
    ((sizeof(g_player[0].user_name)+sizeof(g_player[0].pcolor)+sizeof(g_player[0].pteam) \
3257 helixhorne 926
      +sizeof(g_player[0].frags)+sizeof(DukePlayer_t))*MAXPLAYERS)
1596 plagman 927
 
3857 helixhorne 928
#if !defined LUNATIC
1596 plagman 929
static uint32_t savegame_bitptrsize;
3857 helixhorne 930
#endif
1596 plagman 931
static uint8_t savegame_quotedef[MAXQUOTES>>3];
1599 terminx 932
static char(*savegame_quotes)[MAXQUOTELEN];
933
static char(*savegame_quoteredefs)[MAXQUOTELEN];
1596 plagman 934
static uint8_t savegame_restdata[SVARDATALEN];
935
 
936
static const dataspec_t svgm_udnetw[] =
937
{
3177 helixhorne 938
    { DS_STRING, (void *)"blK:udnt", 0, 1 },
1596 plagman 939
    { 0, &ud.multimode, sizeof(ud.multimode), 1 },
940
    { 0, &g_numPlayerSprites, sizeof(g_numPlayerSprites), 1 },
941
    { 0, &g_playerSpawnPoints, sizeof(g_playerSpawnPoints), 1 },
942
 
943
    { DS_NOCHK, &ud.volume_number, sizeof(ud.volume_number), 1 },
944
    { DS_NOCHK, &ud.level_number, sizeof(ud.level_number), 1 },
945
    { DS_NOCHK, &ud.player_skill, sizeof(ud.player_skill), 1 },
946
 
947
    { DS_NOCHK, &ud.from_bonus, sizeof(ud.from_bonus), 1 },
948
    { DS_NOCHK, &ud.secretlevel, sizeof(ud.secretlevel), 1 },
949
    { DS_NOCHK, &ud.respawn_monsters, sizeof(ud.respawn_monsters), 1 },
950
    { DS_NOCHK, &ud.respawn_items, sizeof(ud.respawn_items), 1 },
951
    { DS_NOCHK, &ud.respawn_inventory, sizeof(ud.respawn_inventory), 1 },
952
    { 0, &ud.god, sizeof(ud.god), 1 },
953
    { 0, &ud.auto_run, sizeof(ud.auto_run), 1 },
954
//    { DS_NOCHK, &ud.crosshair, sizeof(ud.crosshair), 1 },
955
    { DS_NOCHK, &ud.monsters_off, sizeof(ud.monsters_off), 1 },
956
    { DS_NOCHK, &ud.last_level, sizeof(ud.last_level), 1 },
957
    { 0, &ud.eog, sizeof(ud.eog), 1 },
958
    { DS_NOCHK, &ud.coop, sizeof(ud.coop), 1 },
959
    { DS_NOCHK, &ud.marker, sizeof(ud.marker), 1 },
960
    { DS_NOCHK, &ud.ffire, sizeof(ud.ffire), 1 },
961
    { DS_NOCHK, &ud.noexits, sizeof(ud.noexits), 1 },
962
    { DS_NOCHK, &ud.playerai, sizeof(ud.playerai), 1 },
1598 helixhorne 963
    { 0, &ud.pause_on, sizeof(ud.pause_on), 1 },
1596 plagman 964
    { DS_NOCHK, &currentboardfilename[0], BMAX_PATH, 1 },
2207 helixhorne 965
//    { DS_LOADFN, (void *)&sv_postudload, 0, 1 },
1596 plagman 966
    { 0, connectpoint2, sizeof(connectpoint2), 1 },
967
    { 0, &randomseed, sizeof(randomseed), 1 },
968
    { 0, &g_globalRandom, sizeof(g_globalRandom), 1 },
3929 helixhorne 969
#ifdef LUNATIC
970
    // Save game tic count for Lunatic because it is exposed to userland. See
971
    // test/helixspawner.lua for an example.
972
    { 0, &g_moveThingsCount, sizeof(g_moveThingsCount), 1 },
973
#endif
1596 plagman 974
//    { 0, &lockclock_dummy, sizeof(lockclock), 1 },
975
    { DS_END, 0, 0, 0 }
976
};
977
 
2270 helixhorne 978
#if !defined DEBUG_MAIN_ARRAYS
979
# define DS_MAINAR DS_DYNAMIC
980
#else
981
# define DS_MAINAR 0
982
#endif
983
 
1596 plagman 984
static const dataspec_t svgm_secwsp[] =
985
{
3177 helixhorne 986
    { DS_STRING, (void *)"blK:swsp", 0, 1 },
1596 plagman 987
    { DS_NOCHK, &numwalls, sizeof(numwalls), 1 },
2270 helixhorne 988
    { DS_MAINAR|DS_CNT(numwalls), &wall, sizeof(walltype), (intptr_t)&numwalls },
1596 plagman 989
    { DS_NOCHK, &numsectors, sizeof(numsectors), 1 },
2270 helixhorne 990
    { DS_MAINAR|DS_CNT(numsectors), &sector, sizeof(sectortype), (intptr_t)&numsectors },
991
    { DS_MAINAR, &sprite, sizeof(spritetype), MAXSPRITES },
1882 helixhorne 992
#ifdef YAX_ENABLE
993
    { DS_NOCHK, &numyaxbunches, sizeof(numyaxbunches), 1 },
3658 helixhorne 994
# if !defined NEW_MAP_FORMAT
1882 helixhorne 995
    { DS_CNT(numsectors), yax_bunchnum, sizeof(yax_bunchnum[0]), (intptr_t)&numsectors },
996
    { DS_CNT(numwalls), yax_nextwall, sizeof(yax_nextwall[0]), (intptr_t)&numwalls },
3658 helixhorne 997
# endif
1882 helixhorne 998
    { DS_LOADFN|DS_PROTECTFN, (void *)&sv_postyaxload, 0, 1 },
999
#endif
2483 helixhorne 1000
    { 0, &Numsprites, sizeof(Numsprites), 1 },
2470 helixhorne 1001
    { 0, &tailspritefree, sizeof(tailspritefree), 1 },
1596 plagman 1002
    { 0, &headspritesect[0], sizeof(headspritesect[0]), MAXSECTORS+1 },
1003
    { 0, &prevspritesect[0], sizeof(prevspritesect[0]), MAXSPRITES },
1004
    { 0, &nextspritesect[0], sizeof(nextspritesect[0]), MAXSPRITES },
1005
    { 0, &headspritestat[0], sizeof(headspritestat[0]), MAXSTATUS+1 },
1006
    { 0, &prevspritestat[0], sizeof(prevspritestat[0]), MAXSPRITES },
1007
    { 0, &nextspritestat[0], sizeof(nextspritestat[0]), MAXSPRITES },
1820 terminx 1008
#ifdef USE_OPENGL
1596 plagman 1009
    { DS_SAVEFN, (void *)&sv_prespriteextsave, 0, 1 },
1010
#endif
2270 helixhorne 1011
    { DS_MAINAR, &spriteext, sizeof(spriteext_t), MAXSPRITES },
1820 terminx 1012
#ifdef USE_OPENGL
1596 plagman 1013
    { DS_SAVEFN|DS_LOADFN, (void *)&sv_postspriteext, 0, 1 },
1014
#endif
1015
    { 0, &DynamicTileMap[0], sizeof(DynamicTileMap[0]), MAXTILES },  // NOCHK?
3834 hendricks2 1016
    { 0, &DynamicSoundMap[0], sizeof(DynamicSoundMap[0]), MAXSOUNDS },  // NOCHK?
1596 plagman 1017
    { DS_NOCHK, &g_numCyclers, sizeof(g_numCyclers), 1 },
1018
    { DS_CNT(g_numCyclers), &cyclers[0][0], sizeof(cyclers[0]), (intptr_t)&g_numCyclers },
1019
    { DS_NOCHK, &g_numAnimWalls, sizeof(g_numAnimWalls), 1 },
1020
    { DS_CNT(g_numAnimWalls), &animwall, sizeof(animwall[0]), (intptr_t)&g_numAnimWalls },
1021
    { DS_NOCHK, &g_mirrorCount, sizeof(g_mirrorCount), 1 },
4385 terminx 1022
    { DS_NOCHK, &g_mirrorWall[0], sizeof(g_mirrorWall[0]), ARRAY_SIZE(g_mirrorWall) },
1023
    { DS_NOCHK, &g_mirrorSector[0], sizeof(g_mirrorSector[0]), ARRAY_SIZE(g_mirrorSector) },
1596 plagman 1024
// projectiles
1025
    { 0, &SpriteProjectile[0], sizeof(projectile_t), MAXSPRITES },
3151 helixhorne 1026
    { 0, &ProjectileData[0], sizeof(projectile_t), MAXTILES },
1596 plagman 1027
    { 0, &everyothertime, sizeof(everyothertime), 1 },
1028
    { DS_END, 0, 0, 0 }
1029
};
1030
 
1031
static const dataspec_t svgm_script[] =
1032
{
3177 helixhorne 1033
    { DS_STRING, (void *)"blK:scri", 0, 1 },
3456 helixhorne 1034
#if !defined LUNATIC
1596 plagman 1035
    { DS_NOCHK, &g_scriptSize, sizeof(g_scriptSize), 1 },
1036
    { DS_SAVEFN|DS_LOADFN|DS_NOCHK, (void *)&sv_calcbitptrsize, 0, 1 },
1037
    { DS_DYNAMIC|DS_CNT(savegame_bitptrsize)|DS_NOCHK, &bitptr, sizeof(bitptr[0]), (intptr_t)&savegame_bitptrsize },
1038
 
1039
    { DS_SAVEFN|DS_NOCHK, (void *)&sv_prescriptsave_once, 0, 1 },
3456 helixhorne 1040
#endif
3151 helixhorne 1041
    { DS_NOCHK, &g_tile[0], sizeof(tiledata_t), MAXTILES },
3456 helixhorne 1042
#if !defined LUNATIC
1598 helixhorne 1043
    { DS_LOADFN|DS_NOCHK, (void *)&sv_prescriptload_once, 0, 1 },
1596 plagman 1044
    { DS_DYNAMIC|DS_CNT(g_scriptSize)|DS_NOCHK, &script, sizeof(script[0]), (intptr_t)&g_scriptSize },
1045
//    { DS_NOCHK, &apScriptGameEvent[0], sizeof(apScriptGameEvent[0]), MAXGAMEEVENTS },
1046
    { DS_SAVEFN|DS_LOADFN|DS_NOCHK, (void *)&sv_postscript_once, 0, 1 },
3456 helixhorne 1047
#endif
1596 plagman 1048
    { DS_SAVEFN, (void *)&sv_preactordatasave, 0, 1 },
1677 terminx 1049
    { 0, &actor[0], sizeof(actor_t), MAXSPRITES },
1596 plagman 1050
    { DS_SAVEFN|DS_LOADFN, (void *)&sv_postactordata, 0, 1 },
3857 helixhorne 1051
#if defined LUNATIC
1052
    { DS_LOADFN|DS_NOCHK, (void *)&sv_create_lua_state, 0, 1 },
1053
#endif
1596 plagman 1054
    { DS_END, 0, 0, 0 }
1055
};
1056
 
1057
static const dataspec_t svgm_anmisc[] =
1058
{
3177 helixhorne 1059
    { DS_STRING, (void *)"blK:anms", 0, 1 },
1596 plagman 1060
    { 0, &g_animateCount, sizeof(g_animateCount), 1 },
1061
    { 0, &animatesect[0], sizeof(animatesect[0]), MAXANIMATES },
1062
    { 0, &animategoal[0], sizeof(animategoal[0]), MAXANIMATES },
1063
    { 0, &animatevel[0], sizeof(animatevel[0]), MAXANIMATES },
1064
    { DS_SAVEFN, (void *)&sv_preanimateptrsave, 0, 1 },
1065
    { 0, &animateptr[0], sizeof(animateptr[0]), MAXANIMATES },
1066
    { DS_SAVEFN|DS_LOADFN , (void *)&sv_postanimateptr, 0, 1 },
4220 helixhorne 1067
    { 0, &g_curViewscreen, sizeof(g_curViewscreen), 1 },
4385 terminx 1068
    { 0, &msx[0], sizeof(msx[0]), ARRAY_SIZE(msx) },
1069
    { 0, &msy[0], sizeof(msy[0]), ARRAY_SIZE(msy) },
1598 helixhorne 1070
    { 0, &g_spriteDeleteQueuePos, sizeof(g_spriteDeleteQueuePos), 1 },
1071
    { DS_NOCHK, &g_spriteDeleteQueueSize, sizeof(g_spriteDeleteQueueSize), 1 },
1072
    { DS_CNT(g_spriteDeleteQueueSize), &SpriteDeletionQueue[0], sizeof(int16_t), (intptr_t)&g_spriteDeleteQueueSize },
1596 plagman 1073
    { 0, &show2dsector[0], sizeof(uint8_t), MAXSECTORS>>3 },
1074
    { DS_NOCHK, &g_numClouds, sizeof(g_numClouds), 1 },
1075
    { 0, &clouds[0], sizeof(clouds), 1 },
1076
    { 0, &cloudx[0], sizeof(cloudx), 1 },
1077
    { 0, &cloudy[0], sizeof(cloudy), 1 },
3976 helixhorne 1078
    { 0, &g_pskyidx, sizeof(g_pskyidx), 1 },  // DS_NOCHK?
1596 plagman 1079
    { 0, &g_earthquakeTime, sizeof(g_earthquakeTime), 1 },
1080
 
1081
    { DS_SAVEFN|DS_LOADFN|DS_NOCHK, (void *)sv_prequote, 0, 1 },
1082
    { DS_SAVEFN, (void *)&sv_quotesave, 0, 1 },
1083
    { DS_NOCHK, &savegame_quotedef, sizeof(savegame_quotedef), 1 },  // quotes can change during runtime, but new quote numbers cannot be allocated
1084
    { DS_DYNAMIC, &savegame_quotes, MAXQUOTELEN, MAXQUOTES },
1085
    { DS_LOADFN, (void *)&sv_quoteload, 0, 1 },
1086
 
1087
    { DS_NOCHK, &g_numQuoteRedefinitions, sizeof(g_numQuoteRedefinitions), 1 },
1088
    { DS_NOCHK|DS_SAVEFN|DS_LOADFN, (void *)&sv_prequoteredef, 0, 1 },
1089
    { DS_NOCHK|DS_SAVEFN, (void *)&sv_quoteredefsave, 0, 1 },  // quote redefinitions replace quotes at runtime, but cannot be changed after CON compilation
1090
    { DS_NOCHK|DS_DYNAMIC|DS_CNT(g_numQuoteRedefinitions), &savegame_quoteredefs, MAXQUOTELEN, (intptr_t)&g_numQuoteRedefinitions },
1091
    { DS_NOCHK|DS_LOADFN, (void *)&sv_quoteredefload, 0, 1 },
1092
    { DS_NOCHK|DS_SAVEFN|DS_LOADFN, (void *)&sv_postquoteredef, 0, 1 },
3883 helixhorne 1093
#ifdef LUNATIC
1094
    { 0, g_playerWeapon, sizeof(weapondata_t), MAXPLAYERS*MAX_WEAPONS },
1095
#endif
1596 plagman 1096
    { DS_SAVEFN, (void *)&sv_restsave, 0, 1 },
1097
    { 0, savegame_restdata, 1, sizeof(savegame_restdata) },  // sz/cnt swapped for kdfread
1098
    { DS_LOADFN, (void *)&sv_restload, 0, 1 },
1099
 
3177 helixhorne 1100
    { DS_STRING, (void *)"savegame_end", 0, 1 },
1596 plagman 1101
    { DS_END, 0, 0, 0 }
1102
};
1103
 
3789 helixhorne 1104
#if !defined LUNATIC
1595 helixhorne 1105
static dataspec_t *svgm_vars=NULL;
3789 helixhorne 1106
#endif
2207 helixhorne 1107
static uint8_t *dosaveplayer2(FILE *fil, uint8_t *mem);
1108
static int32_t doloadplayer2(int32_t fil, uint8_t **memptr);
1109
static void postloadplayer(int32_t savegamep);
1595 helixhorne 1110
 
1111
// SVGM snapshot system
1112
static uint32_t svsnapsiz;
1113
static uint8_t *svsnapshot, *svinitsnap;
1114
static uint32_t svdiffsiz;
1115
static uint8_t *svdiff;
1116
 
1117
#include "gamedef.h"
1118
 
3789 helixhorne 1119
#if !defined LUNATIC
1120
# define SV_SKIPMASK (/*GAMEVAR_SYSTEM|*/GAMEVAR_READONLY|GAMEVAR_INTPTR|    \
1121
                      GAMEVAR_SHORTPTR|GAMEVAR_CHARPTR /*|GAMEVAR_NORESET*/ |GAMEVAR_SPECIAL)
1595 helixhorne 1122
// setup gamevar data spec for snapshotting and diffing... gamevars must be loaded when called
1123
static void sv_makevarspec()
1124
{
1125
    static char *magic = "blK:vars";
4191 helixhorne 1126
    int32_t i, j, numsavedvars=0, numsavedarrays=0, per;
1595 helixhorne 1127
 
1128
    for (i=0; i<g_gameVarCount; i++)
1129
        numsavedvars += (aGameVars[i].dwFlags&SV_SKIPMASK) ? 0 : 1;
1130
 
4191 helixhorne 1131
    for (i=0; i<g_gameArrayCount; i++)
1132
        numsavedarrays += !(aGameArrays[i].dwFlags & GAMEARRAY_READONLY);  // SYSTEM_GAMEARRAY
1133
 
3808 helixhorne 1134
    Bfree(svgm_vars);
4491 helixhorne 1135
    svgm_vars = (dataspec_t *)Xmalloc((numsavedvars+numsavedarrays+2)*sizeof(dataspec_t));
1595 helixhorne 1136
 
1137
    svgm_vars[0].flags = DS_STRING;
1138
    svgm_vars[0].ptr = magic;
1139
    svgm_vars[0].cnt = 1;
1140
 
1141
    j=1;
1142
    for (i=0; i<g_gameVarCount; i++)
1143
    {
1144
        if (aGameVars[i].dwFlags&SV_SKIPMASK)
1145
            continue;
1146
 
1147
        per = aGameVars[i].dwFlags&GAMEVAR_USER_MASK;
1148
 
1149
        svgm_vars[j].flags = 0;
1150
        svgm_vars[j].ptr = (per==0) ? &aGameVars[i].val.lValue : aGameVars[i].val.plValues;
1151
        svgm_vars[j].size = sizeof(intptr_t);
1152
        svgm_vars[j].cnt = (per==0) ? 1 : (per==GAMEVAR_PERPLAYER ? MAXPLAYERS : MAXSPRITES);
1153
        j++;
1154
    }
1155
 
1156
    for (i=0; i<g_gameArrayCount; i++)
1157
    {
4191 helixhorne 1158
        // We must not update read-only SYSTEM_GAMEARRAY gamearrays: besides
1159
        // being questionable by itself, sizeof(...) may be e.g. 4 whereas the
1160
        // actual element type is int16_t (such as tilesizx[]/tilesizy[]).
1161
        if (aGameArrays[i].dwFlags & GAMEARRAY_READONLY)
1162
            continue;
1163
 
1595 helixhorne 1164
        svgm_vars[j].flags = 0;
1165
        svgm_vars[j].ptr = aGameArrays[i].plValues;
1166
        svgm_vars[j].size = sizeof(aGameArrays[0].plValues[0]);
1167
        svgm_vars[j].cnt = aGameArrays[i].size;  // assumed constant throughout demo, i.e. no RESIZEARRAY
1168
        j++;
1169
    }
1170
 
1171
    svgm_vars[j].flags = DS_END;
4191 helixhorne 1172
    svgm_vars[j].ptr = NULL;
1173
    svgm_vars[j].size = svgm_vars[j].cnt = 0;
3789 helixhorne 1174
}
3415 helixhorne 1175
#endif
1595 helixhorne 1176
 
1177
void sv_freemem()
1178
{
1179
    if (svsnapshot)
1180
        Bfree(svsnapshot), svsnapshot=NULL;
1181
    if (svinitsnap)
1182
        Bfree(svinitsnap), svinitsnap=NULL;
1183
    if (svdiff)
1184
        Bfree(svdiff), svdiff=NULL;
1185
}
1186
 
4491 helixhorne 1187
static void SV_AllocSnap(int32_t allocinit)
1595 helixhorne 1188
{
1189
    sv_freemem();
1190
 
4491 helixhorne 1191
    svsnapshot = (uint8_t *)Xmalloc(svsnapsiz);
1595 helixhorne 1192
    if (allocinit)
4491 helixhorne 1193
        svinitsnap = (uint8_t *)Xmalloc(svsnapsiz);
1595 helixhorne 1194
    svdiffsiz = svsnapsiz;  // theoretically it's less than could be needed in the worst case, but practically it's overkill
4491 helixhorne 1195
    svdiff = (uint8_t *)Xmalloc(svdiffsiz);
1595 helixhorne 1196
}
1197
 
2207 helixhorne 1198
 
3644 helixhorne 1199
EDUKE32_STATIC_ASSERT(sizeof(savehead_t) == 310);
1200
 
2207 helixhorne 1201
// make snapshot only if spot < 0 (demo)
1202
int32_t sv_saveandmakesnapshot(FILE *fil, int8_t spot, int8_t recdiffsp, int8_t diffcompress, int8_t synccompress)
1595 helixhorne 1203
{
2207 helixhorne 1204
    savehead_t h;
1595 helixhorne 1205
 
2207 helixhorne 1206
    // set a few savegame system globals
1595 helixhorne 1207
    savegame_comprthres = SV_DEFAULTCOMPRTHRES;
2207 helixhorne 1208
    savegame_diffcompress = diffcompress;
1595 helixhorne 1209
 
2207 helixhorne 1210
    // calculate total snapshot size
3789 helixhorne 1211
#if !defined LUNATIC
1595 helixhorne 1212
    sv_makevarspec();
1213
    svsnapsiz = calcsz(svgm_vars);
3789 helixhorne 1214
#else
1215
    svsnapsiz = 0;
1216
#endif
1595 helixhorne 1217
    svsnapsiz += calcsz(svgm_udnetw) + calcsz(svgm_secwsp) + calcsz(svgm_script) + calcsz(svgm_anmisc);
1218
 
1219
 
2207 helixhorne 1220
    // create header
1221
    Bmemcpy(h.headerstr, "EDuke32SAVE", 11);
1222
    h.majorver = SV_MAJOR_VER;
1223
    h.minorver = SV_MINOR_VER;
1224
    h.ptrsize = sizeof(intptr_t);
1225
    h.bytever = BYTEVERSION;
1226
 
1227
    h.comprthres = savegame_comprthres;
1228
    h.recdiffsp = recdiffsp;
1229
    h.diffcompress = savegame_diffcompress;
1230
    h.synccompress = synccompress;
1231
 
1232
    h.reccnt = 0;
1233
    h.snapsiz = svsnapsiz;
1234
 
1235
    // the following is kinda redundant, but we save it here to be able to quickly fetch
1236
    // it in a savegame header read
1237
    h.numplayers = ud.multimode;
1238
    h.volnum = ud.volume_number;
1239
    h.levnum = ud.level_number;
1240
    h.skill = ud.player_skill;
1241
 
4589 helixhorne 1242
    if (currentboardfilename[0] != 0 && ud.level_number == 7 && ud.volume_number == 0)
1243
    {
1244
        const uint32_t BSZ = sizeof(h.boardfn);
1245
        EDUKE32_STATIC_ASSERT(BSZ == sizeof(currentboardfilename));
1246
 
1247
        Bstrncpy(h.boardfn, currentboardfilename, BSZ);
1248
 
1249
        // Shoehorn currently playing music into last bytes of board name buffer.
1250
        // SAVEGAME_MUSIC.
1251
        if (g_musicIndex != (0*MAXLEVELS+7) && Bstrlen(h.boardfn) < BSZ-2)
1252
        {
1253
            h.boardfn[BSZ-2] = g_musicIndex / MAXLEVELS;
1254
            h.boardfn[BSZ-1] = g_musicIndex % MAXLEVELS;
1255
        }
1256
    }
1257
 
4472 hendricks2 1258
    if ((unsigned)spot < MAXSAVEGAMES)
1595 helixhorne 1259
    {
2207 helixhorne 1260
        // savegame
2559 helixhorne 1261
        Bstrncpyz(h.savename, ud.savegame[spot], sizeof(h.savename));
1595 helixhorne 1262
    }
2207 helixhorne 1263
    else
1264
    {
1265
        // demo
1595 helixhorne 1266
 
2207 helixhorne 1267
        const time_t t=time(NULL);
1268
        struct tm *st;
1595 helixhorne 1269
 
4133 helixhorne 1270
        Bstrncpyz(h.savename, "EDuke32 demo", sizeof(h.savename));
2207 helixhorne 1271
        if (t>=0 && (st = localtime(&t)))
4133 helixhorne 1272
            Bsnprintf(h.savename, sizeof(h.savename), "Demo %04d%02d%02d %s",
1273
                      st->tm_year+1900, st->tm_mon+1, st->tm_mday, s_buildRev);
2207 helixhorne 1274
    }
1275
 
1276
 
1277
    // write header
1278
    fwrite(&h, sizeof(savehead_t), 1, fil);
1279
 
1280
    // for savegames, the file offset after the screenshot goes here;
1281
    // for demos, we keep it 0 to signify that we didn't save one
1282
    fwrite("\0\0\0\0", 4, 1, fil);
1283
    if (spot >= 0 && waloff[TILE_SAVESHOT])
1284
    {
1285
        int32_t ofs;
1286
 
1287
        // write the screenshot compressed
1288
        dfwrite((char *)waloff[TILE_SAVESHOT], 320, 200, fil);
1289
 
1290
        // write the current file offset right after the header
1291
        ofs = ftell(fil);
1292
        fseek(fil, sizeof(savehead_t), SEEK_SET);
1293
        fwrite(&ofs, 4, 1, fil);
1294
        fseek(fil, ofs, SEEK_SET);
1295
    }
1296
 
1297
#ifdef DEBUGGINGAIDS
1298
    OSD_Printf("sv_saveandmakesnapshot: snapshot size: %d bytes.\n", svsnapsiz);
1299
#endif
1300
 
1301
    if (spot >= 0)
1302
    {
1303
        // savegame
1304
        dosaveplayer2(fil, NULL);
3796 helixhorne 1305
#ifdef LUNATIC
1306
        if (!g_savedOK)
1307
        {
3914 helixhorne 1308
            OSD_Printf("sv_saveandmakesnapshot: failed serializing Lunatic gamevar \"%s\".\n",
3796 helixhorne 1309
                       g_failedVarname);
1310
            g_failedVarname = NULL;
1311
            return 1;
1312
        }
1313
#endif
2207 helixhorne 1314
    }
1315
    else
1316
    {
1317
        uint8_t *p;
1318
 
1319
        // demo
4491 helixhorne 1320
        SV_AllocSnap(0);
2207 helixhorne 1321
 
1322
        p = dosaveplayer2(fil, svsnapshot);
1323
        if (p != svsnapshot+svsnapsiz)
1324
        {
1325
            OSD_Printf("sv_saveandmakesnapshot: ptr-(snapshot end)=%d!\n", (int32_t)(p-(svsnapshot+svsnapsiz)));
1326
            return 1;
1327
        }
1328
    }
1329
 
3156 helixhorne 1330
    g_oldverSavegame[spot] = 0;
1331
 
1595 helixhorne 1332
    return 0;
1333
}
1334
 
3644 helixhorne 1335
 
1336
EDUKE32_STATIC_ASSERT(sizeof(savehead_t) == 310);
1337
 
2207 helixhorne 1338
// if file is not an EDuke32 savegame/demo, h->headerstr will be all zeros
1339
int32_t sv_loadheader(int32_t fil, int32_t spot, savehead_t *h)
1595 helixhorne 1340
{
2207 helixhorne 1341
    int32_t havedemo = (spot < 0);
1595 helixhorne 1342
 
2207 helixhorne 1343
    if (kread(fil, h, sizeof(savehead_t)) != sizeof(savehead_t))
1595 helixhorne 1344
    {
2207 helixhorne 1345
        OSD_Printf("%s %d header corrupt.\n", havedemo ? "Demo":"Savegame", havedemo ? -spot : spot);
1346
        Bmemset(h->headerstr, 0, sizeof(h->headerstr));
1347
        return -1;
1348
    }
1349
 
1350
    if (Bmemcmp(h->headerstr, "EDuke32SAVE", 11))
1351
    {
1352
        h->headerstr[sizeof(h->headerstr)-1] = 0;
1353
        OSD_Printf("%s %d header reads \"%s\", expected \"EDuke32SAVE\".\n",
1354
                   havedemo ? "Demo":"Savegame", havedemo ? -spot : spot, h->headerstr);
1355
        Bmemset(h->headerstr, 0, sizeof(h->headerstr));
1595 helixhorne 1356
        return 1;
1357
    }
1358
 
2207 helixhorne 1359
    if (h->majorver != SV_MAJOR_VER || h->minorver != SV_MINOR_VER || h->bytever != BYTEVERSION)
1595 helixhorne 1360
    {
2207 helixhorne 1361
        if (havedemo)
1362
            OSD_Printf("Incompatible demo version. Expected %d.%d.%d, found %d.%d.%d\n",
1363
                       SV_MAJOR_VER, SV_MINOR_VER, BYTEVERSION,
1364
                       h->majorver, h->minorver, h->bytever);
1595 helixhorne 1365
        return 2;
1366
    }
2207 helixhorne 1367
 
1368
    if (h->ptrsize != sizeof(intptr_t))
1595 helixhorne 1369
    {
2207 helixhorne 1370
        if (havedemo)
1371
            OSD_Printf("Demo incompatible. Expected pointer size %d, found %d\n",
1372
                       (int32_t)sizeof(intptr_t), h->ptrsize);
1595 helixhorne 1373
        return 3;
1374
    }
1375
 
2207 helixhorne 1376
    return 0;
1377
}
1595 helixhorne 1378
 
2207 helixhorne 1379
int32_t sv_loadsnapshot(int32_t fil, int32_t spot, savehead_t *h)
1380
{
1381
    uint8_t *p;
1382
    int32_t i;
1595 helixhorne 1383
 
2207 helixhorne 1384
    if (spot < 0)
1385
    {
1386
        // demo
1387
        i = sv_loadheader(fil, spot, h);
1388
        if (i)
1389
            return i;
1595 helixhorne 1390
 
2207 helixhorne 1391
        // Used to be in doloadplayer2(), now redundant for savegames since
1392
        // we checked before. Multiplayer demos need still to be taken care of.
1393
        if (h->numplayers != numplayers)
1394
            return 9;
1395
    }
1396
    // else (if savegame), we just read the header and are now at offset sizeof(savehead_t)
1595 helixhorne 1397
 
2207 helixhorne 1398
#ifdef DEBUGGINGAIDS
1399
    OSD_Printf("sv_loadsnapshot: snapshot size: %d bytes.\n", h->snapsiz);
1400
#endif
1401
 
1402
    if (kread(fil, &i, 4) != 4)
1595 helixhorne 1403
    {
2207 helixhorne 1404
        OSD_Printf("sv_snapshot: couldn't read 4 bytes after header.\n");
1405
        return 7;
1595 helixhorne 1406
    }
2207 helixhorne 1407
    if (i > 0)
1408
    {
1409
        if (klseek(fil, i, SEEK_SET) != i)
1410
        {
1411
            OSD_Printf("sv_snapshot: failed skipping over the screenshot.\n");
1412
            return 8;
1413
        }
1414
    }
1595 helixhorne 1415
 
2212 helixhorne 1416
    savegame_comprthres = h->comprthres;
1417
 
2207 helixhorne 1418
    if (spot >= 0)
1595 helixhorne 1419
    {
2207 helixhorne 1420
        // savegame
1421
        i = doloadplayer2(fil, NULL);
1422
        if (i)
1423
        {
1424
            OSD_Printf("sv_loadsnapshot: doloadplayer2() returned %d.\n", i);
1425
            return 5;
1426
        }
1595 helixhorne 1427
    }
2207 helixhorne 1428
    else
1429
    {
1430
        // demo
1431
        savegame_diffcompress = h->diffcompress;
1595 helixhorne 1432
 
2207 helixhorne 1433
        svsnapsiz = h->snapsiz;
1595 helixhorne 1434
 
4491 helixhorne 1435
        SV_AllocSnap(1);
1595 helixhorne 1436
 
2207 helixhorne 1437
        p = svsnapshot;
1438
        i = doloadplayer2(fil, &p);
1439
        if (i)
1440
        {
1441
            OSD_Printf("sv_loadsnapshot: doloadplayer2() returned %d.\n", i);
1442
            sv_freemem();
1443
            return 5;
1444
        }
1445
 
1446
        if (p != svsnapshot+svsnapsiz)
1447
        {
1448
            OSD_Printf("sv_loadsnapshot: internal error: p-(snapshot end)=%d!\n",
1449
                       (int32_t)(p-(svsnapshot+svsnapsiz)));
1450
            sv_freemem();
1451
            return 6;
1452
        }
1453
 
1454
        Bmemcpy(svinitsnap, svsnapshot, svsnapsiz);
1595 helixhorne 1455
    }
1456
 
2207 helixhorne 1457
    postloadplayer((spot >= 0));
1458
 
1595 helixhorne 1459
    return 0;
1460
}
1461
 
2207 helixhorne 1462
 
1595 helixhorne 1463
uint32_t sv_writediff(FILE *fil)
1464
{
1465
    uint8_t *p=svsnapshot, *d=svdiff;
1466
    uint32_t diffsiz;
1467
 
1468
    cmpspecdata(svgm_udnetw, &p, &d);
1469
    cmpspecdata(svgm_secwsp, &p, &d);
1470
    cmpspecdata(svgm_script, &p, &d);
1471
    cmpspecdata(svgm_anmisc, &p, &d);
3789 helixhorne 1472
#if !defined LUNATIC
1595 helixhorne 1473
    cmpspecdata(svgm_vars, &p, &d);
3789 helixhorne 1474
#endif
1595 helixhorne 1475
 
1476
    if (p != svsnapshot+svsnapsiz)
1477
        OSD_Printf("sv_writediff: dump+siz=%p, p=%p!\n", svsnapshot+svsnapsiz, p);
1478
 
1479
    diffsiz = d-svdiff;
1480
 
1481
    fwrite("dIfF",4,1,fil);
1482
    fwrite(&diffsiz, sizeof(diffsiz), 1, fil);
1483
    if (savegame_diffcompress)
1484
        dfwrite(svdiff, 1, diffsiz, fil);  // cnt and sz swapped
1485
    else
1486
        fwrite(svdiff, 1, diffsiz, fil);
1487
 
1488
    return diffsiz;
1489
}
1490
 
1491
int32_t sv_readdiff(int32_t fil)
1492
{
1493
    uint8_t *p=svsnapshot, *d=svdiff, i=0; //, tbuf[4];
1599 terminx 1494
    int32_t diffsiz;
1595 helixhorne 1495
 
1496
#if 0  // handled by the caller
1599 terminx 1497
    if (kread(fil, tbuf, 4)!=4)
1498
        return -1;
1499
    if (Bmemcmp(tbuf, "dIfF", 4))
1500
        return 4;
1595 helixhorne 1501
#endif
1599 terminx 1502
    if (kread(fil, &diffsiz, sizeof(uint32_t))!=sizeof(uint32_t))
1503
        return -1;
1595 helixhorne 1504
    if (savegame_diffcompress)
1505
    {
1506
        if (kdfread(svdiff, 1, diffsiz, fil) != diffsiz)  // cnt and sz swapped
1507
            return -2;
1508
    }
1509
    else
1510
    {
1511
        if (kread(fil, svdiff, diffsiz) != diffsiz)
1512
            return -2;
1513
    }
1514
 
1599 terminx 1515
    if (applydiff(svgm_udnetw, &p, &d)) return -3;
1516
    if (applydiff(svgm_secwsp, &p, &d)) return -4;
1517
    if (applydiff(svgm_script, &p, &d)) return -5;
1518
    if (applydiff(svgm_anmisc, &p, &d)) return -6;
3789 helixhorne 1519
#if !defined LUNATIC
1599 terminx 1520
    if (applydiff(svgm_vars, &p, &d)) return -7;
3789 helixhorne 1521
#endif
1595 helixhorne 1522
 
1599 terminx 1523
    if (p!=svsnapshot+svsnapsiz)
1524
        i|=1;
1525
    if (d!=svdiff+diffsiz)
1526
        i|=2;
1527
    if (i)
1528
        OSD_Printf("sv_readdiff: p=%p, svsnapshot+svsnapsiz=%p; d=%p, svdiff+diffsiz=%p",
1529
                   p, svsnapshot+svsnapsiz, d, svdiff+diffsiz);
1530
    return i;
1595 helixhorne 1531
}
1532
 
1533
// SVGM data description
1534
static void sv_postudload()
1535
{
2207 helixhorne 1536
//    Bmemcpy(&boardfilename[0], &currentboardfilename[0], BMAX_PATH);  // DON'T do this in demos!
1537
#if 1
1595 helixhorne 1538
    ud.m_level_number = ud.level_number;
1539
    ud.m_volume_number = ud.volume_number;
1540
    ud.m_player_skill = ud.player_skill;
1541
    ud.m_respawn_monsters = ud.respawn_monsters;
1542
    ud.m_respawn_items = ud.respawn_items;
1543
    ud.m_respawn_inventory = ud.respawn_inventory;
1544
    ud.m_monsters_off = ud.monsters_off;
1545
    ud.m_coop = ud.coop;
1546
    ud.m_marker = ud.marker;
1547
    ud.m_ffire = ud.ffire;
1548
    ud.m_noexits = ud.noexits;
1598 helixhorne 1549
#endif
1595 helixhorne 1550
}
1551
//static int32_t lockclock_dummy;
1552
 
1820 terminx 1553
#ifdef USE_OPENGL
1595 helixhorne 1554
static void sv_prespriteextsave()
1555
{
1556
    int32_t i;
1557
    for (i=0; i<MAXSPRITES; i++)
1558
        if (spriteext[i].mdanimtims)
1559
        {
1560
            spriteext[i].mdanimtims -= mdtims;
1561
            if (spriteext[i].mdanimtims==0)
1562
                spriteext[i].mdanimtims++;
1563
        }
1564
}
1565
static void sv_postspriteext()
1566
{
1567
    int32_t i;
1568
    for (i=0; i<MAXSPRITES; i++)
1569
        if (spriteext[i].mdanimtims)
1570
            spriteext[i].mdanimtims += mdtims;
1571
}
1572
#endif
1573
 
1882 helixhorne 1574
#ifdef YAX_ENABLE
2449 helixhorne 1575
void sv_postyaxload(void)
1882 helixhorne 1576
{
1577
    yax_update(numyaxbunches>0 ? 2 : 1);
1578
}
1579
#endif
1580
 
3456 helixhorne 1581
#if !defined LUNATIC
1595 helixhorne 1582
static void sv_calcbitptrsize()
1583
{
1584
    savegame_bitptrsize = (g_scriptSize+7)>>3;
1585
}
1586
static void sv_prescriptsave_once()
1587
{
1588
    int32_t i;
1589
    for (i=0; i<g_scriptSize; i++)
1590
        if (bitptr[i>>3]&(BITPTR_POINTER<<(i&7)))
2200 helixhorne 1591
            script[i] = (intptr_t *)script[i] - script;
1950 helixhorne 1592
 
3102 terminx 1593
   G_Util_PtrToIdx2(&g_tile[0].execPtr, MAXTILES, sizeof(tiledata_t), script, P2I_FWD_NON0);
1594
   G_Util_PtrToIdx2(&g_tile[0].loadPtr, MAXTILES, sizeof(tiledata_t), script, P2I_FWD_NON0);
1595 helixhorne 1595
}
1598 helixhorne 1596
static void sv_prescriptload_once()
1597
{
1598
    if (script)
1599
        Bfree(script);
4491 helixhorne 1600
    script = (intptr_t *)Xmalloc(g_scriptSize * sizeof(script[0]));
1598 helixhorne 1601
}
1595 helixhorne 1602
static void sv_postscript_once()
1603
{
1604
    int32_t i;
1950 helixhorne 1605
 
3102 terminx 1606
   G_Util_PtrToIdx2(&g_tile[0].execPtr, MAXTILES, sizeof(tiledata_t), script, P2I_BACK_NON0);
1607
   G_Util_PtrToIdx2(&g_tile[0].loadPtr, MAXTILES, sizeof(tiledata_t), script, P2I_BACK_NON0);
1950 helixhorne 1608
 
1595 helixhorne 1609
    for (i=0; i<g_scriptSize; i++)
1610
        if (bitptr[i>>3]&(BITPTR_POINTER<<(i&7)))
2200 helixhorne 1611
            script[i] = (intptr_t)(script + script[i]);
1595 helixhorne 1612
}
3456 helixhorne 1613
#endif
2185 helixhorne 1614
 
1595 helixhorne 1615
static void sv_preactordatasave()
1616
{
1617
    int32_t i;
2451 helixhorne 1618
 
1595 helixhorne 1619
    for (i=0; i<MAXSPRITES; i++)
1620
    {
1910 helixhorne 1621
        actor[i].lightptr = NULL;
1622
        actor[i].lightId = -1;
1595 helixhorne 1623
    }
1624
}
2185 helixhorne 1625
 
1595 helixhorne 1626
static void sv_postactordata()
1627
{
1628
    int32_t i;
1629
 
1630
    for (i=0; i<MAXSPRITES; i++)
1631
    {
1910 helixhorne 1632
        actor[i].lightptr = NULL;
1633
        actor[i].lightId = -1;
1595 helixhorne 1634
    }
1635
}
1636
 
1637
static void sv_preanimateptrsave()
1638
{
1950 helixhorne 1639
    G_Util_PtrToIdx(animateptr, g_animateCount, sector, P2I_FWD);
1595 helixhorne 1640
}
1641
static void sv_postanimateptr()
1642
{
1950 helixhorne 1643
    G_Util_PtrToIdx(animateptr, g_animateCount, sector, P2I_BACK);
1595 helixhorne 1644
}
1645
static void sv_prequote()
1646
{
1647
    if (!savegame_quotes)
4530 terminx 1648
    {
1649
        void *ptr = Xcalloc(MAXQUOTES, MAXQUOTELEN);
1650
        savegame_quotes = (char(*)[MAXQUOTELEN])ptr;
1651
    }
1595 helixhorne 1652
}
1653
static void sv_quotesave()
1654
{
1655
    int32_t i;
1656
    Bmemset(savegame_quotedef, 0, sizeof(savegame_quotedef));
1657
    for (i=0; i<MAXQUOTES; i++)
1658
        if (ScriptQuotes[i])
1659
        {
1660
            savegame_quotedef[i>>3] |= 1<<(i&7);
1661
            Bmemcpy(savegame_quotes[i], ScriptQuotes[i], MAXQUOTELEN);
1662
        }
1663
}
1664
static void sv_quoteload()
1665
{
1666
    int32_t i;
1667
    for (i=0; i<MAXQUOTES; i++)
1668
    {
1669
        if (savegame_quotedef[i>>3]&(1<<(i&7)))
1670
        {
3356 helixhorne 1671
            C_AllocQuote(i);
1595 helixhorne 1672
            Bmemcpy(ScriptQuotes[i], savegame_quotes[i], MAXQUOTELEN);
1673
        }
1674
    }
1675
}
1676
static void sv_prequoteredef()
1677
{
1678
    // "+1" needed for dfwrite which doesn't handle the src==NULL && cnt==0 case
4530 terminx 1679
    void *ptr = Xcalloc(g_numQuoteRedefinitions+1, MAXQUOTELEN);
1680
    savegame_quoteredefs = (char(*)[MAXQUOTELEN])ptr;
1595 helixhorne 1681
}
1682
static void sv_quoteredefsave()
1683
{
1684
    int32_t i;
1685
    for (i=0; i<g_numQuoteRedefinitions; i++)
1686
        if (ScriptQuoteRedefinitions[i])
1687
            Bmemcpy(savegame_quoteredefs[i], ScriptQuoteRedefinitions[i], MAXQUOTELEN);
1688
}
1689
static void sv_quoteredefload()
1690
{
1691
    int32_t i;
1692
    for (i=0; i<g_numQuoteRedefinitions; i++)
1693
    {
1694
        if (!ScriptQuoteRedefinitions[i])
4491 helixhorne 1695
            ScriptQuoteRedefinitions[i] = (char *)Xcalloc(1,MAXQUOTELEN);
1595 helixhorne 1696
        Bmemcpy(ScriptQuoteRedefinitions[i], savegame_quoteredefs[i], MAXQUOTELEN);
1697
    }
1698
}
1699
static void sv_postquoteredef()
1700
{
1701
    Bfree(savegame_quoteredefs), savegame_quoteredefs=NULL;
1702
}
1703
static void sv_restsave()
1704
{
1705
    int32_t i;
1706
    uint8_t *mem = savegame_restdata;
1707
    DukePlayer_t dummy_ps;
1708
 
1709
    Bmemset(&dummy_ps, 0, sizeof(DukePlayer_t));
1710
 
1599 terminx 1711
#define CPDAT(ptr,sz) Bmemcpy(mem, ptr, sz), mem+=sz
1595 helixhorne 1712
    for (i=0; i<MAXPLAYERS; i++)
1713
    {
1714
        CPDAT(g_player[i].user_name, 32);
1715
        CPDAT(&g_player[i].pcolor, sizeof(g_player[0].pcolor));
1716
        CPDAT(&g_player[i].pteam, sizeof(g_player[0].pteam));
1717
        CPDAT(&g_player[i].frags[0], sizeof(g_player[0].frags));
1718
        if (g_player[i].ps)
1719
            CPDAT(g_player[i].ps, sizeof(DukePlayer_t));
1720
        else
1721
            CPDAT(&dummy_ps, sizeof(DukePlayer_t));
1722
    }
2207 helixhorne 1723
 
3257 helixhorne 1724
    Bassert((savegame_restdata+SVARDATALEN)-mem == 0);
1599 terminx 1725
#undef CPDAT
1595 helixhorne 1726
}
1727
static void sv_restload()
1728
{
1729
    int32_t i;
1730
    uint8_t *mem = savegame_restdata;
1731
    DukePlayer_t dummy_ps;
1732
 
1599 terminx 1733
#define CPDAT(ptr,sz) Bmemcpy(ptr, mem, sz), mem+=sz
1595 helixhorne 1734
    for (i=0; i<MAXPLAYERS; i++)
1735
    {
1736
        CPDAT(g_player[i].user_name, 32);
1737
        CPDAT(&g_player[i].pcolor, sizeof(g_player[0].pcolor));
1738
        CPDAT(&g_player[i].pteam, sizeof(g_player[0].pteam));
1739
        CPDAT(&g_player[i].frags[0], sizeof(g_player[0].frags));
1740
        if (g_player[i].ps)
1741
            CPDAT(g_player[i].ps, sizeof(DukePlayer_t));
1742
        else
1743
            CPDAT(&dummy_ps, sizeof(DukePlayer_t));
1744
    }
1599 terminx 1745
#undef CPDAT
1595 helixhorne 1746
}
1747
 
2200 helixhorne 1748
#ifdef DEBUGGINGAIDS
2218 helixhorne 1749
# define PRINTSIZE(name) do { if (mem) OSD_Printf(name ": %d\n", (int32_t)(mem-tmem)); \
1750
        OSD_Printf(name ": %d ms\n", getticks()-t); t=getticks(); tmem=mem; } while (0)
2200 helixhorne 1751
#else
2218 helixhorne 1752
# define PRINTSIZE(name) do { } while (0)
2200 helixhorne 1753
#endif
1595 helixhorne 1754
 
3796 helixhorne 1755
#ifdef LUNATIC
4119 helixhorne 1756
// <levelnum>: if we're not serializing for a mapstate, -1
1757
//  otherwise, the linearized level number
1758
LUNATIC_CB const char *(*El_SerializeGamevars)(int32_t *slenptr, int32_t levelnum);
3796 helixhorne 1759
#endif
1760
 
2207 helixhorne 1761
static uint8_t *dosaveplayer2(FILE *fil, uint8_t *mem)
1595 helixhorne 1762
{
2218 helixhorne 1763
#ifdef DEBUGGINGAIDS
1595 helixhorne 1764
    uint8_t *tmem = mem;
2218 helixhorne 1765
    int32_t t=getticks();
1766
#endif
1595 helixhorne 1767
    mem=writespecdata(svgm_udnetw, fil, mem);  // user settings, players & net
2200 helixhorne 1768
    PRINTSIZE("ud");
1595 helixhorne 1769
    mem=writespecdata(svgm_secwsp, fil, mem);  // sector, wall, sprite
2200 helixhorne 1770
    PRINTSIZE("sws");
3891 helixhorne 1771
#ifdef LUNATIC
3796 helixhorne 1772
    {
3891 helixhorne 1773
        // Serialize Lunatic gamevars. When loading, the restoration code must
1774
        // be present before Lua state creation in svgm_script, so save it
1775
        // right before, too.
3796 helixhorne 1776
        int32_t slen, slen_ext;
4119 helixhorne 1777
        const char *svcode = El_SerializeGamevars(&slen, -1);
3796 helixhorne 1778
 
1779
        if (slen < 0)
1780
        {
1781
            // Serialization failed.
1782
            g_savedOK = 0;
1783
            g_failedVarname = svcode;
1784
            return mem;
1785
        }
1786
 
1787
        fwrite("\0\1LunaGVAR\3\4", 12, 1, fil);
1788
        slen_ext = B_LITTLE32(slen);
1789
        fwrite(&slen_ext, sizeof(slen_ext), 1, fil);
3891 helixhorne 1790
        dfwrite(svcode, 1, slen, fil);  // cnt and sz swapped
3796 helixhorne 1791
 
1792
        g_savedOK = 1;
1793
    }
3789 helixhorne 1794
#endif
3891 helixhorne 1795
    mem=writespecdata(svgm_script, fil, mem);  // script
1796
    PRINTSIZE("script");
1797
    mem=writespecdata(svgm_anmisc, fil, mem);  // animates, quotes & misc.
1798
    PRINTSIZE("animisc");
1595 helixhorne 1799
 
3891 helixhorne 1800
#if !defined LUNATIC
1801
    Gv_WriteSave(fil, 1);  // gamevars
1802
    mem=writespecdata(svgm_vars, 0, mem);
1803
    PRINTSIZE("vars");
1804
#endif
1805
 
1595 helixhorne 1806
    return mem;
1807
}
1808
 
3891 helixhorne 1809
#ifdef LUNATIC
1810
char *g_elSavecode = NULL;
1595 helixhorne 1811
 
3891 helixhorne 1812
static int32_t El_ReadSaveCode(int32_t fil)
1813
{
1814
    // Read Lua code to restore gamevar values from the savegame.
1815
    // It will be run from Lua with its state creation later on.
1816
 
1817
    char header[12];
1818
    int32_t slen;
1819
 
1820
    if (kread(fil, header, 12) != 12)
1821
    {
1822
        OSD_Printf("doloadplayer2: failed reading Lunatic gamevar header.\n");
1823
        return -100;
1824
    }
1825
 
1826
    if (Bmemcmp(header, "\0\1LunaGVAR\3\4", 12))
1827
    {
1828
        OSD_Printf("doloadplayer2: Lunatic gamevar header doesn't match.\n");
1829
        return -101;
1830
    }
1831
 
1832
    if (kread(fil, &slen, sizeof(slen)) != sizeof(slen))
1833
    {
1834
        OSD_Printf("doloadplayer2: failed reading Lunatic gamevar string size.\n");
1835
        return -102;
1836
    }
1837
 
1838
    slen = B_LITTLE32(slen);
1839
    if (slen < 0)
1840
    {
1841
        OSD_Printf("doloadplayer2: invalid Lunatic gamevar string size %d.\n", slen);
1842
        return -103;
1843
    }
1844
 
1845
    if (slen > 0)
1846
    {
4491 helixhorne 1847
        char *svcode = (char *)Xmalloc(slen+1);
3891 helixhorne 1848
 
1849
        if (kdfread(svcode, 1, slen, fil) != slen)  // cnt and sz swapped
1850
        {
1851
            OSD_Printf("doloadplayer2: failed reading Lunatic gamevar restoration code.\n");
1852
            Bfree(svcode);
1853
            return -104;
1854
        }
1855
 
1856
        svcode[slen] = 0;
1857
        g_elSavecode = svcode;
1858
    }
1859
 
1860
    return 0;
1861
}
1862
 
1863
void El_FreeSaveCode(void)
1864
{
1865
    // Free Lunatic gamevar savegame restoration Lua code.
1866
    Bfree(g_elSavecode);
1867
    g_elSavecode = NULL;
1868
}
1869
#endif
1870
 
2207 helixhorne 1871
static int32_t doloadplayer2(int32_t fil, uint8_t **memptr)
1595 helixhorne 1872
{
2218 helixhorne 1873
    uint8_t *mem = memptr ? *memptr : NULL;
1874
#ifdef DEBUGGINGAIDS
1875
    uint8_t *tmem=mem;
1876
    int32_t t=getticks();
1877
#endif
2207 helixhorne 1878
    if (readspecdata(svgm_udnetw, fil, &mem)) return -2;
2200 helixhorne 1879
    PRINTSIZE("ud");
1595 helixhorne 1880
    if (readspecdata(svgm_secwsp, fil, &mem)) return -4;
2200 helixhorne 1881
    PRINTSIZE("sws");
3891 helixhorne 1882
#ifdef LUNATIC
1883
    {
1884
        int32_t ret = El_ReadSaveCode(fil);
1885
        if (ret < 0)
1886
            return ret;
1887
    }
1888
#endif
1595 helixhorne 1889
    if (readspecdata(svgm_script, fil, &mem)) return -5;
2200 helixhorne 1890
    PRINTSIZE("script");
1595 helixhorne 1891
    if (readspecdata(svgm_anmisc, fil, &mem)) return -6;
2200 helixhorne 1892
    PRINTSIZE("animisc");
1595 helixhorne 1893
 
3789 helixhorne 1894
#if !defined LUNATIC
1595 helixhorne 1895
    if (Gv_ReadSave(fil, 1)) return -7;
1896
 
2190 helixhorne 1897
    if (mem)
2200 helixhorne 1898
    {
2207 helixhorne 1899
        int32_t i;
1900
 
2200 helixhorne 1901
        sv_makevarspec();
2190 helixhorne 1902
        for (i=1; svgm_vars[i].flags!=DS_END; i++)
1903
        {
1904
            Bmemcpy(mem, svgm_vars[i].ptr, svgm_vars[i].size*svgm_vars[i].cnt);  // careful! works because there are no DS_DYNAMIC's!
1905
            mem += svgm_vars[i].size*svgm_vars[i].cnt;
1906
        }
2200 helixhorne 1907
    }
2218 helixhorne 1908
    PRINTSIZE("vars");
3789 helixhorne 1909
#endif
1595 helixhorne 1910
 
1911
    if (memptr)
1912
        *memptr = mem;
1913
    return 0;
1914
}
1915
 
1916
int32_t sv_updatestate(int32_t frominit)
1917
{
1918
    uint8_t *p = svsnapshot, *pbeg=p;
1919
 
1920
    if (frominit)
1921
        Bmemcpy(svsnapshot, svinitsnap, svsnapsiz);
1922
 
1923
    if (readspecdata(svgm_udnetw, -1, &p)) return -2;
1924
    if (readspecdata(svgm_secwsp, -1, &p)) return -4;
1925
    if (readspecdata(svgm_script, -1, &p)) return -5;
1926
    if (readspecdata(svgm_anmisc, -1, &p)) return -6;
1927
 
3789 helixhorne 1928
#if !defined LUNATIC
1595 helixhorne 1929
    if (readspecdata(svgm_vars, -1, &p)) return -8;
3789 helixhorne 1930
#endif
1595 helixhorne 1931
 
1932
    if (p != pbeg+svsnapsiz)
2200 helixhorne 1933
    {
1595 helixhorne 1934
        OSD_Printf("sv_updatestate: ptr-(snapshot end)=%d\n", (int32_t)(p-(pbeg+svsnapsiz)));
2200 helixhorne 1935
        return -9;
1936
    }
1595 helixhorne 1937
 
1938
    if (frominit)
2207 helixhorne 1939
        postloadplayer(0);
1940
#ifdef POLYMER
3346 terminx 1941
    if (getrendermode() == REND_POLYMER)
2207 helixhorne 1942
        polymer_resetlights();  // must do it after polymer_loadboard() !!!
1943
#endif
1595 helixhorne 1944
 
1945
    return 0;
1946
}
1947
 
2207 helixhorne 1948
static void postloadplayer(int32_t savegamep)
1595 helixhorne 1949
{
1950
    int32_t i;
2207 helixhorne 1951
 
1595 helixhorne 1952
    //1
1953
    if (g_player[myconnectindex].ps->over_shoulder_on != 0)
1954
    {
3405 helixhorne 1955
        CAMERADIST = 0;
1956
        CAMERACLOCK = 0;
1595 helixhorne 1957
        g_player[myconnectindex].ps->over_shoulder_on = 1;
1958
    }
1959
 
2207 helixhorne 1960
    //2
1595 helixhorne 1961
    screenpeek = myconnectindex;
2207 helixhorne 1962
 
1963
    //2.5
1964
    if (savegamep)
1965
    {
4589 helixhorne 1966
        int32_t musicIdx = (ud.volume_number*MAXLEVELS) + ud.level_number;
1967
 
2207 helixhorne 1968
        Bmemset(gotpic, 0, sizeof(gotpic));
1969
        S_ClearSoundLocks();
1970
        G_CacheMapData();
1971
 
1972
        if (boardfilename[0] != 0 && ud.level_number == 7 && ud.volume_number == 0)
1973
        {
4589 helixhorne 1974
            const uint32_t BSZ = sizeof(boardfilename);
2207 helixhorne 1975
            char levname[BMAX_PATH];
1976
 
1977
            G_SetupFilenameBasedMusic(levname, boardfilename, ud.level_number);
4589 helixhorne 1978
 
1979
            // Potentially extract the custom music volume/level from
1980
            // boardfilename[] stored with SAVEGAME_MUSIC.
1981
            if (Bstrlen(boardfilename) < BSZ-2)
1982
            {
1983
                int32_t mi = MAXLEVELS*boardfilename[BSZ-2] + boardfilename[BSZ-1];
1984
 
1985
                if (mi != 0 && (unsigned)mi < MUS_FIRST_SPECIAL)
1986
                    musicIdx = mi;
1987
            }
2207 helixhorne 1988
        }
1989
 
1990
        if (ud.config.MusicToggle)
1991
        {
4589 helixhorne 1992
            if (MapInfo[musicIdx].musicfn != NULL &&
1993
                (musicIdx != g_musicIndex /* || MapInfo[MUS_LOADING].musicfn */))
2207 helixhorne 1994
            {
1995
                S_StopMusic();
4589 helixhorne 1996
 
1997
                g_musicIndex = musicIdx;
4948 hendricks2 1998
                S_PlayMusic(MapInfo[g_musicIndex].musicfn);
2207 helixhorne 1999
            }
2000
 
2001
            S_PauseMusic(0);
2002
        }
2003
 
2004
        g_player[myconnectindex].ps->gm = MODE_GAME;
2005
        ud.recstat = 0;
2006
 
2007
        if (g_player[myconnectindex].ps->jetpack_on)
2008
            A_PlaySound(DUKE_JETPACK_IDLE, g_player[myconnectindex].ps->i);
2009
    }
2010
 
1595 helixhorne 2011
    //3
2012
    P_UpdateScreenPal(g_player[myconnectindex].ps);
2306 helixhorne 2013
    g_restorePalette = -1;
2207 helixhorne 2014
 
2015
    //3.5
2016
    if (savegamep)
1595 helixhorne 2017
    {
3156 helixhorne 2018
        for (SPRITES_OF(STAT_FX, i))
3083 terminx 2019
            if (sprite[i].picnum == MUSICANDSFX)
2020
            {
2021
                T2 = ud.config.SoundToggle;
2022
                T1 = 0;
2023
            }
2024
 
2207 helixhorne 2025
        G_UpdateScreenArea();
2026
        FX_SetReverb(0);
1595 helixhorne 2027
    }
2207 helixhorne 2028
 
2029
    //4
2030
    if (savegamep)
1595 helixhorne 2031
    {
2235 helixhorne 2032
        if (ud.lockout)
2207 helixhorne 2033
        {
2034
            for (i=0; i<g_numAnimWalls; i++)
2297 helixhorne 2035
                switch (DYNAMICTILEMAP(wall[animwall[i].wallnum].picnum))
2207 helixhorne 2036
                {
2037
                case FEMPIC1__STATIC:
2038
                    wall[animwall[i].wallnum].picnum = BLANKSCREEN;
2039
                    break;
2040
                case FEMPIC2__STATIC:
2041
                case FEMPIC3__STATIC:
2042
                    wall[animwall[i].wallnum].picnum = SCREENBREAK6;
2043
                    break;
2044
                }
2045
        }
2235 helixhorne 2046
#if 0
2047
        else
2048
        {
2049
            for (i=0; i<g_numAnimWalls; i++)
2050
                if (wall[animwall[i].wallnum].extra >= 0)
2051
                    wall[animwall[i].wallnum].picnum = wall[animwall[i].wallnum].extra;
2052
        }
2053
#endif
1595 helixhorne 2054
    }
2055
 
2056
    //5
1949 helixhorne 2057
    G_ResetInterpolations();
1595 helixhorne 2058
 
2059
    //6
2060
    g_showShareware = 0;
2207 helixhorne 2061
    if (savegamep)
2062
        everyothertime = 0;
2190 helixhorne 2063
 
2207 helixhorne 2064
    //7
1595 helixhorne 2065
    for (i=0; i<MAXPLAYERS; i++)
2190 helixhorne 2066
        g_player[i].playerquitflag = 1;
1595 helixhorne 2067
 
2207 helixhorne 2068
    // ----------
2069
 
2070
    //7.5
2071
    if (savegamep)
2072
    {
2073
        ready2send = 1;
2074
        G_ClearFIFO();
2075
        Net_WaitForServer();
2076
    }
2077
 
2078
    //8
2079
    // if (savegamep)  ?
3929 helixhorne 2080
#ifdef LUNATIC
2081
    G_ResetTimers(1);
2082
#else
2083
    G_ResetTimers(0);
2084
#endif
1595 helixhorne 2085
 
2086
#ifdef POLYMER
2207 helixhorne 2087
    //9
3346 terminx 2088
    if (getrendermode() == REND_POLYMER)
1596 plagman 2089
        polymer_loadboard();
1595 helixhorne 2090
#elif 0
3346 terminx 2091
    if (getrendermode() == REND_POLYMER)
1595 helixhorne 2092
    {
2093
        int32_t i = 0;
2094
 
2095
        polymer_loadboard();
2096
        while (i < MAXSPRITES)
2097
        {
1625 terminx 2098
            if (actor[i].lightptr)
1595 helixhorne 2099
            {
1625 terminx 2100
                polymer_deletelight(actor[i].lightId);
2101
                actor[i].lightptr = NULL;
2102
                actor[i].lightId = -1;
1595 helixhorne 2103
            }
2104
            i++;
2105
        }
2106
    }
2107
#endif
2207 helixhorne 2108
    // this light pointer nulling needs to be outside the getrendermode check
2109
    // because we might be loading the savegame using another renderer but
2110
    // change to Polymer later
2111
    for (i=0; i<MAXSPRITES; i++)
2112
    {
2113
        actor[i].lightptr = NULL;
2114
        actor[i].lightId = -1;
2115
    }
1595 helixhorne 2116
}
2117
 
2118
////////// END GENERIC SAVING/LOADING SYSTEM //////////