Subversion Repositories eduke32

Rev

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