Subversion Repositories eduke32

Rev

Rev 4990 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 4990 Rev 5069
1
//-------------------------------------------------------------------------
1
//-------------------------------------------------------------------------
2
/*
2
/*
3
Copyright (C) 2010 EDuke32 developers and contributors
3
Copyright (C) 2010 EDuke32 developers and contributors
4

4

5
This file is part of EDuke32.
5
This file is part of EDuke32.
6

6

7
EDuke32 is free software; you can redistribute it and/or
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
8
modify it under the terms of the GNU General Public License version 2
9
as published by the Free Software Foundation.
9
as published by the Free Software Foundation.
10

10

11
This program is distributed in the hope that it will be useful,
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14

14

15
See the GNU General Public License for more details.
15
See the GNU General Public License for more details.
16

16

17
You should have received a copy of the GNU General Public License
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
18
along with this program; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
*/
20
*/
21
//-------------------------------------------------------------------------
21
//-------------------------------------------------------------------------
22
22
23
#include "duke3d.h"
23
#include "duke3d.h"
24
#include "premap.h"
24
#include "premap.h"
25
#include "menus.h"  // menutext
25
#include "menus.h"  // menutext
26
#include "prlights.h"
26
#include "prlights.h"
27
#include "savegame.h"
27
#include "savegame.h"
28
#ifdef LUNATIC
28
#ifdef LUNATIC
29
# include "lunatic_game.h"
29
# include "lunatic_game.h"
30
static int32_t g_savedOK;
30
static int32_t g_savedOK;
31
const char *g_failedVarname;
31
const char *g_failedVarname;
32
#endif
32
#endif
33
33
34
extern char *bitptr;
34
extern char *bitptr;
35
35
36
uint8_t g_oldverSavegame[MAXSAVEGAMES];
36
uint8_t g_oldverSavegame[MAXSAVEGAMES];
37
37
38
#define BITPTR_POINTER 1
38
#define BITPTR_POINTER 1
39
39
40
// For storing pointers in files.
40
// For storing pointers in files.
41
//  back_p==0: ptr -> "small int"
41
//  back_p==0: ptr -> "small int"
42
//  back_p==1: "small int" -> ptr
42
//  back_p==1: "small int" -> ptr
43
//
43
//
44
//  mode: see enum in savegame.h
44
//  mode: see enum in savegame.h
45
void G_Util_PtrToIdx(void *ptr, int32_t count, const void *base, int32_t mode)
45
void G_Util_PtrToIdx(void *ptr, int32_t const count, const void *base, int32_t const mode)
46
{
46
{
47
    intptr_t *iptr = (intptr_t *)ptr;
47
    intptr_t *iptr = (intptr_t *)ptr;
48
    intptr_t const ibase = (intptr_t)base;
48
    intptr_t const ibase = (intptr_t)base;
49
    int32_t const onlynon0_p = mode&P2I_ONLYNON0_BIT;
49
    int32_t const onlynon0_p = mode&P2I_ONLYNON0_BIT;
50
50
51
    // TODO: convert to proper offsets/indices for (a step towards) cross-
51
    // TODO: convert to proper offsets/indices for (a step towards) cross-
52
    //       compatibility between 32- and 64-bit systems in the netplay.
52
    //       compatibility between 32- and 64-bit systems in the netplay.
53
    //       REMEMBER to bump BYTEVERSION then.
53
    //       REMEMBER to bump BYTEVERSION then.
54
54
55
    // WARNING: C std doesn't say that bit pattern of NULL is necessarily 0!
55
    // WARNING: C std doesn't say that bit pattern of NULL is necessarily 0!
56
    if ((mode & P2I_BACK_BIT) == 0)
56
    if ((mode & P2I_BACK_BIT) == 0)
57
    {
57
    {
58
        for (int i = 0; i < count; i++)
58
        for (int i = 0; i < count; i++)
59
            if (!onlynon0_p || iptr[i])
59
            if (!onlynon0_p || iptr[i])
60
                iptr[i] -= ibase;
60
                iptr[i] -= ibase;
61
    }
61
    }
62
    else
62
    else
63
    {
63
    {
64
        for (int i = 0; i < count; i++)
64
        for (int i = 0; i < count; i++)
65
            if (!onlynon0_p || iptr[i])
65
            if (!onlynon0_p || iptr[i])
66
                iptr[i] += ibase;
66
                iptr[i] += ibase;
67
    }
67
    }
68
}
68
}
69
69
70
void G_Util_PtrToIdx2(void *ptr, int32_t count, size_t stride, const void *base, int32_t mode)
70
void G_Util_PtrToIdx2(void *ptr, int32_t const count, size_t const stride, const void *base, int32_t const mode)
71
{
71
{
72
    uint8_t *iptr = (uint8_t *)ptr;
72
    uint8_t *iptr = (uint8_t *)ptr;
73
    intptr_t const ibase = (intptr_t)base;
73
    intptr_t const ibase = (intptr_t)base;
74
    int32_t const onlynon0_p = mode&P2I_ONLYNON0_BIT;
74
    int32_t const onlynon0_p = mode&P2I_ONLYNON0_BIT;
75
75
76
    if ((mode & P2I_BACK_BIT) == 0)
76
    if ((mode & P2I_BACK_BIT) == 0)
77
    {
77
    {
78
        for (int i = 0; i < count; ++i)
78
        for (int i = 0; i < count; ++i)
79
        {
79
        {
80
            if (!onlynon0_p || *(intptr_t *)iptr)
80
            if (!onlynon0_p || *(intptr_t *)iptr)
81
                *(intptr_t *)iptr -= ibase;
81
                *(intptr_t *)iptr -= ibase;
82
82
83
            iptr += stride;
83
            iptr += stride;
84
        }
84
        }
85
    }
85
    }
86
    else
86
    else
87
    {
87
    {
88
        for (int i = 0; i < count; ++i)
88
        for (int i = 0; i < count; ++i)
89
        {
89
        {
90
            if (!onlynon0_p || *(intptr_t *)iptr)
90
            if (!onlynon0_p || *(intptr_t *)iptr)
91
                *(intptr_t *)iptr += ibase;
91
                *(intptr_t *)iptr += ibase;
92
92
93
            iptr += stride;
93
            iptr += stride;
94
        }
94
        }
95
    }
95
    }
96
}
96
}
97
97
98
// TODO: sync with TROR special interpolations? (e.g. upper floor of subway)
98
// TODO: sync with TROR special interpolations? (e.g. upper floor of subway)
99
void G_ResetInterpolations(void)
99
void G_ResetInterpolations(void)
100
{
100
{
101
    int32_t k, i;
101
    int32_t k, i;
102
102
103
    g_numInterpolations = 0;
103
    g_numInterpolations = 0;
104
    startofdynamicinterpolations = 0;
104
    startofdynamicinterpolations = 0;
105
105
106
    k = headspritestat[STAT_EFFECTOR];
106
    k = headspritestat[STAT_EFFECTOR];
107
    while (k >= 0)
107
    while (k >= 0)
108
    {
108
    {
109
        switch (sprite[k].lotag)
109
        switch (sprite[k].lotag)
110
        {
110
        {
111
        case SE_31_FLOOR_RISE_FALL:
111
        case SE_31_FLOOR_RISE_FALL:
112
            G_SetInterpolation(&sector[sprite[k].sectnum].floorz);
112
            G_SetInterpolation(&sector[sprite[k].sectnum].floorz);
113
            break;
113
            break;
114
        case SE_32_CEILING_RISE_FALL:
114
        case SE_32_CEILING_RISE_FALL:
115
            G_SetInterpolation(&sector[sprite[k].sectnum].ceilingz);
115
            G_SetInterpolation(&sector[sprite[k].sectnum].ceilingz);
116
            break;
116
            break;
117
        case SE_17_WARP_ELEVATOR:
117
        case SE_17_WARP_ELEVATOR:
118
        case SE_25_PISTON:
118
        case SE_25_PISTON:
119
            G_SetInterpolation(&sector[sprite[k].sectnum].floorz);
119
            G_SetInterpolation(&sector[sprite[k].sectnum].floorz);
120
            G_SetInterpolation(&sector[sprite[k].sectnum].ceilingz);
120
            G_SetInterpolation(&sector[sprite[k].sectnum].ceilingz);
121
            break;
121
            break;
122
        case SE_0_ROTATING_SECTOR:
122
        case SE_0_ROTATING_SECTOR:
123
        case SE_5:
123
        case SE_5:
124
        case SE_6_SUBWAY:
124
        case SE_6_SUBWAY:
125
        case SE_11_SWINGING_DOOR:
125
        case SE_11_SWINGING_DOOR:
126
        case SE_14_SUBWAY_CAR:
126
        case SE_14_SUBWAY_CAR:
127
        case SE_15_SLIDING_DOOR:
127
        case SE_15_SLIDING_DOOR:
128
        case SE_16_REACTOR:
128
        case SE_16_REACTOR:
129
        case SE_26:
129
        case SE_26:
130
        case SE_30_TWO_WAY_TRAIN:
130
        case SE_30_TWO_WAY_TRAIN:
131
            Sect_SetInterpolation(sprite[k].sectnum);
131
            Sect_SetInterpolation(sprite[k].sectnum);
132
            break;
132
            break;
133
        }
133
        }
134
134
135
        k = nextspritestat[k];
135
        k = nextspritestat[k];
136
    }
136
    }
137
137
138
    for (i=g_numInterpolations-1; i>=0; i--) bakipos[i] = *curipos[i];
138
    for (i=g_numInterpolations-1; i>=0; i--) bakipos[i] = *curipos[i];
139
    for (i = g_animateCount-1; i>=0; i--)
139
    for (i = g_animateCount-1; i>=0; i--)
140
        G_SetInterpolation(animateptr[i]);
140
        G_SetInterpolation(animateptr[i]);
141
}
141
}
142
142
143
void ReadSaveGameHeaders(void)
143
void ReadSaveGameHeaders(void)
144
{
144
{
145
    char fn[16];
145
    char fn[16];
146
    int32_t fil, i;
146
    int32_t fil, i;
147
147
148
    savehead_t h;
148
    savehead_t h;
149
149
150
    EDUKE32_STATIC_ASSERT(sizeof(h.savename) == sizeof(ud.savegame[0]));
150
    EDUKE32_STATIC_ASSERT(sizeof(h.savename) == sizeof(ud.savegame[0]));
151
151
152
    Bstrcpy(fn, "dukesav0.esv");
152
    Bstrcpy(fn, "dukesav0.esv");
153
153
154
    for (i=0; i<MAXSAVEGAMES; i++)
154
    for (i=0; i<MAXSAVEGAMES; i++)
155
    {
155
    {
156
        int32_t k;
156
        int32_t k;
157
157
158
        fn[7] = i + '0';
158
        fn[7] = i + '0';
159
        fil = kopen4loadfrommod(fn, 0);
159
        fil = kopen4loadfrommod(fn, 0);
160
        if (fil == -1)
160
        if (fil == -1)
161
        {
161
        {
162
            Bmemset(ud.savegame[i], 0, sizeof(ud.savegame[i]));
162
            Bmemset(ud.savegame[i], 0, sizeof(ud.savegame[i]));
163
            continue;
163
            continue;
164
        }
164
        }
165
165
166
        k = sv_loadheader(fil, i, &h);
166
        k = sv_loadheader(fil, i, &h);
167
        if (k)
167
        if (k)
168
        {
168
        {
169
            // old version, signal to menu code
169
            // old version, signal to menu code
170
            if (k==2 || k==3)
170
            if (k==2 || k==3)
171
                g_oldverSavegame[i] = 1;
171
                g_oldverSavegame[i] = 1;
172
            // else h.savename is all zeros (fatal failure, like wrong header
172
            // else h.savename is all zeros (fatal failure, like wrong header
173
            // magic or too short header)
173
            // magic or too short header)
174
        }
174
        }
175
175
176
        Bmemcpy(ud.savegame[i], h.savename, sizeof(ud.savegame[i]));
176
        Bmemcpy(ud.savegame[i], h.savename, sizeof(ud.savegame[i]));
177
177
178
        kclose(fil);
178
        kclose(fil);
179
    }
179
    }
180
}
180
}
181
181
182
int32_t G_LoadSaveHeaderNew(int32_t spot, savehead_t *saveh)
182
int32_t G_LoadSaveHeaderNew(int32_t spot, savehead_t *saveh)
183
{
183
{
184
    char fn[16];
184
    char fn[16];
185
    int32_t fil, screenshotofs, i;
185
    int32_t fil, screenshotofs, i;
186
186
187
    Bassert(spot < MAXSAVEGAMES);
187
    Bassert(spot < MAXSAVEGAMES);
188
188
189
    Bstrcpy(fn, "dukesav0.esv");
189
    Bstrcpy(fn, "dukesav0.esv");
190
    fn[7] = spot + '0';
190
    fn[7] = spot + '0';
191
191
192
    fil = kopen4loadfrommod(fn, 0);
192
    fil = kopen4loadfrommod(fn, 0);
193
    if (fil == -1)
193
    if (fil == -1)
194
        return -1;
194
        return -1;
195
195
196
    i = sv_loadheader(fil, spot, saveh);
196
    i = sv_loadheader(fil, spot, saveh);
197
    if (i && (i != 2 && i != 3))
197
    if (i && (i != 2 && i != 3))
198
        goto corrupt;
198
        goto corrupt;
199
199
200
    if (kread(fil, &screenshotofs, 4) != 4)
200
    if (kread(fil, &screenshotofs, 4) != 4)
201
        goto corrupt;
201
        goto corrupt;
202
202
203
    walock[TILE_LOADSHOT] = 255;
203
    walock[TILE_LOADSHOT] = 255;
204
    if (waloff[TILE_LOADSHOT] == 0)
204
    if (waloff[TILE_LOADSHOT] == 0)
205
        allocache(&waloff[TILE_LOADSHOT], 320*200, &walock[TILE_LOADSHOT]);
205
        allocache(&waloff[TILE_LOADSHOT], 320*200, &walock[TILE_LOADSHOT]);
206
    tilesiz[TILE_LOADSHOT].x = 200;
206
    tilesiz[TILE_LOADSHOT].x = 200;
207
    tilesiz[TILE_LOADSHOT].y = 320;
207
    tilesiz[TILE_LOADSHOT].y = 320;
208
    if (screenshotofs)
208
    if (screenshotofs)
209
    {
209
    {
210
        if (kdfread((char *)waloff[TILE_LOADSHOT], 320, 200, fil) != 200)
210
        if (kdfread((char *)waloff[TILE_LOADSHOT], 320, 200, fil) != 200)
211
        {
211
        {
212
            OSD_Printf("G_LoadSaveHeaderNew(%d): failed reading screenshot\n", spot);
212
            OSD_Printf("G_LoadSaveHeaderNew(%d): failed reading screenshot\n", spot);
213
            goto corrupt;
213
            goto corrupt;
214
        }
214
        }
215
    }
215
    }
216
    else
216
    else
217
    {
217
    {
218
        Bmemset((char *)waloff[TILE_LOADSHOT], 0, 320*200);
218
        Bmemset((char *)waloff[TILE_LOADSHOT], 0, 320*200);
219
    }
219
    }
220
    invalidatetile(TILE_LOADSHOT, 0, 255);
220
    invalidatetile(TILE_LOADSHOT, 0, 255);
221
221
222
    kclose(fil);
222
    kclose(fil);
223
    return 0;
223
    return 0;
224
224
225
corrupt:
225
corrupt:
226
    kclose(fil);
226
    kclose(fil);
227
    return 1;
227
    return 1;
228
}
228
}
229
229
230
230
231
static void sv_postudload();
231
static void sv_postudload();
232
232
233
// XXX: keyboard input 'blocked' after load fail? (at least ESC?)
233
// XXX: keyboard input 'blocked' after load fail? (at least ESC?)
234
int32_t G_LoadPlayer(int32_t spot)
234
int32_t G_LoadPlayer(int32_t spot)
235
{
235
{
236
    char fn[16];
236
    char fn[16];
237
    int32_t fil, i;
237
    int32_t fil, i;
238
238
239
    savehead_t h;
239
    savehead_t h;
240
240
241
    Bassert(spot < MAXSAVEGAMES);
241
    Bassert(spot < MAXSAVEGAMES);
242
242
243
    Bstrcpy(fn, "dukesav0.esv");
243
    Bstrcpy(fn, "dukesav0.esv");
244
    fn[7] = spot + '0';
244
    fn[7] = spot + '0';
245
245
246
    fil = kopen4loadfrommod(fn, 0);
246
    fil = kopen4loadfrommod(fn, 0);
247
    if (fil == -1)
247
    if (fil == -1)
248
        return -1;
248
        return -1;
249
249
250
    ready2send = 0;
250
    ready2send = 0;
251
251
252
    i = sv_loadheader(fil, spot, &h);
252
    i = sv_loadheader(fil, spot, &h);
253
    if ((i && i != 2) || h.numplayers != ud.multimode)
253
    if ((i && i != 2) || h.numplayers != ud.multimode)
254
    {
254
    {
255
        if (i == 2 || i == 3)
255
        if (i == 2 || i == 3)
256
            P_DoQuote(QUOTE_SAVE_BAD_VERSION, g_player[myconnectindex].ps);
256
            P_DoQuote(QUOTE_SAVE_BAD_VERSION, g_player[myconnectindex].ps);
257
        else if (h.numplayers!=ud.multimode)
257
        else if (h.numplayers!=ud.multimode)
258
            P_DoQuote(QUOTE_SAVE_BAD_PLAYERS, g_player[myconnectindex].ps);
258
            P_DoQuote(QUOTE_SAVE_BAD_PLAYERS, g_player[myconnectindex].ps);
259
259
260
        kclose(fil);
260
        kclose(fil);
261
        ototalclock = totalclock;
261
        ototalclock = totalclock;
262
        ready2send = 1;
262
        ready2send = 1;
263
263
264
        return 1;
264
        return 1;
265
    }
265
    }
266
266
267
    // some setup first
267
    // some setup first
268
    ud.multimode = h.numplayers;
268
    ud.multimode = h.numplayers;
269
269
270
    if (numplayers > 1)
270
    if (numplayers > 1)
271
    {
271
    {
272
        pub = NUMPAGES;
272
        pub = NUMPAGES;
273
        pus = NUMPAGES;
273
        pus = NUMPAGES;
274
        G_UpdateScreenArea();
274
        G_UpdateScreenArea();
275
        G_DrawBackground();
275
        G_DrawBackground();
276
        menutext(160,100, 0,0, "LOADING...");
276
        menutext(160,100, 0,0, "LOADING...");
277
        nextpage();
277
        nextpage();
278
    }
278
    }
279
279
280
    Net_WaitForServer();
280
    Net_WaitForServer();
281
281
282
    FX_StopAllSounds();
282
    FX_StopAllSounds();
283
    S_ClearSoundLocks();
283
    S_ClearSoundLocks();
284
284
285
    if (spot >= 0 && numplayers==1)
285
    if (spot >= 0 && numplayers==1)
286
    {
286
    {
287
        Bmemcpy(ud.savegame[spot], h.savename, sizeof(ud.savegame[0]));
287
        Bmemcpy(ud.savegame[spot], h.savename, sizeof(ud.savegame[0]));
288
        ud.savegame[spot][sizeof(ud.savegame[0])-1] = 0;
288
        ud.savegame[spot][sizeof(ud.savegame[0])-1] = 0;
289
    }
289
    }
290
290
291
    // non-"m_" fields will be loaded from svgm_udnetw
291
    // non-"m_" fields will be loaded from svgm_udnetw
292
    ud.m_volume_number = h.volnum;
292
    ud.m_volume_number = h.volnum;
293
    ud.m_level_number = h.levnum;
293
    ud.m_level_number = h.levnum;
294
    ud.m_player_skill = h.skill;
294
    ud.m_player_skill = h.skill;
295
295
296
    {
296
    {
297
        // NOTE: Bmemcpy needed for SAVEGAME_MUSIC.
297
        // NOTE: Bmemcpy needed for SAVEGAME_MUSIC.
298
        EDUKE32_STATIC_ASSERT(sizeof(boardfilename) == sizeof(h.boardfn));
298
        EDUKE32_STATIC_ASSERT(sizeof(boardfilename) == sizeof(h.boardfn));
299
        Bmemcpy(boardfilename, h.boardfn, sizeof(boardfilename));
299
        Bmemcpy(boardfilename, h.boardfn, sizeof(boardfilename));
300
    }
300
    }
301
301
302
    E_MapArt_Setup(h.boardfn);  // XXX: Better after the following filename tweaking?
302
    E_MapArt_Setup(h.boardfn);  // XXX: Better after the following filename tweaking?
303
303
304
    if (boardfilename[0])
304
    if (boardfilename[0])
305
        Bstrcpy(currentboardfilename, boardfilename);
305
        Bstrcpy(currentboardfilename, boardfilename);
306
    else if (MapInfo[h.volnum*MAXLEVELS + h.levnum].filename)
306
    else if (MapInfo[h.volnum*MAXLEVELS + h.levnum].filename)
307
        Bstrcpy(currentboardfilename, MapInfo[h.volnum*MAXLEVELS + h.levnum].filename);
307
        Bstrcpy(currentboardfilename, MapInfo[h.volnum*MAXLEVELS + h.levnum].filename);
308
308
309
    if (currentboardfilename[0])
309
    if (currentboardfilename[0])
310
    {
310
    {
311
        append_ext_UNSAFE(currentboardfilename, ".mhk");
311
        append_ext_UNSAFE(currentboardfilename, ".mhk");
312
        loadmaphack(currentboardfilename);
312
        loadmaphack(currentboardfilename);
313
    }
313
    }
314
314
315
    Bmemcpy(currentboardfilename, boardfilename, BMAX_PATH);
315
    Bmemcpy(currentboardfilename, boardfilename, BMAX_PATH);
316
316
317
    if (i == 2)
317
    if (i == 2)
318
    {
318
    {
319
        G_NewGame_EnterLevel();
319
        G_NewGame_EnterLevel();
320
    }
320
    }
321
    else
321
    else
322
    {
322
    {
323
        // read the rest...
323
        // read the rest...
324
        i = sv_loadsnapshot(fil, spot, &h);
324
        i = sv_loadsnapshot(fil, spot, &h);
325
        if (i)
325
        if (i)
326
        {
326
        {
327
            // in theory, we could load into an initial dump first and trivially
327
            // in theory, we could load into an initial dump first and trivially
328
            // recover if things go wrong...
328
            // recover if things go wrong...
329
            Bsprintf(tempbuf, "Loading save game file \"%s\" failed (code %d), cannot recover.", fn, i);
329
            Bsprintf(tempbuf, "Loading save game file \"%s\" failed (code %d), cannot recover.", fn, i);
330
            G_GameExit(tempbuf);
330
            G_GameExit(tempbuf);
331
        }
331
        }
332
    }
332
    }
333
    sv_postudload();  // ud.m_XXX = ud.XXX
333
    sv_postudload();  // ud.m_XXX = ud.XXX
334
334
335
    VM_OnEvent(EVENT_LOADGAME, g_player[myconnectindex].ps->i, myconnectindex);
335
    VM_OnEvent(EVENT_LOADGAME, g_player[myconnectindex].ps->i, myconnectindex);
336
336
337
    return 0;
337
    return 0;
338
}
338
}
339
339
340
////////// TIMER SAVING/RESTORING //////////
340
////////// TIMER SAVING/RESTORING //////////
341
341
342
static struct {
342
static struct {
343
    int32_t totalclock, totalclocklock;  // engine
343
    int32_t totalclock, totalclocklock;  // engine
344
    int32_t ototalclock, lockclock;  // game
344
    int32_t ototalclock, lockclock;  // game
345
} g_timers;
345
} g_timers;
346
346
347
static void G_SaveTimers(void)
347
static void G_SaveTimers(void)
348
{
348
{
349
    g_timers.totalclock = totalclock;
349
    g_timers.totalclock = totalclock;
350
    g_timers.totalclocklock = totalclocklock;
350
    g_timers.totalclocklock = totalclocklock;
351
    g_timers.ototalclock = ototalclock;
351
    g_timers.ototalclock = ototalclock;
352
    g_timers.lockclock = lockclock;
352
    g_timers.lockclock = lockclock;
353
}
353
}
354
354
355
static void G_RestoreTimers(void)
355
static void G_RestoreTimers(void)
356
{
356
{
357
    sampletimer();
357
    sampletimer();
358
358
359
    totalclock = g_timers.totalclock;
359
    totalclock = g_timers.totalclock;
360
    totalclocklock = g_timers.totalclocklock;
360
    totalclocklock = g_timers.totalclocklock;
361
    ototalclock = g_timers.ototalclock;
361
    ototalclock = g_timers.ototalclock;
362
    lockclock = g_timers.lockclock;
362
    lockclock = g_timers.lockclock;
363
}
363
}
364
364
365
//////////
365
//////////
366
366
367
#ifdef __ANDROID__
367
#ifdef __ANDROID__
368
static void G_SavePalette(void)
368
static void G_SavePalette(void)
369
{
369
{
370
    int32_t pfil;
370
    int32_t pfil;
371
371
372
    if ((pfil = kopen4load("palette.dat", 0)) != -1)
372
    if ((pfil = kopen4load("palette.dat", 0)) != -1)
373
    {
373
    {
374
        int len = kfilelength(pfil);
374
        int len = kfilelength(pfil);
375
375
376
        FILE *fil = fopen("palette.dat", "rb");
376
        FILE *fil = fopen("palette.dat", "rb");
377
377
378
        if (!fil)
378
        if (!fil)
379
        {
379
        {
380
            fil = fopen("palette.dat", "wb");
380
            fil = fopen("palette.dat", "wb");
381
381
382
            if (fil)
382
            if (fil)
383
            {
383
            {
384
                char *buf = (char *) Xaligned_alloc(16, len);
384
                char *buf = (char *) Xaligned_alloc(16, len);
385
385
386
                kread(pfil, buf, len);
386
                kread(pfil, buf, len);
387
                fwrite(buf, len, 1, fil);
387
                fwrite(buf, len, 1, fil);
388
                fclose(fil);
388
                fclose(fil);
389
            }
389
            }
390
        }
390
        }
391
        else fclose(fil);
391
        else fclose(fil);
392
    }
392
    }
393
}
393
}
394
#endif
394
#endif
395
395
396
int32_t G_SavePlayer(int32_t spot)
396
int32_t G_SavePlayer(int32_t spot)
397
{
397
{
398
    char fn[16];
398
    char fn[16];
399
    FILE *fil;
399
    FILE *fil;
400
400
401
#ifdef __ANDROID__
401
#ifdef __ANDROID__
402
    G_SavePalette();
402
    G_SavePalette();
403
#endif
403
#endif
404
404
405
    Bassert(spot < MAXSAVEGAMES);
405
    Bassert(spot < MAXSAVEGAMES);
406
406
407
    G_SaveTimers();
407
    G_SaveTimers();
408
408
409
    Bstrcpy(fn, "dukesav0.esv");
409
    Bstrcpy(fn, "dukesav0.esv");
410
    fn[7] = spot + '0';
410
    fn[7] = spot + '0';
411
411
412
//    Bstrcpy(mpfn, "edukA_00.esv");
412
//    Bstrcpy(mpfn, "edukA_00.esv");
413
413
414
    Net_WaitForServer();
414
    Net_WaitForServer();
415
    ready2send = 0;
415
    ready2send = 0;
416
416
417
    {
417
    {
418
        char temp[BMAX_PATH];
418
        char temp[BMAX_PATH];
419
419
420
        if (G_ModDirSnprintf(temp, sizeof(temp), "%s", fn))
420
        if (G_ModDirSnprintf(temp, sizeof(temp), "%s", fn))
421
        {
421
        {
422
            OSD_Printf("G_SavePlayer: file name \"%s\" too long\n", fn);
422
            OSD_Printf("G_SavePlayer: file name \"%s\" too long\n", fn);
423
            return -1;
423
            return -1;
424
        }
424
        }
425
425
426
        errno = 0;
426
        errno = 0;
427
        fil = fopen(temp, "wb");
427
        fil = fopen(temp, "wb");
428
        if (!fil)
428
        if (!fil)
429
        {
429
        {
430
            OSD_Printf("G_SavePlayer: failed opening \"%s\" for writing: %s\n",
430
            OSD_Printf("G_SavePlayer: failed opening \"%s\" for writing: %s\n",
431
                       temp, strerror(errno));
431
                       temp, strerror(errno));
432
            return -1;
432
            return -1;
433
        }
433
        }
434
    }
434
    }
435
435
436
#ifdef POLYMER
436
#ifdef POLYMER
437
    if (getrendermode() == REND_POLYMER)
437
    if (getrendermode() == REND_POLYMER)
438
        polymer_resetlights();
438
        polymer_resetlights();
439
#endif
439
#endif
440
440
441
    VM_OnEvent(EVENT_SAVEGAME, g_player[myconnectindex].ps->i, myconnectindex);
441
    VM_OnEvent(EVENT_SAVEGAME, g_player[myconnectindex].ps->i, myconnectindex);
442
442
443
    // SAVE!
443
    // SAVE!
444
    sv_saveandmakesnapshot(fil, spot, 0, 0, 0);
444
    sv_saveandmakesnapshot(fil, spot, 0, 0, 0);
445
445
446
    fclose(fil);
446
    fclose(fil);
447
447
448
    if (!g_netServer && ud.multimode < 2)
448
    if (!g_netServer && ud.multimode < 2)
449
    {
449
    {
450
#ifdef LUNATIC
450
#ifdef LUNATIC
451
        if (!g_savedOK)
451
        if (!g_savedOK)
452
            Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "^10Failed Saving Game");
452
            Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "^10Failed Saving Game");
453
        else
453
        else
454
#endif
454
#endif
455
            Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "Game Saved");
455
            Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "Game Saved");
456
        P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
456
        P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
457
    }
457
    }
458
458
459
    ready2send = 1;
459
    ready2send = 1;
460
    Net_WaitForServer();
460
    Net_WaitForServer();
461
461
462
    G_RestoreTimers();
462
    G_RestoreTimers();
463
    ototalclock = totalclock;
463
    ototalclock = totalclock;
464
464
465
    return 0;
465
    return 0;
466
}
466
}
467
467
468
void G_LoadPlayerMaybeMulti(int32_t slot)
468
void G_LoadPlayerMaybeMulti(int32_t slot)
469
{
469
{
470
    if (g_netServer || ud.multimode > 1)
470
    if (g_netServer || ud.multimode > 1)
471
    {
471
    {
472
        Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "Multiplayer Loading Not Yet Supported");
472
        Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "Multiplayer Loading Not Yet Supported");
473
        P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
473
        P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
474
474
475
//        G_LoadPlayer(-1-g_lastSaveSlot);
475
//        G_LoadPlayer(-1-g_lastSaveSlot);
476
//        g_player[myconnectindex].ps->gm = MODE_GAME;
476
//        g_player[myconnectindex].ps->gm = MODE_GAME;
477
    }
477
    }
478
    else
478
    else
479
    {
479
    {
480
        int32_t c = G_LoadPlayer(slot);
480
        int32_t c = G_LoadPlayer(slot);
481
        if (c == 0)
481
        if (c == 0)
482
            g_player[myconnectindex].ps->gm = MODE_GAME;
482
            g_player[myconnectindex].ps->gm = MODE_GAME;
483
    }
483
    }
484
}
484
}
485
485
486
void G_SavePlayerMaybeMulti(int32_t slot)
486
void G_SavePlayerMaybeMulti(int32_t slot)
487
{
487
{
488
    Bassert(slot >= 0);
488
    Bassert(slot >= 0);
489
489
490
    CONFIG_WriteSetup(2);
490
    CONFIG_WriteSetup(2);
491
491
492
    if (g_netServer || ud.multimode > 1)
492
    if (g_netServer || ud.multimode > 1)
493
    {
493
    {
494
        Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "Multiplayer Saving Not Yet Supported");
494
        Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "Multiplayer Saving Not Yet Supported");
495
        P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
495
        P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
496
//        G_SavePlayer(-1-slot);
496
//        G_SavePlayer(-1-slot);
497
    }
497
    }
498
    else
498
    else
499
    {
499
    {
500
        G_SavePlayer(slot);
500
        G_SavePlayer(slot);
501
    }
501
    }
502
}
502
}
503
503
504
////////// GENERIC SAVING/LOADING SYSTEM //////////
504
////////// GENERIC SAVING/LOADING SYSTEM //////////
505
505
506
typedef struct dataspec_
506
typedef struct dataspec_
507
{
507
{
508
    uint32_t flags;
508
    uint32_t flags;
509
    void *ptr;
509
    void *ptr;
510
    uint32_t size;
510
    uint32_t size;
511
    intptr_t cnt;
511
    intptr_t cnt;
512
} dataspec_t;
512
} dataspec_t;
513
513
514
#define SV_DEFAULTCOMPRTHRES 8
514
#define SV_DEFAULTCOMPRTHRES 8
515
static uint8_t savegame_diffcompress;  // 0:none, 1:Ken's LZW in cache1d.c
515
static uint8_t savegame_diffcompress;  // 0:none, 1:Ken's LZW in cache1d.c
516
static uint8_t savegame_comprthres;
516
static uint8_t savegame_comprthres;
517
517
518
518
519
#define DS_DYNAMIC 1  // dereference .ptr one more time
519
#define DS_DYNAMIC 1  // dereference .ptr one more time
520
#define DS_STRING 2
520
#define DS_STRING 2
521
#define DS_CMP 4
521
#define DS_CMP 4
522
// 8
522
// 8
523
#define DS_CNT(x) ((sizeof(x))<<3)  // .cnt is pointer to...
523
#define DS_CNT(x) ((sizeof(x))<<3)  // .cnt is pointer to...
524
#define DS_CNT16 16
524
#define DS_CNT16 16
525
#define DS_CNT32 32
525
#define DS_CNT32 32
526
#define DS_CNTMASK (8|DS_CNT16|DS_CNT32|64)
526
#define DS_CNTMASK (8|DS_CNT16|DS_CNT32|64)
527
// 64
527
// 64
528
#define DS_LOADFN 128  // .ptr is function that is run when loading
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
529
#define DS_SAVEFN 256  // .ptr is function that is run when saving
530
#define DS_NOCHK 1024  // don't check for diffs (and don't write out in dump) since assumed constant throughout demo
530
#define DS_NOCHK 1024  // don't check for diffs (and don't write out in dump) since assumed constant throughout demo
531
#define DS_PROTECTFN 512
531
#define DS_PROTECTFN 512
532
#define DS_END (0x70000000)
532
#define DS_END (0x70000000)
533
533
534
static int32_t ds_getcnt(const dataspec_t *sp)
534
static int32_t ds_getcnt(const dataspec_t *sp)
535
{
535
{
536
    int rv = -1;
536
    int rv = -1;
537
537
538
    switch (sp->flags & DS_CNTMASK)
538
    switch (sp->flags & DS_CNTMASK)
539
    {
539
    {
540
        case 0: rv = sp->cnt; break;
540
        case 0: rv = sp->cnt; break;
541
        case DS_CNT16: rv = *((int16_t *)sp->cnt); break;
541
        case DS_CNT16: rv = *((int16_t *)sp->cnt); break;
542
        case DS_CNT32: rv = *((int32_t *)sp->cnt); break;
542
        case DS_CNT32: rv = *((int32_t *)sp->cnt); break;
543
    }
543
    }
544
544
545
    return rv;
545
    return rv;
546
}
546
}
547
547
548
static inline void ds_get(const dataspec_t *sp, void **ptr, int32_t *cnt)
548
static inline void ds_get(const dataspec_t *sp, void **ptr, int32_t *cnt)
549
{
549
{
550
    *cnt = ds_getcnt(sp);
550
    *cnt = ds_getcnt(sp);
551
    *ptr = (sp->flags&DS_DYNAMIC) ? *((void **)sp->ptr) : sp->ptr;
551
    *ptr = (sp->flags&DS_DYNAMIC) ? *((void **)sp->ptr) : sp->ptr;
552
}
552
}
553
553
554
// write state to file and/or to dump
554
// write state to file and/or to dump
555
static uint8_t *writespecdata(const dataspec_t *spec, FILE *fil, uint8_t *dump)
555
static uint8_t *writespecdata(const dataspec_t *spec, FILE *fil, uint8_t *dump)
556
{
556
{
557
    int32_t cnt;
557
    int32_t cnt;
558
    void *ptr;
558
    void *ptr;
559
    const dataspec_t *sp=spec;
559
    const dataspec_t *sp=spec;
560
560
561
    for (; sp->flags!=DS_END; sp++)
561
    for (; sp->flags!=DS_END; sp++)
562
    {
562
    {
563
        if (sp->flags&(DS_SAVEFN|DS_LOADFN))
563
        if (sp->flags&(DS_SAVEFN|DS_LOADFN))
564
        {
564
        {
565
            if (sp->flags&DS_SAVEFN)
565
            if (sp->flags&DS_SAVEFN)
566
                (*(void (*)(void))sp->ptr)();
566
                (*(void (*)(void))sp->ptr)();
567
            continue;
567
            continue;
568
        }
568
        }
569
569
570
        if (!fil && (sp->flags&(DS_NOCHK|DS_CMP|DS_STRING)))
570
        if (!fil && (sp->flags&(DS_NOCHK|DS_CMP|DS_STRING)))
571
            continue;
571
            continue;
572
572
573
        if (sp->flags&DS_STRING)
573
        if (sp->flags&DS_STRING)
574
        {
574
        {
575
            fwrite(sp->ptr, Bstrlen((const char *)sp->ptr), 1, fil);  // not null-terminated!
575
            fwrite(sp->ptr, Bstrlen((const char *)sp->ptr), 1, fil);  // not null-terminated!
576
            continue;
576
            continue;
577
        }
577
        }
578
578
579
        ds_get(sp, &ptr, &cnt);
579
        ds_get(sp, &ptr, &cnt);
580
        if (cnt < 0) { OSD_Printf("wsd: cnt=%d, f=0x%x.\n",cnt,sp->flags); continue; }
580
        if (cnt < 0) { OSD_Printf("wsd: cnt=%d, f=0x%x.\n",cnt,sp->flags); continue; }
581
581
582
        if (fil)
582
        if (fil)
583
        {
583
        {
584
            if (((sp->flags&DS_CNTMASK)==0 && sp->size*cnt<=savegame_comprthres)
584
            if (((sp->flags&DS_CNTMASK)==0 && sp->size*cnt<=savegame_comprthres)
585
                    || (sp->flags&DS_CMP))
585
                    || (sp->flags&DS_CMP))
586
                fwrite(ptr, sp->size, cnt, fil);
586
                fwrite(ptr, sp->size, cnt, fil);
587
            else
587
            else
588
                dfwrite((void *)ptr, sp->size, cnt, fil);
588
                dfwrite((void *)ptr, sp->size, cnt, fil);
589
        }
589
        }
590
590
591
        if (dump && (sp->flags&(DS_NOCHK|DS_CMP))==0)
591
        if (dump && (sp->flags&(DS_NOCHK|DS_CMP))==0)
592
        {
592
        {
593
            Bmemcpy(dump, ptr, sp->size*cnt);
593
            Bmemcpy(dump, ptr, sp->size*cnt);
594
            dump += sp->size*cnt;
594
            dump += sp->size*cnt;
595
        }
595
        }
596
    }
596
    }
597
    return dump;
597
    return dump;
598
}
598
}
599
599
600
// let havedump=dumpvar&&*dumpvar
600
// let havedump=dumpvar&&*dumpvar
601
// (fil>=0 && havedump): first restore dump from file, then restore state from dump
601
// (fil>=0 && havedump): first restore dump from file, then restore state from dump
602
// (fil<0 && havedump): only restore state from dump
602
// (fil<0 && havedump): only restore state from dump
603
// (fil>=0 && !havedump): only restore state from file
603
// (fil>=0 && !havedump): only restore state from file
604
static int32_t readspecdata(const dataspec_t *spec, int32_t fil, uint8_t **dumpvar)
604
static int32_t readspecdata(const dataspec_t *spec, int32_t fil, uint8_t **dumpvar)
605
{
605
{
606
    int32_t cnt, i, j;
606
    int32_t cnt, i, j;
607
    void *ptr;
607
    void *ptr;
608
    uint8_t *dump=dumpvar?*dumpvar:NULL, *mem;
608
    uint8_t *dump=dumpvar?*dumpvar:NULL, *mem;
609
    const dataspec_t *sp=spec;
609
    const dataspec_t *sp=spec;
610
    static char cmpstrbuf[32];
610
    static char cmpstrbuf[32];
611
611
612
    for (; sp->flags!=DS_END; sp++)
612
    for (; sp->flags!=DS_END; sp++)
613
    {
613
    {
614
        if (fil < 0 && sp->flags&(DS_NOCHK|DS_STRING|DS_CMP))  // we're updating
614
        if (fil < 0 && sp->flags&(DS_NOCHK|DS_STRING|DS_CMP))  // we're updating
615
            continue;
615
            continue;
616
616
617
        if (sp->flags&(DS_LOADFN|DS_SAVEFN))
617
        if (sp->flags&(DS_LOADFN|DS_SAVEFN))
618
        {
618
        {
619
            if (sp->flags&DS_LOADFN)
619
            if (sp->flags&DS_LOADFN)
620
                (*(void (*)())sp->ptr)();
620
                (*(void (*)())sp->ptr)();
621
            continue;
621
            continue;
622
        }
622
        }
623
623
624
        if (sp->flags&(DS_STRING|DS_CMP))  // DS_STRING and DS_CMP is for static data only
624
        if (sp->flags&(DS_STRING|DS_CMP))  // DS_STRING and DS_CMP is for static data only
625
        {
625
        {
626
            if (sp->flags&(DS_STRING))
626
            if (sp->flags&(DS_STRING))
627
                i = Bstrlen((const char *)sp->ptr);
627
                i = Bstrlen((const char *)sp->ptr);
628
            else
628
            else
629
                i = sp->size*sp->cnt;
629
                i = sp->size*sp->cnt;
630
630
631
            j=kread(fil, cmpstrbuf, i);
631
            j=kread(fil, cmpstrbuf, i);
632
            if (j!=i || Bmemcmp(sp->ptr, cmpstrbuf, i))
632
            if (j!=i || Bmemcmp(sp->ptr, cmpstrbuf, i))
633
            {
633
            {
634
                OSD_Printf("rsd: spec=%s, idx=%d:\n", (char *)spec->ptr, (int32_t)(sp-spec));
634
                OSD_Printf("rsd: spec=%s, idx=%d:\n", (char *)spec->ptr, (int32_t)(sp-spec));
635
                if (j!=i)
635
                if (j!=i)
636
                    OSD_Printf("    kread returned %d, expected %d.\n", j, i);
636
                    OSD_Printf("    kread returned %d, expected %d.\n", j, i);
637
                else
637
                else
638
                    OSD_Printf("    sp->ptr and cmpstrbuf not identical!\n");
638
                    OSD_Printf("    sp->ptr and cmpstrbuf not identical!\n");
639
                return -1;
639
                return -1;
640
            }
640
            }
641
            continue;
641
            continue;
642
        }
642
        }
643
643
644
        ds_get(sp, &ptr, &cnt);
644
        ds_get(sp, &ptr, &cnt);
645
        if (cnt < 0) { OSD_Printf("rsd: cnt<0... wtf?\n"); return -1; }
645
        if (cnt < 0) { OSD_Printf("rsd: cnt<0... wtf?\n"); return -1; }
646
646
647
        if (fil>=0)
647
        if (fil>=0)
648
        {
648
        {
649
            mem = (dump && (sp->flags&DS_NOCHK)==0) ? dump : (uint8_t *)ptr;
649
            mem = (dump && (sp->flags&DS_NOCHK)==0) ? dump : (uint8_t *)ptr;
650
650
651
            if ((sp->flags&DS_CNTMASK)==0 && sp->size*cnt<=savegame_comprthres)
651
            if ((sp->flags&DS_CNTMASK)==0 && sp->size*cnt<=savegame_comprthres)
652
            {
652
            {
653
                i = kread(fil, mem, cnt*sp->size);
653
                i = kread(fil, mem, cnt*sp->size);
654
                j = cnt*sp->size;
654
                j = cnt*sp->size;
655
            }
655
            }
656
            else
656
            else
657
            {
657
            {
658
                i = kdfread(mem, sp->size, cnt, fil);
658
                i = kdfread(mem, sp->size, cnt, fil);
659
                j = cnt;
659
                j = cnt;
660
            }
660
            }
661
            if (i!=j)
661
            if (i!=j)
662
            {
662
            {
663
                OSD_Printf("rsd: spec=%s, idx=%d, mem=%p\n", (char *)spec->ptr, (int32_t)(sp-spec), mem);
663
                OSD_Printf("rsd: spec=%s, idx=%d, mem=%p\n", (char *)spec->ptr, (int32_t)(sp-spec), mem);
664
                OSD_Printf("     (%s): read %d, expected %d!\n",
664
                OSD_Printf("     (%s): read %d, expected %d!\n",
665
                           ((sp->flags&DS_CNTMASK)==0 && sp->size*cnt<=savegame_comprthres)?
665
                           ((sp->flags&DS_CNTMASK)==0 && sp->size*cnt<=savegame_comprthres)?
666
                           "uncompressed":"compressed", i, j);
666
                           "uncompressed":"compressed", i, j);
667
667
668
                if (i==-1)
668
                if (i==-1)
669
                    OSD_Printf("     read: %s\n", strerror(errno));
669
                    OSD_Printf("     read: %s\n", strerror(errno));
670
                return -1;
670
                return -1;
671
            }
671
            }
672
        }
672
        }
673
673
674
        if (dump && (sp->flags&DS_NOCHK)==0)
674
        if (dump && (sp->flags&DS_NOCHK)==0)
675
        {
675
        {
676
            Bmemcpy(ptr, dump, sp->size*cnt);
676
            Bmemcpy(ptr, dump, sp->size*cnt);
677
            dump += sp->size*cnt;
677
            dump += sp->size*cnt;
678
        }
678
        }
679
    }
679
    }
680
680
681
    if (dumpvar)
681
    if (dumpvar)
682
        *dumpvar = dump;
682
        *dumpvar = dump;
683
    return 0;
683
    return 0;
684
}
684
}
685
685
686
#define UINT(bits) uint##bits##_t
686
#define UINT(bits) uint##bits##_t
687
#define BYTES(bits) (bits>>3)
687
#define BYTES(bits) (bits>>3)
688
#define VAL(bits,p) (*(UINT(bits) *)(p))
688
#define VAL(bits,p) (*(UINT(bits) *)(p))
689
689
690
static void docmpsd(const void *ptr, void *dump, uint32_t size, uint32_t cnt, uint8_t **diffvar)
690
static void docmpsd(const void *ptr, void *dump, uint32_t size, uint32_t cnt, uint8_t **diffvar)
691
{
691
{
692
    uint8_t *retdiff = *diffvar;
692
    uint8_t *retdiff = *diffvar;
693
693
694
    // Hail to the C preprocessor, baby!
694
    // Hail to the C preprocessor, baby!
695
#define CPSINGLEVAL(Datbits) \
695
#define CPSINGLEVAL(Datbits) \
696
        if (VAL(Datbits, ptr) != VAL(Datbits, dump))  \
696
        if (VAL(Datbits, ptr) != VAL(Datbits, dump))  \
697
        {                                             \
697
        {                                             \
698
            VAL(Datbits, retdiff) = VAL(Datbits, dump) = VAL(Datbits, ptr); \
698
            VAL(Datbits, retdiff) = VAL(Datbits, dump) = VAL(Datbits, ptr); \
699
            *diffvar = retdiff+BYTES(Datbits);        \
699
            *diffvar = retdiff+BYTES(Datbits);        \
700
        }
700
        }
701
701
702
    if (cnt==1)
702
    if (cnt==1)
703
        switch (size)
703
        switch (size)
704
        {
704
        {
705
        case 8: CPSINGLEVAL(64); return;
705
        case 8: CPSINGLEVAL(64); return;
706
        case 4: CPSINGLEVAL(32); return;
706
        case 4: CPSINGLEVAL(32); return;
707
        case 2: CPSINGLEVAL(16); return;
707
        case 2: CPSINGLEVAL(16); return;
708
        case 1: CPSINGLEVAL(8); return;
708
        case 1: CPSINGLEVAL(8); return;
709
        }
709
        }
710
710
711
#define CPELTS(Idxbits, Datbits) do \
711
#define CPELTS(Idxbits, Datbits) do \
712
    {                                         \
712
    {                                         \
713
        for (i=0; i<nelts; i++)               \
713
        for (i=0; i<nelts; i++)               \
714
        {                                     \
714
        {                                     \
715
            if (*p!=*op)                      \
715
            if (*p!=*op)                      \
716
            {                                 \
716
            {                                 \
717
                *op = *p;      \
717
                *op = *p;      \
718
                VAL(Idxbits, retdiff) = i;    \
718
                VAL(Idxbits, retdiff) = i;    \
719
                retdiff += BYTES(Idxbits);    \
719
                retdiff += BYTES(Idxbits);    \
720
                VAL(Datbits, retdiff) = *p;   \
720
                VAL(Datbits, retdiff) = *p;   \
721
                retdiff += BYTES(Datbits);    \
721
                retdiff += BYTES(Datbits);    \
722
            }                                 \
722
            }                                 \
723
            p++;                              \
723
            p++;                              \
724
            op++;                             \
724
            op++;                             \
725
        }                                     \
725
        }                                     \
726
        VAL(Idxbits, retdiff) = -1;           \
726
        VAL(Idxbits, retdiff) = -1;           \
727
        retdiff += BYTES(Idxbits);            \
727
        retdiff += BYTES(Idxbits);            \
728
    } while (0)
728
    } while (0)
729
729
730
#define CPDATA(Datbits) do \
730
#define CPDATA(Datbits) do \
731
    { \
731
    { \
732
        const UINT(Datbits) *p=(UINT(Datbits) *)ptr;    \
732
        const UINT(Datbits) *p=(UINT(Datbits) *)ptr;    \
733
        UINT(Datbits) *op=(UINT(Datbits) *)dump;        \
733
        UINT(Datbits) *op=(UINT(Datbits) *)dump;        \
734
        uint32_t i, nelts=tabledivide32_noinline(size*cnt, BYTES(Datbits));    \
734
        uint32_t i, nelts=tabledivide32_noinline(size*cnt, BYTES(Datbits));    \
735
        if (nelts>65536)                                \
735
        if (nelts>65536)                                \
736
            CPELTS(32,Datbits);                         \
736
            CPELTS(32,Datbits);                         \
737
        else if (nelts>256)                             \
737
        else if (nelts>256)                             \
738
            CPELTS(16,Datbits);                         \
738
            CPELTS(16,Datbits);                         \
739
        else                                            \
739
        else                                            \
740
            CPELTS(8,Datbits);                          \
740
            CPELTS(8,Datbits);                          \
741
    } while (0)
741
    } while (0)
742
742
743
    if (size==8)
743
    if (size==8)
744
        CPDATA(64);
744
        CPDATA(64);
745
    else if ((size&3)==0)
745
    else if ((size&3)==0)
746
        CPDATA(32);
746
        CPDATA(32);
747
    else if ((size&1)==0)
747
    else if ((size&1)==0)
748
        CPDATA(16);
748
        CPDATA(16);
749
    else
749
    else
750
        CPDATA(8);
750
        CPDATA(8);
751
751
752
    *diffvar = retdiff;
752
    *diffvar = retdiff;
753
    return;
753
    return;
754
754
755
#undef CPELTS
755
#undef CPELTS
756
#undef CPSINGLEVAL
756
#undef CPSINGLEVAL
757
#undef CPDATA
757
#undef CPDATA
758
}
758
}
759
759
760
// get the number of elements to be monitored for changes
760
// get the number of elements to be monitored for changes
761
static int32_t getnumvar(const dataspec_t *spec)
761
static int32_t getnumvar(const dataspec_t *spec)
762
{
762
{
763
    int32_t n=0;
763
    int32_t n=0;
764
    for (; spec->flags!=DS_END; spec++)
764
    for (; spec->flags!=DS_END; spec++)
765
        n += (spec->flags&(DS_STRING|DS_CMP|DS_NOCHK|DS_SAVEFN|DS_LOADFN) ? 0 : 1);
765
        n += (spec->flags&(DS_STRING|DS_CMP|DS_NOCHK|DS_SAVEFN|DS_LOADFN) ? 0 : 1);
766
    return n;
766
    return n;
767
}
767
}
768
768
769
// update dump at *dumpvar with new state and write diff to *diffvar
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)
770
static void cmpspecdata(const dataspec_t *spec, uint8_t **dumpvar, uint8_t **diffvar)
771
{
771
{
772
    void *ptr;
772
    void *ptr;
773
    uint8_t *dump=*dumpvar, *diff=*diffvar, *tmptr;
773
    uint8_t *dump=*dumpvar, *diff=*diffvar, *tmptr;
774
    const dataspec_t *sp=spec;
774
    const dataspec_t *sp=spec;
775
    int32_t cnt, eltnum=0, nbytes=(getnumvar(spec)+7)>>3, l=Bstrlen((const char *)spec->ptr);
775
    int32_t cnt, eltnum=0, nbytes=(getnumvar(spec)+7)>>3, l=Bstrlen((const char *)spec->ptr);
776
776
777
    Bmemcpy(diff, spec->ptr, l);
777
    Bmemcpy(diff, spec->ptr, l);
778
    diff+=l;
778
    diff+=l;
779
779
780
    while (nbytes--)
780
    while (nbytes--)
781
        *(diff++) = 0;  // the bitmap of indices which elements of spec have changed go here
781
        *(diff++) = 0;  // the bitmap of indices which elements of spec have changed go here
782
782
783
    for (sp++; sp->flags!=DS_END; sp++)
783
    for (sp++; sp->flags!=DS_END; sp++)
784
    {
784
    {
785
        if ((sp->flags&(DS_NOCHK|DS_STRING|DS_CMP)))
785
        if ((sp->flags&(DS_NOCHK|DS_STRING|DS_CMP)))
786
            continue;
786
            continue;
787
787
788
        if (sp->flags&(DS_LOADFN|DS_SAVEFN))
788
        if (sp->flags&(DS_LOADFN|DS_SAVEFN))
789
        {
789
        {
790
            if ((sp->flags&(DS_PROTECTFN))==0)
790
            if ((sp->flags&(DS_PROTECTFN))==0)
791
                (*(void (*)())sp->ptr)();
791
                (*(void (*)())sp->ptr)();
792
            continue;
792
            continue;
793
        }
793
        }
794
794
795
        ds_get(sp, &ptr, &cnt);
795
        ds_get(sp, &ptr, &cnt);
796
        if (cnt < 0) { OSD_Printf("csd: cnt=%d, f=0x%x\n", cnt, sp->flags); continue; }
796
        if (cnt < 0) { OSD_Printf("csd: cnt=%d, f=0x%x\n", cnt, sp->flags); continue; }
797
797
798
        tmptr = diff;
798
        tmptr = diff;
799
        docmpsd(ptr, dump, sp->size, cnt, &diff);
799
        docmpsd(ptr, dump, sp->size, cnt, &diff);
800
        if (diff != tmptr)
800
        if (diff != tmptr)
801
            (*diffvar + l)[eltnum>>3] |= 1<<(eltnum&7);
801
            (*diffvar + l)[eltnum>>3] |= 1<<(eltnum&7);
802
        dump += sp->size*cnt;
802
        dump += sp->size*cnt;
803
        eltnum++;
803
        eltnum++;
804
    }
804
    }
805
805
806
    *diffvar = diff;
806
    *diffvar = diff;
807
    *dumpvar = dump;
807
    *dumpvar = dump;
808
    return;
808
    return;
809
}
809
}
810
810
811
#define VALOFS(bits,p,ofs) (*(((UINT(bits) *)(p)) + (ofs)))
811
#define VALOFS(bits,p,ofs) (*(((UINT(bits) *)(p)) + (ofs)))
812
812
813
// apply diff to dump, not to state! state is restored from dump afterwards.
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)
814
static int32_t applydiff(const dataspec_t *spec, uint8_t **dumpvar, uint8_t **diffvar)
815
{
815
{
816
    uint8_t *dumptr=*dumpvar, *diffptr=*diffvar;
816
    uint8_t *dumptr=*dumpvar, *diffptr=*diffvar;
817
    const dataspec_t *sp=spec;
817
    const dataspec_t *sp=spec;
818
    int32_t cnt, eltnum=-1, nbytes=(getnumvar(spec)+7)>>3, l=Bstrlen((const char *)spec->ptr);
818
    int32_t cnt, eltnum=-1, nbytes=(getnumvar(spec)+7)>>3, l=Bstrlen((const char *)spec->ptr);
819
819
820
    if (Bmemcmp(diffptr, spec->ptr, l))  // check STRING magic (sync check)
820
    if (Bmemcmp(diffptr, spec->ptr, l))  // check STRING magic (sync check)
821
        return 1;
821
        return 1;
822
822
823
    diffptr += l+nbytes;
823
    diffptr += l+nbytes;
824
824
825
    for (sp++; sp->flags!=DS_END; sp++)
825
    for (sp++; sp->flags!=DS_END; sp++)
826
    {
826
    {
827
        if ((sp->flags&(DS_NOCHK|DS_STRING|DS_CMP|DS_LOADFN|DS_SAVEFN)))
827
        if ((sp->flags&(DS_NOCHK|DS_STRING|DS_CMP|DS_LOADFN|DS_SAVEFN)))
828
            continue;
828
            continue;
829
829
830
        cnt = ds_getcnt(sp);
830
        cnt = ds_getcnt(sp);
831
        if (cnt < 0) return 1;
831
        if (cnt < 0) return 1;
832
832
833
        eltnum++;
833
        eltnum++;
834
        if (((*diffvar + l)[eltnum>>3]&(1<<(eltnum&7))) == 0)
834
        if (((*diffvar + l)[eltnum>>3]&(1<<(eltnum&7))) == 0)
835
        {
835
        {
836
            dumptr += sp->size*cnt;
836
            dumptr += sp->size*cnt;
837
            continue;
837
            continue;
838
        }
838
        }
839
839
840
// ----------
840
// ----------
841
#define CPSINGLEVAL(Datbits) \
841
#define CPSINGLEVAL(Datbits) \
842
            VAL(Datbits, dumptr) = VAL(Datbits, diffptr); \
842
            VAL(Datbits, dumptr) = VAL(Datbits, diffptr); \
843
            diffptr += BYTES(Datbits); \
843
            diffptr += BYTES(Datbits); \
844
            dumptr += BYTES(Datbits)
844
            dumptr += BYTES(Datbits)
845
845
846
        if (cnt==1)
846
        if (cnt==1)
847
        {
847
        {
848
            switch (sp->size)
848
            switch (sp->size)
849
            {
849
            {
850
            case 8: CPSINGLEVAL(64); continue;
850
            case 8: CPSINGLEVAL(64); continue;
851
            case 4: CPSINGLEVAL(32); continue;
851
            case 4: CPSINGLEVAL(32); continue;
852
            case 2: CPSINGLEVAL(16); continue;
852
            case 2: CPSINGLEVAL(16); continue;
853
            case 1: CPSINGLEVAL(8); continue;
853
            case 1: CPSINGLEVAL(8); continue;
854
            }
854
            }
855
        }
855
        }
856
856
857
#define CPELTS(Idxbits, Datbits) do \
857
#define CPELTS(Idxbits, Datbits) do \
858
        {                                    \
858
        {                                    \
859
            UINT(Idxbits) idx;               \
859
            UINT(Idxbits) idx;               \
860
            goto readidx_##Idxbits##_##Datbits; \
860
            goto readidx_##Idxbits##_##Datbits; \
861
            do                               \
861
            do                               \
862
            {                                \
862
            {                                \
863
                VALOFS(Datbits, dumptr, idx) = VAL(Datbits, diffptr); \
863
                VALOFS(Datbits, dumptr, idx) = VAL(Datbits, diffptr); \
864
                diffptr += BYTES(Datbits);   \
864
                diffptr += BYTES(Datbits);   \
865
readidx_##Idxbits##_##Datbits:               \
865
readidx_##Idxbits##_##Datbits:               \
866
                idx = VAL(Idxbits, diffptr); \
866
                idx = VAL(Idxbits, diffptr); \
867
                diffptr += BYTES(Idxbits);   \
867
                diffptr += BYTES(Idxbits);   \
868
            } while ((int##Idxbits##_t)idx != -1);  \
868
            } while ((int##Idxbits##_t)idx != -1);  \
869
        } while (0)
869
        } while (0)
870
870
871
#define CPDATA(Datbits) do \
871
#define CPDATA(Datbits) do \
872
        {                             \
872
        {                             \
873
            uint32_t nelts=tabledivide32_noinline(sp->size*cnt, BYTES(Datbits)); \
873
            uint32_t nelts=tabledivide32_noinline(sp->size*cnt, BYTES(Datbits)); \
874
            if (nelts>65536)          \
874
            if (nelts>65536)          \
875
                CPELTS(32,Datbits);   \
875
                CPELTS(32,Datbits);   \
876
            else if (nelts>256)       \
876
            else if (nelts>256)       \
877
                CPELTS(16,Datbits);   \
877
                CPELTS(16,Datbits);   \
878
            else                      \
878
            else                      \
879
                CPELTS(8,Datbits);    \
879
                CPELTS(8,Datbits);    \
880
        } while (0)
880
        } while (0)
881
881
882
        if (sp->size==8)
882
        if (sp->size==8)
883
            CPDATA(64);
883
            CPDATA(64);
884
        else if ((sp->size&3)==0)
884
        else if ((sp->size&3)==0)
885
            CPDATA(32);
885
            CPDATA(32);
886
        else if ((sp->size&1)==0)
886
        else if ((sp->size&1)==0)
887
            CPDATA(16);
887
            CPDATA(16);
888
        else
888
        else
889
            CPDATA(8);
889
            CPDATA(8);
890
        dumptr += sp->size*cnt;
890
        dumptr += sp->size*cnt;
891
// ----------
891
// ----------
892
892
893
#undef CPELTS
893
#undef CPELTS
894
#undef CPSINGLEVAL
894
#undef CPSINGLEVAL
895
#undef CPDATA
895
#undef CPDATA
896
    }
896
    }
897
897
898
    *diffvar = diffptr;
898
    *diffvar = diffptr;
899
    *dumpvar = dumptr;
899
    *dumpvar = dumptr;
900
    return 0;
900
    return 0;
901
}
901
}
902
902
903
#undef VAL
903
#undef VAL
904
#undef VALOFS
904
#undef VALOFS
905
#undef BYTES
905
#undef BYTES
906
#undef UINT
906
#undef UINT
907
907
908
// calculate size needed for dump
908
// calculate size needed for dump
909
static uint32_t calcsz(const dataspec_t *spec)
909
static uint32_t calcsz(const dataspec_t *spec)
910
{
910
{
911
    const dataspec_t *sp=spec;
911
    const dataspec_t *sp=spec;
912
    int32_t cnt;
912
    int32_t cnt;
913
    uint32_t dasiz=0;
913
    uint32_t dasiz=0;
914
914
915
    for (; sp->flags!=DS_END; sp++)
915
    for (; sp->flags!=DS_END; sp++)
916
    {
916
    {
917
        // DS_STRINGs are used as sync checks in the diffs but not in the dump
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)))
918
        if ((sp->flags&(DS_CMP|DS_NOCHK|DS_SAVEFN|DS_LOADFN|DS_STRING)))
919
            continue;
919
            continue;
920
920
921
        cnt = ds_getcnt(sp);
921
        cnt = ds_getcnt(sp);
922
        if (cnt<=0) continue;
922
        if (cnt<=0) continue;
923
923
924
        dasiz += cnt*sp->size;
924
        dasiz += cnt*sp->size;
925
    }
925
    }
926
926
927
    return dasiz;
927
    return dasiz;
928
}
928
}
929
929
930
#ifdef USE_OPENGL
930
#ifdef USE_OPENGL
931
static void sv_prespriteextsave();
931
static void sv_prespriteextsave();
932
static void sv_postspriteext();
932
static void sv_postspriteext();
933
#endif
933
#endif
934
#if !defined LUNATIC
934
#if !defined LUNATIC
935
static void sv_calcbitptrsize();
935
static void sv_calcbitptrsize();
936
static void sv_prescriptsave_once();
936
static void sv_prescriptsave_once();
937
static void sv_prescriptload_once();
937
static void sv_prescriptload_once();
938
static void sv_postscript_once();
938
static void sv_postscript_once();
939
#else
939
#else
940
// Recreate Lua state.
940
// Recreate Lua state.
941
// XXX: It may matter a great deal when this is run from if the Lua code refers
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.
942
// to C-side data at file scope. Such usage is strongly discouraged though.
943
static void sv_create_lua_state(void)
943
static void sv_create_lua_state(void)
944
{
944
{
945
    El_CreateGameState();
945
    El_CreateGameState();
946
    G_PostCreateGameState();
946
    G_PostCreateGameState();
947
}
947
}
948
#endif
948
#endif
949
static void sv_preactordatasave();
949
static void sv_preactordatasave();
950
static void sv_postactordata();
950
static void sv_postactordata();
951
static void sv_preanimateptrsave();
951
static void sv_preanimateptrsave();
952
static void sv_postanimateptr();
952
static void sv_postanimateptr();
953
static void sv_prequote();
953
static void sv_prequote();
954
static void sv_quotesave();
954
static void sv_quotesave();
955
static void sv_quoteload();
955
static void sv_quoteload();
956
static void sv_prequoteredef();
956
static void sv_prequoteredef();
957
static void sv_quoteredefsave();
957
static void sv_quoteredefsave();
958
static void sv_quoteredefload();
958
static void sv_quoteredefload();
959
static void sv_postquoteredef();
959
static void sv_postquoteredef();
960
static void sv_restsave();
960
static void sv_restsave();
961
static void sv_restload();
961
static void sv_restload();
962
962
963
#define SVARDATALEN \
963
#define SVARDATALEN \
964
    ((sizeof(g_player[0].user_name)+sizeof(g_player[0].pcolor)+sizeof(g_player[0].pteam) \
964
    ((sizeof(g_player[0].user_name)+sizeof(g_player[0].pcolor)+sizeof(g_player[0].pteam) \
965
      +sizeof(g_player[0].frags)+sizeof(DukePlayer_t))*MAXPLAYERS)
965
      +sizeof(g_player[0].frags)+sizeof(DukePlayer_t))*MAXPLAYERS)
966
966
967
#if !defined LUNATIC
967
#if !defined LUNATIC
968
static uint32_t savegame_bitptrsize;
968
static uint32_t savegame_bitptrsize;
969
#endif
969
#endif
970
static uint8_t savegame_quotedef[MAXQUOTES>>3];
970
static uint8_t savegame_quotedef[MAXQUOTES>>3];
971
static char(*savegame_quotes)[MAXQUOTELEN];
971
static char(*savegame_quotes)[MAXQUOTELEN];
972
static char(*savegame_quoteredefs)[MAXQUOTELEN];
972
static char(*savegame_quoteredefs)[MAXQUOTELEN];
973
static uint8_t savegame_restdata[SVARDATALEN];
973
static uint8_t savegame_restdata[SVARDATALEN];
974
974
975
static const dataspec_t svgm_udnetw[] =
975
static const dataspec_t svgm_udnetw[] =
976
{
976
{
977
    { DS_STRING, (void *)"blK:udnt", 0, 1 },
977
    { DS_STRING, (void *)"blK:udnt", 0, 1 },
978
    { 0, &ud.multimode, sizeof(ud.multimode), 1 },
978
    { 0, &ud.multimode, sizeof(ud.multimode), 1 },
979
    { 0, &g_numPlayerSprites, sizeof(g_numPlayerSprites), 1 },
979
    { 0, &g_numPlayerSprites, sizeof(g_numPlayerSprites), 1 },
980
    { 0, &g_playerSpawnPoints, sizeof(g_playerSpawnPoints), 1 },
980
    { 0, &g_playerSpawnPoints, sizeof(g_playerSpawnPoints), 1 },
981
981
982
    { DS_NOCHK, &ud.volume_number, sizeof(ud.volume_number), 1 },
982
    { DS_NOCHK, &ud.volume_number, sizeof(ud.volume_number), 1 },
983
    { DS_NOCHK, &ud.level_number, sizeof(ud.level_number), 1 },
983
    { DS_NOCHK, &ud.level_number, sizeof(ud.level_number), 1 },
984
    { DS_NOCHK, &ud.player_skill, sizeof(ud.player_skill), 1 },
984
    { DS_NOCHK, &ud.player_skill, sizeof(ud.player_skill), 1 },
985
985
986
    { DS_NOCHK, &ud.from_bonus, sizeof(ud.from_bonus), 1 },
986
    { DS_NOCHK, &ud.from_bonus, sizeof(ud.from_bonus), 1 },
987
    { DS_NOCHK, &ud.secretlevel, sizeof(ud.secretlevel), 1 },
987
    { DS_NOCHK, &ud.secretlevel, sizeof(ud.secretlevel), 1 },
988
    { DS_NOCHK, &ud.respawn_monsters, sizeof(ud.respawn_monsters), 1 },
988
    { DS_NOCHK, &ud.respawn_monsters, sizeof(ud.respawn_monsters), 1 },
989
    { DS_NOCHK, &ud.respawn_items, sizeof(ud.respawn_items), 1 },
989
    { DS_NOCHK, &ud.respawn_items, sizeof(ud.respawn_items), 1 },
990
    { DS_NOCHK, &ud.respawn_inventory, sizeof(ud.respawn_inventory), 1 },
990
    { DS_NOCHK, &ud.respawn_inventory, sizeof(ud.respawn_inventory), 1 },
991
    { 0, &ud.god, sizeof(ud.god), 1 },
991
    { 0, &ud.god, sizeof(ud.god), 1 },
992
    { 0, &ud.auto_run, sizeof(ud.auto_run), 1 },
992
    { 0, &ud.auto_run, sizeof(ud.auto_run), 1 },
993
//    { DS_NOCHK, &ud.crosshair, sizeof(ud.crosshair), 1 },
993
//    { DS_NOCHK, &ud.crosshair, sizeof(ud.crosshair), 1 },
994
    { DS_NOCHK, &ud.monsters_off, sizeof(ud.monsters_off), 1 },
994
    { DS_NOCHK, &ud.monsters_off, sizeof(ud.monsters_off), 1 },
995
    { DS_NOCHK, &ud.last_level, sizeof(ud.last_level), 1 },
995
    { DS_NOCHK, &ud.last_level, sizeof(ud.last_level), 1 },
996
    { 0, &ud.eog, sizeof(ud.eog), 1 },
996
    { 0, &ud.eog, sizeof(ud.eog), 1 },
997
    { DS_NOCHK, &ud.coop, sizeof(ud.coop), 1 },
997
    { DS_NOCHK, &ud.coop, sizeof(ud.coop), 1 },
998
    { DS_NOCHK, &ud.marker, sizeof(ud.marker), 1 },
998
    { DS_NOCHK, &ud.marker, sizeof(ud.marker), 1 },
999
    { DS_NOCHK, &ud.ffire, sizeof(ud.ffire), 1 },
999
    { DS_NOCHK, &ud.ffire, sizeof(ud.ffire), 1 },
1000
    { DS_NOCHK, &ud.noexits, sizeof(ud.noexits), 1 },
1000
    { DS_NOCHK, &ud.noexits, sizeof(ud.noexits), 1 },
1001
    { DS_NOCHK, &ud.playerai, sizeof(ud.playerai), 1 },
1001
    { DS_NOCHK, &ud.playerai, sizeof(ud.playerai), 1 },
1002
    { 0, &ud.pause_on, sizeof(ud.pause_on), 1 },
1002
    { 0, &ud.pause_on, sizeof(ud.pause_on), 1 },
1003
    { DS_NOCHK, &currentboardfilename[0], BMAX_PATH, 1 },
1003
    { DS_NOCHK, &currentboardfilename[0], BMAX_PATH, 1 },
1004
//    { DS_LOADFN, (void *)&sv_postudload, 0, 1 },
1004
//    { DS_LOADFN, (void *)&sv_postudload, 0, 1 },
1005
    { 0, connectpoint2, sizeof(connectpoint2), 1 },
1005
    { 0, connectpoint2, sizeof(connectpoint2), 1 },
1006
    { 0, &randomseed, sizeof(randomseed), 1 },
1006
    { 0, &randomseed, sizeof(randomseed), 1 },
1007
    { 0, &g_globalRandom, sizeof(g_globalRandom), 1 },
1007
    { 0, &g_globalRandom, sizeof(g_globalRandom), 1 },
1008
#ifdef LUNATIC
1008
#ifdef LUNATIC
1009
    // Save game tic count for Lunatic because it is exposed to userland. See
1009
    // Save game tic count for Lunatic because it is exposed to userland. See
1010
    // test/helixspawner.lua for an example.
1010
    // test/helixspawner.lua for an example.
1011
    { 0, &g_moveThingsCount, sizeof(g_moveThingsCount), 1 },
1011
    { 0, &g_moveThingsCount, sizeof(g_moveThingsCount), 1 },
1012
#endif
1012
#endif
1013
//    { 0, &lockclock_dummy, sizeof(lockclock), 1 },
1013
//    { 0, &lockclock_dummy, sizeof(lockclock), 1 },
1014
    { DS_END, 0, 0, 0 }
1014
    { DS_END, 0, 0, 0 }
1015
};
1015
};
1016
1016
1017
#if !defined DEBUG_MAIN_ARRAYS
1017
#if !defined DEBUG_MAIN_ARRAYS
1018
# define DS_MAINAR DS_DYNAMIC
1018
# define DS_MAINAR DS_DYNAMIC
1019
#else
1019
#else
1020
# define DS_MAINAR 0
1020
# define DS_MAINAR 0
1021
#endif
1021
#endif
1022
1022
1023
static const dataspec_t svgm_secwsp[] =
1023
static const dataspec_t svgm_secwsp[] =
1024
{
1024
{
1025
    { DS_STRING, (void *)"blK:swsp", 0, 1 },
1025
    { DS_STRING, (void *)"blK:swsp", 0, 1 },
1026
    { DS_NOCHK, &numwalls, sizeof(numwalls), 1 },
1026
    { DS_NOCHK, &numwalls, sizeof(numwalls), 1 },
1027
    { DS_MAINAR|DS_CNT(numwalls), &wall, sizeof(walltype), (intptr_t)&numwalls },
1027
    { DS_MAINAR|DS_CNT(numwalls), &wall, sizeof(walltype), (intptr_t)&numwalls },
1028
    { DS_NOCHK, &numsectors, sizeof(numsectors), 1 },
1028
    { DS_NOCHK, &numsectors, sizeof(numsectors), 1 },
1029
    { DS_MAINAR|DS_CNT(numsectors), &sector, sizeof(sectortype), (intptr_t)&numsectors },
1029
    { DS_MAINAR|DS_CNT(numsectors), &sector, sizeof(sectortype), (intptr_t)&numsectors },
1030
    { DS_MAINAR, &sprite, sizeof(spritetype), MAXSPRITES },
1030
    { DS_MAINAR, &sprite, sizeof(spritetype), MAXSPRITES },
1031
#ifdef YAX_ENABLE
1031
#ifdef YAX_ENABLE
1032
    { DS_NOCHK, &numyaxbunches, sizeof(numyaxbunches), 1 },
1032
    { DS_NOCHK, &numyaxbunches, sizeof(numyaxbunches), 1 },
1033
# if !defined NEW_MAP_FORMAT
1033
# if !defined NEW_MAP_FORMAT
1034
    { DS_CNT(numsectors), yax_bunchnum, sizeof(yax_bunchnum[0]), (intptr_t)&numsectors },
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 },
1035
    { DS_CNT(numwalls), yax_nextwall, sizeof(yax_nextwall[0]), (intptr_t)&numwalls },
1036
# endif
1036
# endif
1037
    { DS_LOADFN|DS_PROTECTFN, (void *)&sv_postyaxload, 0, 1 },
1037
    { DS_LOADFN|DS_PROTECTFN, (void *)&sv_postyaxload, 0, 1 },
1038
#endif
1038
#endif
1039
    { 0, &Numsprites, sizeof(Numsprites), 1 },
1039
    { 0, &Numsprites, sizeof(Numsprites), 1 },
1040
    { 0, &tailspritefree, sizeof(tailspritefree), 1 },
1040
    { 0, &tailspritefree, sizeof(tailspritefree), 1 },
1041
    { 0, &headspritesect[0], sizeof(headspritesect[0]), MAXSECTORS+1 },
1041
    { 0, &headspritesect[0], sizeof(headspritesect[0]), MAXSECTORS+1 },
1042
    { 0, &prevspritesect[0], sizeof(prevspritesect[0]), MAXSPRITES },
1042
    { 0, &prevspritesect[0], sizeof(prevspritesect[0]), MAXSPRITES },
1043
    { 0, &nextspritesect[0], sizeof(nextspritesect[0]), MAXSPRITES },
1043
    { 0, &nextspritesect[0], sizeof(nextspritesect[0]), MAXSPRITES },
1044
    { 0, &headspritestat[0], sizeof(headspritestat[0]), MAXSTATUS+1 },
1044
    { 0, &headspritestat[0], sizeof(headspritestat[0]), MAXSTATUS+1 },
1045
    { 0, &prevspritestat[0], sizeof(prevspritestat[0]), MAXSPRITES },
1045
    { 0, &prevspritestat[0], sizeof(prevspritestat[0]), MAXSPRITES },
1046
    { 0, &nextspritestat[0], sizeof(nextspritestat[0]), MAXSPRITES },
1046
    { 0, &nextspritestat[0], sizeof(nextspritestat[0]), MAXSPRITES },
1047
#ifdef USE_OPENGL
1047
#ifdef USE_OPENGL
1048
    { DS_SAVEFN, (void *)&sv_prespriteextsave, 0, 1 },
1048
    { DS_SAVEFN, (void *)&sv_prespriteextsave, 0, 1 },
1049
#endif
1049
#endif
1050
    { DS_MAINAR, &spriteext, sizeof(spriteext_t), MAXSPRITES },
1050
    { DS_MAINAR, &spriteext, sizeof(spriteext_t), MAXSPRITES },
1051
#ifdef USE_OPENGL
1051
#ifdef USE_OPENGL
1052
    { DS_SAVEFN|DS_LOADFN, (void *)&sv_postspriteext, 0, 1 },
1052
    { DS_SAVEFN|DS_LOADFN, (void *)&sv_postspriteext, 0, 1 },
1053
#endif
1053
#endif
1054
    { 0, &DynamicTileMap[0], sizeof(DynamicTileMap[0]), MAXTILES },  // NOCHK?
1054
    { 0, &DynamicTileMap[0], sizeof(DynamicTileMap[0]), MAXTILES },  // NOCHK?
1055
    { 0, &DynamicSoundMap[0], sizeof(DynamicSoundMap[0]), MAXSOUNDS },  // NOCHK?
1055
    { 0, &DynamicSoundMap[0], sizeof(DynamicSoundMap[0]), MAXSOUNDS },  // NOCHK?
1056
    { DS_NOCHK, &g_numCyclers, sizeof(g_numCyclers), 1 },
1056
    { DS_NOCHK, &g_numCyclers, sizeof(g_numCyclers), 1 },
1057
    { DS_CNT(g_numCyclers), &cyclers[0][0], sizeof(cyclers[0]), (intptr_t)&g_numCyclers },
1057
    { DS_CNT(g_numCyclers), &cyclers[0][0], sizeof(cyclers[0]), (intptr_t)&g_numCyclers },
1058
    { DS_NOCHK, &g_numAnimWalls, sizeof(g_numAnimWalls), 1 },
1058
    { DS_NOCHK, &g_numAnimWalls, sizeof(g_numAnimWalls), 1 },
1059
    { DS_CNT(g_numAnimWalls), &animwall, sizeof(animwall[0]), (intptr_t)&g_numAnimWalls },
1059
    { DS_CNT(g_numAnimWalls), &animwall, sizeof(animwall[0]), (intptr_t)&g_numAnimWalls },
1060
    { DS_NOCHK, &g_mirrorCount, sizeof(g_mirrorCount), 1 },
1060
    { DS_NOCHK, &g_mirrorCount, sizeof(g_mirrorCount), 1 },
1061
    { DS_NOCHK, &g_mirrorWall[0], sizeof(g_mirrorWall[0]), ARRAY_SIZE(g_mirrorWall) },
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) },
1062
    { DS_NOCHK, &g_mirrorSector[0], sizeof(g_mirrorSector[0]), ARRAY_SIZE(g_mirrorSector) },
1063
// projectiles
1063
// projectiles
1064
    { 0, &SpriteProjectile[0], sizeof(projectile_t), MAXSPRITES },
1064
    { 0, &SpriteProjectile[0], sizeof(projectile_t), MAXSPRITES },
1065
    { 0, &ProjectileData[0], sizeof(projectile_t), MAXTILES },
1065
    { 0, &ProjectileData[0], sizeof(projectile_t), MAXTILES },
1066
    { 0, &everyothertime, sizeof(everyothertime), 1 },
1066
    { 0, &everyothertime, sizeof(everyothertime), 1 },
1067
    { DS_END, 0, 0, 0 }
1067
    { DS_END, 0, 0, 0 }
1068
};
1068
};
1069
1069
1070
static const dataspec_t svgm_script[] =
1070
static const dataspec_t svgm_script[] =
1071
{
1071
{
1072
    { DS_STRING, (void *)"blK:scri", 0, 1 },
1072
    { DS_STRING, (void *)"blK:scri", 0, 1 },
1073
#if !defined LUNATIC
1073
#if !defined LUNATIC
1074
    { DS_NOCHK, &g_scriptSize, sizeof(g_scriptSize), 1 },
1074
    { DS_NOCHK, &g_scriptSize, sizeof(g_scriptSize), 1 },
1075
    { DS_SAVEFN|DS_LOADFN|DS_NOCHK, (void *)&sv_calcbitptrsize, 0, 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 },
1076
    { DS_DYNAMIC|DS_CNT(savegame_bitptrsize)|DS_NOCHK, &bitptr, sizeof(bitptr[0]), (intptr_t)&savegame_bitptrsize },
1077
1077
1078
    { DS_SAVEFN|DS_NOCHK, (void *)&sv_prescriptsave_once, 0, 1 },
1078
    { DS_SAVEFN|DS_NOCHK, (void *)&sv_prescriptsave_once, 0, 1 },
1079
#endif
1079
#endif
1080
    { DS_NOCHK, &g_tile[0], sizeof(tiledata_t), MAXTILES },
1080
    { DS_NOCHK, &g_tile[0], sizeof(tiledata_t), MAXTILES },
1081
#if !defined LUNATIC
1081
#if !defined LUNATIC
1082
    { DS_LOADFN|DS_NOCHK, (void *)&sv_prescriptload_once, 0, 1 },
1082
    { DS_LOADFN|DS_NOCHK, (void *)&sv_prescriptload_once, 0, 1 },
1083
    { DS_DYNAMIC|DS_CNT(g_scriptSize)|DS_NOCHK, &script, sizeof(script[0]), (intptr_t)&g_scriptSize },
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 },
1084
//    { DS_NOCHK, &apScriptGameEvent[0], sizeof(apScriptGameEvent[0]), MAXGAMEEVENTS },
1085
    { DS_SAVEFN|DS_LOADFN|DS_NOCHK, (void *)&sv_postscript_once, 0, 1 },
1085
    { DS_SAVEFN|DS_LOADFN|DS_NOCHK, (void *)&sv_postscript_once, 0, 1 },
1086
#endif
1086
#endif
1087
    { DS_SAVEFN, (void *)&sv_preactordatasave, 0, 1 },
1087
    { DS_SAVEFN, (void *)&sv_preactordatasave, 0, 1 },
1088
    { 0, &actor[0], sizeof(actor_t), MAXSPRITES },
1088
    { 0, &actor[0], sizeof(actor_t), MAXSPRITES },
1089
    { DS_SAVEFN|DS_LOADFN, (void *)&sv_postactordata, 0, 1 },
1089
    { DS_SAVEFN|DS_LOADFN, (void *)&sv_postactordata, 0, 1 },
1090
#if defined LUNATIC
1090
#if defined LUNATIC
1091
    { DS_LOADFN|DS_NOCHK, (void *)&sv_create_lua_state, 0, 1 },
1091
    { DS_LOADFN|DS_NOCHK, (void *)&sv_create_lua_state, 0, 1 },
1092
#endif
1092
#endif
1093
    { DS_END, 0, 0, 0 }
1093
    { DS_END, 0, 0, 0 }
1094
};
1094
};
1095
1095
1096
static const dataspec_t svgm_anmisc[] =
1096
static const dataspec_t svgm_anmisc[] =
1097
{
1097
{
1098
    { DS_STRING, (void *)"blK:anms", 0, 1 },
1098
    { DS_STRING, (void *)"blK:anms", 0, 1 },
1099
    { 0, &g_animateCount, sizeof(g_animateCount), 1 },
1099
    { 0, &g_animateCount, sizeof(g_animateCount), 1 },
1100
    { 0, &animatesect[0], sizeof(animatesect[0]), MAXANIMATES },
1100
    { 0, &animatesect[0], sizeof(animatesect[0]), MAXANIMATES },
1101
    { 0, &animategoal[0], sizeof(animategoal[0]), MAXANIMATES },
1101
    { 0, &animategoal[0], sizeof(animategoal[0]), MAXANIMATES },
1102
    { 0, &animatevel[0], sizeof(animatevel[0]), MAXANIMATES },
1102
    { 0, &animatevel[0], sizeof(animatevel[0]), MAXANIMATES },
1103
    { DS_SAVEFN, (void *)&sv_preanimateptrsave, 0, 1 },
1103
    { DS_SAVEFN, (void *)&sv_preanimateptrsave, 0, 1 },
1104
    { 0, &animateptr[0], sizeof(animateptr[0]), MAXANIMATES },
1104
    { 0, &animateptr[0], sizeof(animateptr[0]), MAXANIMATES },
1105
    { DS_SAVEFN|DS_LOADFN , (void *)&sv_postanimateptr, 0, 1 },
1105
    { DS_SAVEFN|DS_LOADFN , (void *)&sv_postanimateptr, 0, 1 },
1106
    { 0, &g_curViewscreen, sizeof(g_curViewscreen), 1 },
1106
    { 0, &g_curViewscreen, sizeof(g_curViewscreen), 1 },
1107
    { 0, &msx[0], sizeof(msx[0]), ARRAY_SIZE(msx) },
1107
    { 0, &msx[0], sizeof(msx[0]), ARRAY_SIZE(msx) },
1108
    { 0, &msy[0], sizeof(msy[0]), ARRAY_SIZE(msy) },
1108
    { 0, &msy[0], sizeof(msy[0]), ARRAY_SIZE(msy) },
1109
    { 0, &g_spriteDeleteQueuePos, sizeof(g_spriteDeleteQueuePos), 1 },
1109
    { 0, &g_spriteDeleteQueuePos, sizeof(g_spriteDeleteQueuePos), 1 },
1110
    { DS_NOCHK, &g_spriteDeleteQueueSize, sizeof(g_spriteDeleteQueueSize), 1 },
1110
    { DS_NOCHK, &g_spriteDeleteQueueSize, sizeof(g_spriteDeleteQueueSize), 1 },
1111
    { DS_CNT(g_spriteDeleteQueueSize), &SpriteDeletionQueue[0], sizeof(int16_t), (intptr_t)&g_spriteDeleteQueueSize },
1111
    { DS_CNT(g_spriteDeleteQueueSize), &SpriteDeletionQueue[0], sizeof(int16_t), (intptr_t)&g_spriteDeleteQueueSize },
1112
    { 0, &show2dsector[0], sizeof(uint8_t), MAXSECTORS>>3 },
1112
    { 0, &show2dsector[0], sizeof(uint8_t), MAXSECTORS>>3 },
1113
    { DS_NOCHK, &g_numClouds, sizeof(g_numClouds), 1 },
1113
    { DS_NOCHK, &g_numClouds, sizeof(g_numClouds), 1 },
1114
    { 0, &clouds[0], sizeof(clouds), 1 },
1114
    { 0, &clouds[0], sizeof(clouds), 1 },
1115
    { 0, &cloudx[0], sizeof(cloudx), 1 },
1115
    { 0, &cloudx[0], sizeof(cloudx), 1 },
1116
    { 0, &cloudy[0], sizeof(cloudy), 1 },
1116
    { 0, &cloudy[0], sizeof(cloudy), 1 },
1117
    { 0, &g_pskyidx, sizeof(g_pskyidx), 1 },  // DS_NOCHK?
1117
    { 0, &g_pskyidx, sizeof(g_pskyidx), 1 },  // DS_NOCHK?
1118
    { 0, &g_earthquakeTime, sizeof(g_earthquakeTime), 1 },
1118
    { 0, &g_earthquakeTime, sizeof(g_earthquakeTime), 1 },
1119
1119
1120
    { DS_SAVEFN|DS_LOADFN|DS_NOCHK, (void *)sv_prequote, 0, 1 },
1120
    { DS_SAVEFN|DS_LOADFN|DS_NOCHK, (void *)sv_prequote, 0, 1 },
1121
    { DS_SAVEFN, (void *)&sv_quotesave, 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
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 },
1123
    { DS_DYNAMIC, &savegame_quotes, MAXQUOTELEN, MAXQUOTES },
1124
    { DS_LOADFN, (void *)&sv_quoteload, 0, 1 },
1124
    { DS_LOADFN, (void *)&sv_quoteload, 0, 1 },
1125
1125
1126
    { DS_NOCHK, &g_numQuoteRedefinitions, sizeof(g_numQuoteRedefinitions), 1 },
1126
    { DS_NOCHK, &g_numQuoteRedefinitions, sizeof(g_numQuoteRedefinitions), 1 },
1127
    { DS_NOCHK|DS_SAVEFN|DS_LOADFN, (void *)&sv_prequoteredef, 0, 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
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 },
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 },
1130
    { DS_NOCHK|DS_LOADFN, (void *)&sv_quoteredefload, 0, 1 },
1131
    { DS_NOCHK|DS_SAVEFN|DS_LOADFN, (void *)&sv_postquoteredef, 0, 1 },
1131
    { DS_NOCHK|DS_SAVEFN|DS_LOADFN, (void *)&sv_postquoteredef, 0, 1 },
1132
#ifdef LUNATIC
1132
#ifdef LUNATIC
1133
    { 0, g_playerWeapon, sizeof(weapondata_t), MAXPLAYERS*MAX_WEAPONS },
1133
    { 0, g_playerWeapon, sizeof(weapondata_t), MAXPLAYERS*MAX_WEAPONS },
1134
#endif
1134
#endif
1135
    { DS_SAVEFN, (void *)&sv_restsave, 0, 1 },
1135
    { DS_SAVEFN, (void *)&sv_restsave, 0, 1 },
1136
    { 0, savegame_restdata, 1, sizeof(savegame_restdata) },  // sz/cnt swapped for kdfread
1136
    { 0, savegame_restdata, 1, sizeof(savegame_restdata) },  // sz/cnt swapped for kdfread
1137
    { DS_LOADFN, (void *)&sv_restload, 0, 1 },
1137
    { DS_LOADFN, (void *)&sv_restload, 0, 1 },
1138
1138
1139
    { DS_STRING, (void *)"savegame_end", 0, 1 },
1139
    { DS_STRING, (void *)"savegame_end", 0, 1 },
1140
    { DS_END, 0, 0, 0 }
1140
    { DS_END, 0, 0, 0 }
1141
};
1141
};
1142
1142
1143
#if !defined LUNATIC
1143
#if !defined LUNATIC
1144
static dataspec_t *svgm_vars=NULL;
1144
static dataspec_t *svgm_vars=NULL;
1145
#endif
1145
#endif
1146
static uint8_t *dosaveplayer2(FILE *fil, uint8_t *mem);
1146
static uint8_t *dosaveplayer2(FILE *fil, uint8_t *mem);
1147
static int32_t doloadplayer2(int32_t fil, uint8_t **memptr);
1147
static int32_t doloadplayer2(int32_t fil, uint8_t **memptr);
1148
static void postloadplayer(int32_t savegamep);
1148
static void postloadplayer(int32_t savegamep);
1149
1149
1150
// SVGM snapshot system
1150
// SVGM snapshot system
1151
static uint32_t svsnapsiz;
1151
static uint32_t svsnapsiz;
1152
static uint8_t *svsnapshot, *svinitsnap;
1152
static uint8_t *svsnapshot, *svinitsnap;
1153
static uint32_t svdiffsiz;
1153
static uint32_t svdiffsiz;
1154
static uint8_t *svdiff;
1154
static uint8_t *svdiff;
1155
1155
1156
#include "gamedef.h"
1156
#include "gamedef.h"
1157
1157
1158
#if !defined LUNATIC
1158
#if !defined LUNATIC
1159
# define SV_SKIPMASK (/*GAMEVAR_SYSTEM|*/GAMEVAR_READONLY|GAMEVAR_INTPTR|    \
1159
# define SV_SKIPMASK (/*GAMEVAR_SYSTEM|*/GAMEVAR_READONLY|GAMEVAR_INTPTR|    \
1160
                      GAMEVAR_SHORTPTR|GAMEVAR_CHARPTR /*|GAMEVAR_NORESET*/ |GAMEVAR_SPECIAL)
1160
                      GAMEVAR_SHORTPTR|GAMEVAR_CHARPTR /*|GAMEVAR_NORESET*/ |GAMEVAR_SPECIAL)
1161
// setup gamevar data spec for snapshotting and diffing... gamevars must be loaded when called
1161
// setup gamevar data spec for snapshotting and diffing... gamevars must be loaded when called
1162
static void sv_makevarspec()
1162
static void sv_makevarspec()
1163
{
1163
{
1164
    static char *magic = "blK:vars";
1164
    static char *magic = "blK:vars";
1165
    int32_t i, j, numsavedvars=0, numsavedarrays=0, per;
1165
    int32_t i, j, numsavedvars=0, numsavedarrays=0, per;
1166
1166
1167
    for (i=0; i<g_gameVarCount; i++)
1167
    for (i=0; i<g_gameVarCount; i++)
1168
        numsavedvars += (aGameVars[i].dwFlags&SV_SKIPMASK) ? 0 : 1;
1168
        numsavedvars += (aGameVars[i].dwFlags&SV_SKIPMASK) ? 0 : 1;
1169
1169
1170
    for (i=0; i<g_gameArrayCount; i++)
1170
    for (i=0; i<g_gameArrayCount; i++)
1171
        numsavedarrays += !(aGameArrays[i].dwFlags & GAMEARRAY_READONLY);  // SYSTEM_GAMEARRAY
1171
        numsavedarrays += !(aGameArrays[i].dwFlags & GAMEARRAY_READONLY);  // SYSTEM_GAMEARRAY
1172
1172
1173
    Bfree(svgm_vars);
1173
    Bfree(svgm_vars);
1174
    svgm_vars = (dataspec_t *)Xmalloc((numsavedvars+numsavedarrays+2)*sizeof(dataspec_t));
1174
    svgm_vars = (dataspec_t *)Xmalloc((numsavedvars+numsavedarrays+2)*sizeof(dataspec_t));
1175
1175
1176
    svgm_vars[0].flags = DS_STRING;
1176
    svgm_vars[0].flags = DS_STRING;
1177
    svgm_vars[0].ptr = magic;
1177
    svgm_vars[0].ptr = magic;
1178
    svgm_vars[0].cnt = 1;
1178
    svgm_vars[0].cnt = 1;
1179
1179
1180
    j=1;
1180
    j=1;
1181
    for (i=0; i<g_gameVarCount; i++)
1181
    for (i=0; i<g_gameVarCount; i++)
1182
    {
1182
    {
1183
        if (aGameVars[i].dwFlags&SV_SKIPMASK)
1183
        if (aGameVars[i].dwFlags&SV_SKIPMASK)
1184
            continue;
1184
            continue;
1185
1185
1186
        per = aGameVars[i].dwFlags&GAMEVAR_USER_MASK;
1186
        per = aGameVars[i].dwFlags&GAMEVAR_USER_MASK;
1187
1187
1188
        svgm_vars[j].flags = 0;
1188
        svgm_vars[j].flags = 0;
1189
        svgm_vars[j].ptr = (per==0) ? &aGameVars[i].val.lValue : aGameVars[i].val.plValues;
1189
        svgm_vars[j].ptr = (per==0) ? &aGameVars[i].val.lValue : aGameVars[i].val.plValues;
1190
        svgm_vars[j].size = sizeof(intptr_t);
1190
        svgm_vars[j].size = sizeof(intptr_t);
1191
        svgm_vars[j].cnt = (per==0) ? 1 : (per==GAMEVAR_PERPLAYER ? MAXPLAYERS : MAXSPRITES);
1191
        svgm_vars[j].cnt = (per==0) ? 1 : (per==GAMEVAR_PERPLAYER ? MAXPLAYERS : MAXSPRITES);
1192
        j++;
1192
        j++;
1193
    }
1193
    }
1194
1194
1195
    for (i=0; i<g_gameArrayCount; i++)
1195
    for (i=0; i<g_gameArrayCount; i++)
1196
    {
1196
    {
1197
        // We must not update read-only SYSTEM_GAMEARRAY gamearrays: besides
1197
        // We must not update read-only SYSTEM_GAMEARRAY gamearrays: besides
1198
        // being questionable by itself, sizeof(...) may be e.g. 4 whereas the
1198
        // being questionable by itself, sizeof(...) may be e.g. 4 whereas the
1199
        // actual element type is int16_t (such as tilesizx[]/tilesizy[]).
1199
        // actual element type is int16_t (such as tilesizx[]/tilesizy[]).
1200
        if (aGameArrays[i].dwFlags & GAMEARRAY_READONLY)
1200
        if (aGameArrays[i].dwFlags & GAMEARRAY_READONLY)
1201
            continue;
1201
            continue;
1202
1202
1203
        svgm_vars[j].flags = 0;
1203
        svgm_vars[j].flags = 0;
1204
        svgm_vars[j].ptr = aGameArrays[i].plValues;
1204
        svgm_vars[j].ptr = aGameArrays[i].plValues;
1205
        svgm_vars[j].size = sizeof(aGameArrays[0].plValues[0]);
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
1206
        svgm_vars[j].cnt = aGameArrays[i].size;  // assumed constant throughout demo, i.e. no RESIZEARRAY
1207
        j++;
1207
        j++;
1208
    }
1208
    }
1209
1209
1210
    svgm_vars[j].flags = DS_END;
1210
    svgm_vars[j].flags = DS_END;
1211
    svgm_vars[j].ptr = NULL;
1211
    svgm_vars[j].ptr = NULL;
1212
    svgm_vars[j].size = svgm_vars[j].cnt = 0;
1212
    svgm_vars[j].size = svgm_vars[j].cnt = 0;
1213
}
1213
}
1214
#endif
1214
#endif
1215
1215
1216
void sv_freemem()
1216
void sv_freemem()
1217
{
1217
{
1218
    DO_FREE_AND_NULL(svsnapshot);
1218
    DO_FREE_AND_NULL(svsnapshot);
1219
    DO_FREE_AND_NULL(svinitsnap);
1219
    DO_FREE_AND_NULL(svinitsnap);
1220
    DO_FREE_AND_NULL(svdiff);
1220
    DO_FREE_AND_NULL(svdiff);
1221
}
1221
}
1222
1222
1223
static void SV_AllocSnap(int32_t allocinit)
1223
static void SV_AllocSnap(int32_t allocinit)
1224
{
1224
{
1225
    sv_freemem();
1225
    sv_freemem();
1226
1226
1227
    svsnapshot = (uint8_t *)Xmalloc(svsnapsiz);
1227
    svsnapshot = (uint8_t *)Xmalloc(svsnapsiz);
1228
    if (allocinit)
1228
    if (allocinit)
1229
        svinitsnap = (uint8_t *)Xmalloc(svsnapsiz);
1229
        svinitsnap = (uint8_t *)Xmalloc(svsnapsiz);
1230
    svdiffsiz = svsnapsiz;  // theoretically it's less than could be needed in the worst case, but practically it's overkill
1230
    svdiffsiz = svsnapsiz;  // theoretically it's less than could be needed in the worst case, but practically it's overkill
1231
    svdiff = (uint8_t *)Xmalloc(svdiffsiz);
1231
    svdiff = (uint8_t *)Xmalloc(svdiffsiz);
1232
}
1232
}
1233
1233
1234
EDUKE32_STATIC_ASSERT(sizeof(savehead_t) == SAVEHEAD_SIZE);
1234
EDUKE32_STATIC_ASSERT(sizeof(savehead_t) == SAVEHEAD_SIZE);
1235
1235
1236
// make snapshot only if spot < 0 (demo)
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)
1237
int32_t sv_saveandmakesnapshot(FILE *fil, int8_t spot, int8_t recdiffsp, int8_t diffcompress, int8_t synccompress)
1238
{
1238
{
1239
    savehead_t h;
1239
    savehead_t h;
1240
1240
1241
    // set a few savegame system globals
1241
    // set a few savegame system globals
1242
    savegame_comprthres = SV_DEFAULTCOMPRTHRES;
1242
    savegame_comprthres = SV_DEFAULTCOMPRTHRES;
1243
    savegame_diffcompress = diffcompress;
1243
    savegame_diffcompress = diffcompress;
1244
1244
1245
    // calculate total snapshot size
1245
    // calculate total snapshot size
1246
#if !defined LUNATIC
1246
#if !defined LUNATIC
1247
    sv_makevarspec();
1247
    sv_makevarspec();
1248
    svsnapsiz = calcsz(svgm_vars);
1248
    svsnapsiz = calcsz(svgm_vars);
1249
#else
1249
#else
1250
    svsnapsiz = 0;
1250
    svsnapsiz = 0;
1251
#endif
1251
#endif
1252
    svsnapsiz += calcsz(svgm_udnetw) + calcsz(svgm_secwsp) + calcsz(svgm_script) + calcsz(svgm_anmisc);
1252
    svsnapsiz += calcsz(svgm_udnetw) + calcsz(svgm_secwsp) + calcsz(svgm_script) + calcsz(svgm_anmisc);
1253
1253
1254
1254
1255
    // create header
1255
    // create header
1256
    Bmemcpy(h.headerstr, "EDuke32SAVE", 11);
1256
    Bmemcpy(h.headerstr, "EDuke32SAVE", 11);
1257
    h.majorver = SV_MAJOR_VER;
1257
    h.majorver = SV_MAJOR_VER;
1258
    h.minorver = SV_MINOR_VER;
1258
    h.minorver = SV_MINOR_VER;
1259
    h.ptrsize = sizeof(intptr_t);
1259
    h.ptrsize = sizeof(intptr_t);
1260
    h.bytever = BYTEVERSION;
1260
    h.bytever = BYTEVERSION;
1261
1261
1262
    h.comprthres = savegame_comprthres;
1262
    h.comprthres = savegame_comprthres;
1263
    h.recdiffsp = recdiffsp;
1263
    h.recdiffsp = recdiffsp;
1264
    h.diffcompress = savegame_diffcompress;
1264
    h.diffcompress = savegame_diffcompress;
1265
    h.synccompress = synccompress;
1265
    h.synccompress = synccompress;
1266
1266
1267
    h.reccnt = 0;
1267
    h.reccnt = 0;
1268
    h.snapsiz = svsnapsiz;
1268
    h.snapsiz = svsnapsiz;
1269
1269
1270
    // the following is kinda redundant, but we save it here to be able to quickly fetch
1270
    // the following is kinda redundant, but we save it here to be able to quickly fetch
1271
    // it in a savegame header read
1271
    // it in a savegame header read
1272
    h.numplayers = ud.multimode;
1272
    h.numplayers = ud.multimode;
1273
    h.volnum = ud.volume_number;
1273
    h.volnum = ud.volume_number;
1274
    h.levnum = ud.level_number;
1274
    h.levnum = ud.level_number;
1275
    h.skill = ud.player_skill;
1275
    h.skill = ud.player_skill;
1276
1276
1277
    if (currentboardfilename[0] != 0 && ud.level_number == 7 && ud.volume_number == 0)
1277
    if (currentboardfilename[0] != 0 && ud.level_number == 7 && ud.volume_number == 0)
1278
    {
1278
    {
1279
        const uint32_t BSZ = sizeof(h.boardfn);
1279
        const uint32_t BSZ = sizeof(h.boardfn);
1280
        EDUKE32_STATIC_ASSERT(BSZ == sizeof(currentboardfilename));
1280
        EDUKE32_STATIC_ASSERT(BSZ == sizeof(currentboardfilename));
1281
1281
1282
        Bstrncpy(h.boardfn, currentboardfilename, BSZ);
1282
        Bstrncpy(h.boardfn, currentboardfilename, BSZ);
1283
1283
1284
        // Shoehorn currently playing music into last bytes of board name buffer.
1284
        // Shoehorn currently playing music into last bytes of board name buffer.
1285
        // SAVEGAME_MUSIC.
1285
        // SAVEGAME_MUSIC.
1286
        if (g_musicIndex != (0*MAXLEVELS+7) && Bstrlen(h.boardfn) < BSZ-2)
1286
        if (g_musicIndex != (0*MAXLEVELS+7) && Bstrlen(h.boardfn) < BSZ-2)
1287
        {
1287
        {
1288
            h.boardfn[BSZ-2] = g_musicIndex / MAXLEVELS;
1288
            h.boardfn[BSZ-2] = g_musicIndex / MAXLEVELS;
1289
            h.boardfn[BSZ-1] = g_musicIndex % MAXLEVELS;
1289
            h.boardfn[BSZ-1] = g_musicIndex % MAXLEVELS;
1290
        }
1290
        }
1291
    }
1291
    }
1292
1292
1293
    if ((unsigned)spot < MAXSAVEGAMES)
1293
    if ((unsigned)spot < MAXSAVEGAMES)
1294
    {
1294
    {
1295
        // savegame
1295
        // savegame
1296
        Bstrncpyz(h.savename, ud.savegame[spot], sizeof(h.savename));
1296
        Bstrncpyz(h.savename, ud.savegame[spot], sizeof(h.savename));
1297
#ifdef __ANDROID__
1297
#ifdef __ANDROID__
1298
        Bstrncpyz(h.volname, EpisodeNames[ud.volume_number], sizeof(h.volname));
1298
        Bstrncpyz(h.volname, EpisodeNames[ud.volume_number], sizeof(h.volname));
1299
        Bstrncpyz(h.skillname, SkillNames[ud.player_skill], sizeof(h.skillname));
1299
        Bstrncpyz(h.skillname, SkillNames[ud.player_skill], sizeof(h.skillname));
1300
#endif
1300
#endif
1301
    }
1301
    }
1302
    else
1302
    else
1303
    {
1303
    {
1304
        // demo
1304
        // demo
1305
1305
1306
        const time_t t=time(NULL);
1306
        const time_t t=time(NULL);
1307
        struct tm *st;
1307
        struct tm *st;
1308
1308
1309
        Bstrncpyz(h.savename, "EDuke32 demo", sizeof(h.savename));
1309
        Bstrncpyz(h.savename, "EDuke32 demo", sizeof(h.savename));
1310
        if (t>=0 && (st = localtime(&t)))
1310
        if (t>=0 && (st = localtime(&t)))
1311
            Bsnprintf(h.savename, sizeof(h.savename), "Demo %04d%02d%02d %s",
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);
1312
                      st->tm_year+1900, st->tm_mon+1, st->tm_mday, s_buildRev);
1313
    }
1313
    }
1314
1314
1315
1315
1316
    // write header
1316
    // write header
1317
    fwrite(&h, sizeof(savehead_t), 1, fil);
1317
    fwrite(&h, sizeof(savehead_t), 1, fil);
1318
1318
1319
    // for savegames, the file offset after the screenshot goes here;
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
1320
    // for demos, we keep it 0 to signify that we didn't save one
1321
    fwrite("\0\0\0\0", 4, 1, fil);
1321
    fwrite("\0\0\0\0", 4, 1, fil);
1322
    if (spot >= 0 && waloff[TILE_SAVESHOT])
1322
    if (spot >= 0 && waloff[TILE_SAVESHOT])
1323
    {
1323
    {
1324
        int32_t ofs;
1324
        int32_t ofs;
1325
1325
1326
        // write the screenshot compressed
1326
        // write the screenshot compressed
1327
        dfwrite((char *)waloff[TILE_SAVESHOT], 320, 200, fil);
1327
        dfwrite((char *)waloff[TILE_SAVESHOT], 320, 200, fil);
1328
1328
1329
        // write the current file offset right after the header
1329
        // write the current file offset right after the header
1330
        ofs = ftell(fil);
1330
        ofs = ftell(fil);
1331
        fseek(fil, sizeof(savehead_t), SEEK_SET);
1331
        fseek(fil, sizeof(savehead_t), SEEK_SET);
1332
        fwrite(&ofs, 4, 1, fil);
1332
        fwrite(&ofs, 4, 1, fil);
1333
        fseek(fil, ofs, SEEK_SET);
1333
        fseek(fil, ofs, SEEK_SET);
1334
    }
1334
    }
1335
1335
1336
#ifdef DEBUGGINGAIDS
1336
#ifdef DEBUGGINGAIDS
1337
    OSD_Printf("sv_saveandmakesnapshot: snapshot size: %d bytes.\n", svsnapsiz);
1337
    OSD_Printf("sv_saveandmakesnapshot: snapshot size: %d bytes.\n", svsnapsiz);
1338
#endif
1338
#endif
1339
1339
1340
    if (spot >= 0)
1340
    if (spot >= 0)
1341
    {
1341
    {
1342
        // savegame
1342
        // savegame
1343
        dosaveplayer2(fil, NULL);
1343
        dosaveplayer2(fil, NULL);
1344
#ifdef LUNATIC
1344
#ifdef LUNATIC
1345
        if (!g_savedOK)
1345
        if (!g_savedOK)
1346
        {
1346
        {
1347
            OSD_Printf("sv_saveandmakesnapshot: failed serializing Lunatic gamevar \"%s\".\n",
1347
            OSD_Printf("sv_saveandmakesnapshot: failed serializing Lunatic gamevar \"%s\".\n",
1348
                       g_failedVarname);
1348
                       g_failedVarname);
1349
            g_failedVarname = NULL;
1349
            g_failedVarname = NULL;
1350
            return 1;
1350
            return 1;
1351
        }
1351
        }
1352
#endif
1352
#endif
1353
    }
1353
    }
1354
    else
1354
    else
1355
    {
1355
    {
1356
        uint8_t *p;
1356
        uint8_t *p;
1357
1357
1358
        // demo
1358
        // demo
1359
        SV_AllocSnap(0);
1359
        SV_AllocSnap(0);
1360
1360
1361
        p = dosaveplayer2(fil, svsnapshot);
1361
        p = dosaveplayer2(fil, svsnapshot);
1362
        if (p != svsnapshot+svsnapsiz)
1362
        if (p != svsnapshot+svsnapsiz)
1363
        {
1363
        {
1364
            OSD_Printf("sv_saveandmakesnapshot: ptr-(snapshot end)=%d!\n", (int32_t)(p-(svsnapshot+svsnapsiz)));
1364
            OSD_Printf("sv_saveandmakesnapshot: ptr-(snapshot end)=%d!\n", (int32_t)(p-(svsnapshot+svsnapsiz)));
1365
            return 1;
1365
            return 1;
1366
        }
1366
        }
1367
    }
1367
    }
1368
1368
1369
    g_oldverSavegame[spot] = 0;
1369
    g_oldverSavegame[spot] = 0;
1370
1370
1371
    return 0;
1371
    return 0;
1372
}
1372
}
1373
1373
1374
EDUKE32_STATIC_ASSERT(sizeof(savehead_t) == SAVEHEAD_SIZE);
1374
EDUKE32_STATIC_ASSERT(sizeof(savehead_t) == SAVEHEAD_SIZE);
1375
1375
1376
// if file is not an EDuke32 savegame/demo, h->headerstr will be all zeros
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)
1377
int32_t sv_loadheader(int32_t fil, int32_t spot, savehead_t *h)
1378
{
1378
{
1379
    int32_t havedemo = (spot < 0);
1379
    int32_t havedemo = (spot < 0);
1380
1380
1381
    if (kread(fil, h, sizeof(savehead_t)) != sizeof(savehead_t))
1381
    if (kread(fil, h, sizeof(savehead_t)) != sizeof(savehead_t))
1382
    {
1382
    {
1383
        OSD_Printf("%s %d header corrupt.\n", havedemo ? "Demo":"Savegame", havedemo ? -spot : spot);
1383
        OSD_Printf("%s %d header corrupt.\n", havedemo ? "Demo":"Savegame", havedemo ? -spot : spot);
1384
        Bmemset(h->headerstr, 0, sizeof(h->headerstr));
1384
        Bmemset(h->headerstr, 0, sizeof(h->headerstr));
1385
        return -1;
1385
        return -1;
1386
    }
1386
    }
1387
1387
1388
    if (Bmemcmp(h->headerstr, "EDuke32SAVE", 11))
1388
    if (Bmemcmp(h->headerstr, "EDuke32SAVE", 11))
1389
    {
1389
    {
1390
        h->headerstr[sizeof(h->headerstr)-1] = 0;
1390
        h->headerstr[sizeof(h->headerstr)-1] = 0;
1391
        OSD_Printf("%s %d header reads \"%s\", expected \"EDuke32SAVE\".\n",
1391
        OSD_Printf("%s %d header reads \"%s\", expected \"EDuke32SAVE\".\n",
1392
                   havedemo ? "Demo":"Savegame", havedemo ? -spot : spot, h->headerstr);
1392
                   havedemo ? "Demo":"Savegame", havedemo ? -spot : spot, h->headerstr);
1393
        Bmemset(h->headerstr, 0, sizeof(h->headerstr));
1393
        Bmemset(h->headerstr, 0, sizeof(h->headerstr));
1394
        return 1;
1394
        return 1;
1395
    }
1395
    }
1396
1396
1397
    if (h->majorver != SV_MAJOR_VER || h->minorver != SV_MINOR_VER || h->bytever != BYTEVERSION)
1397
    if (h->majorver != SV_MAJOR_VER || h->minorver != SV_MINOR_VER || h->bytever != BYTEVERSION)
1398
    {
1398
    {
1399
        if (havedemo)
1399
        if (havedemo)
1400
            OSD_Printf("Incompatible demo version. Expected %d.%d.%d, found %d.%d.%d\n",
1400
            OSD_Printf("Incompatible demo version. Expected %d.%d.%d, found %d.%d.%d\n",
1401
                       SV_MAJOR_VER, SV_MINOR_VER, BYTEVERSION,
1401
                       SV_MAJOR_VER, SV_MINOR_VER, BYTEVERSION,
1402
                       h->majorver, h->minorver, h->bytever);
1402
                       h->majorver, h->minorver, h->bytever);
1403
        return 2;
1403
        return 2;
1404
    }
1404
    }
1405
1405
1406
    if (h->ptrsize != sizeof(intptr_t))
1406
    if (h->ptrsize != sizeof(intptr_t))
1407
    {
1407
    {
1408
        if (havedemo)
1408
        if (havedemo)
1409
            OSD_Printf("Demo incompatible. Expected pointer size %d, found %d\n",
1409
            OSD_Printf("Demo incompatible. Expected pointer size %d, found %d\n",
1410
                       (int32_t)sizeof(intptr_t), h->ptrsize);
1410
                       (int32_t)sizeof(intptr_t), h->ptrsize);
1411
        return 3;
1411
        return 3;
1412
    }
1412
    }
1413
1413
1414
    return 0;
1414
    return 0;
1415
}
1415
}
1416
1416
1417
int32_t sv_loadsnapshot(int32_t fil, int32_t spot, savehead_t *h)
1417
int32_t sv_loadsnapshot(int32_t fil, int32_t spot, savehead_t *h)
1418
{
1418
{
1419
    uint8_t *p;
1419
    uint8_t *p;
1420
    int32_t i;
1420
    int32_t i;
1421
1421
1422
    if (spot < 0)
1422
    if (spot < 0)
1423
    {
1423
    {
1424
        // demo
1424
        // demo
1425
        i = sv_loadheader(fil, spot, h);
1425
        i = sv_loadheader(fil, spot, h);
1426
        if (i)
1426
        if (i)
1427
            return i;
1427
            return i;
1428
1428
1429
        // Used to be in doloadplayer2(), now redundant for savegames since
1429
        // Used to be in doloadplayer2(), now redundant for savegames since
1430
        // we checked before. Multiplayer demos need still to be taken care of.
1430
        // we checked before. Multiplayer demos need still to be taken care of.
1431
        if (h->numplayers != numplayers)
1431
        if (h->numplayers != numplayers)
1432
            return 9;
1432
            return 9;
1433
    }
1433
    }
1434
    // else (if savegame), we just read the header and are now at offset sizeof(savehead_t)
1434
    // else (if savegame), we just read the header and are now at offset sizeof(savehead_t)
1435
1435
1436
#ifdef DEBUGGINGAIDS
1436
#ifdef DEBUGGINGAIDS
1437
    OSD_Printf("sv_loadsnapshot: snapshot size: %d bytes.\n", h->snapsiz);
1437
    OSD_Printf("sv_loadsnapshot: snapshot size: %d bytes.\n", h->snapsiz);
1438
#endif
1438
#endif
1439
1439
1440
    if (kread(fil, &i, 4) != 4)
1440
    if (kread(fil, &i, 4) != 4)
1441
    {
1441
    {
1442
        OSD_Printf("sv_snapshot: couldn't read 4 bytes after header.\n");
1442
        OSD_Printf("sv_snapshot: couldn't read 4 bytes after header.\n");
1443
        return 7;
1443
        return 7;
1444
    }
1444
    }
1445
    if (i > 0)
1445
    if (i > 0)
1446
    {
1446
    {
1447
        if (klseek(fil, i, SEEK_SET) != i)
1447
        if (klseek(fil, i, SEEK_SET) != i)
1448
        {
1448
        {
1449
            OSD_Printf("sv_snapshot: failed skipping over the screenshot.\n");
1449
            OSD_Printf("sv_snapshot: failed skipping over the screenshot.\n");
1450
            return 8;
1450
            return 8;
1451
        }
1451
        }
1452
    }
1452
    }
1453
1453
1454
    savegame_comprthres = h->comprthres;
1454
    savegame_comprthres = h->comprthres;
1455
1455
1456
    if (spot >= 0)
1456
    if (spot >= 0)
1457
    {
1457
    {
1458
        // savegame
1458
        // savegame
1459
        i = doloadplayer2(fil, NULL);
1459
        i = doloadplayer2(fil, NULL);
1460
        if (i)
1460
        if (i)
1461
        {
1461
        {
1462
            OSD_Printf("sv_loadsnapshot: doloadplayer2() returned %d.\n", i);
1462
            OSD_Printf("sv_loadsnapshot: doloadplayer2() returned %d.\n", i);
1463
            return 5;
1463
            return 5;
1464
        }
1464
        }
1465
    }
1465
    }
1466
    else
1466
    else
1467
    {
1467
    {
1468
        // demo
1468
        // demo
1469
        savegame_diffcompress = h->diffcompress;
1469
        savegame_diffcompress = h->diffcompress;
1470
1470
1471
        svsnapsiz = h->snapsiz;
1471
        svsnapsiz = h->snapsiz;
1472
1472
1473
        SV_AllocSnap(1);
1473
        SV_AllocSnap(1);
1474
1474
1475
        p = svsnapshot;
1475
        p = svsnapshot;
1476
        i = doloadplayer2(fil, &p);
1476
        i = doloadplayer2(fil, &p);
1477
        if (i)
1477
        if (i)
1478
        {
1478
        {
1479
            OSD_Printf("sv_loadsnapshot: doloadplayer2() returned %d.\n", i);
1479
            OSD_Printf("sv_loadsnapshot: doloadplayer2() returned %d.\n", i);
1480
            sv_freemem();
1480
            sv_freemem();
1481
            return 5;
1481
            return 5;
1482
        }
1482
        }
1483
1483
1484
        if (p != svsnapshot+svsnapsiz)
1484
        if (p != svsnapshot+svsnapsiz)
1485
        {
1485
        {
1486
            OSD_Printf("sv_loadsnapshot: internal error: p-(snapshot end)=%d!\n",
1486
            OSD_Printf("sv_loadsnapshot: internal error: p-(snapshot end)=%d!\n",
1487
                       (int32_t)(p-(svsnapshot+svsnapsiz)));
1487
                       (int32_t)(p-(svsnapshot+svsnapsiz)));
1488
            sv_freemem();
1488
            sv_freemem();
1489
            return 6;
1489
            return 6;
1490
        }
1490
        }
1491
1491
1492
        Bmemcpy(svinitsnap, svsnapshot, svsnapsiz);
1492
        Bmemcpy(svinitsnap, svsnapshot, svsnapsiz);
1493
    }
1493
    }
1494
1494
1495
    postloadplayer((spot >= 0));
1495
    postloadplayer((spot >= 0));
1496
1496
1497
    return 0;
1497
    return 0;
1498
}
1498
}
1499
1499
1500
1500
1501
uint32_t sv_writediff(FILE *fil)
1501
uint32_t sv_writediff(FILE *fil)
1502
{
1502
{
1503
    uint8_t *p=svsnapshot, *d=svdiff;
1503
    uint8_t *p=svsnapshot, *d=svdiff;
1504
    uint32_t diffsiz;
1504
    uint32_t diffsiz;
1505
1505
1506
    cmpspecdata(svgm_udnetw, &p, &d);
1506
    cmpspecdata(svgm_udnetw, &p, &d);
1507
    cmpspecdata(svgm_secwsp, &p, &d);
1507
    cmpspecdata(svgm_secwsp, &p, &d);
1508
    cmpspecdata(svgm_script, &p, &d);
1508
    cmpspecdata(svgm_script, &p, &d);
1509
    cmpspecdata(svgm_anmisc, &p, &d);
1509
    cmpspecdata(svgm_anmisc, &p, &d);
1510
#if !defined LUNATIC
1510
#if !defined LUNATIC
1511
    cmpspecdata(svgm_vars, &p, &d);
1511
    cmpspecdata(svgm_vars, &p, &d);
1512
#endif
1512
#endif
1513
1513
1514
    if (p != svsnapshot+svsnapsiz)
1514
    if (p != svsnapshot+svsnapsiz)
1515
        OSD_Printf("sv_writediff: dump+siz=%p, p=%p!\n", svsnapshot+svsnapsiz, p);
1515
        OSD_Printf("sv_writediff: dump+siz=%p, p=%p!\n", svsnapshot+svsnapsiz, p);
1516
1516
1517
    diffsiz = d-svdiff;
1517
    diffsiz = d-svdiff;
1518
1518
1519
    fwrite("dIfF",4,1,fil);
1519
    fwrite("dIfF",4,1,fil);
1520
    fwrite(&diffsiz, sizeof(diffsiz), 1, fil);
1520
    fwrite(&diffsiz, sizeof(diffsiz), 1, fil);
1521
    if (savegame_diffcompress)
1521
    if (savegame_diffcompress)
1522
        dfwrite(svdiff, 1, diffsiz, fil);  // cnt and sz swapped
1522
        dfwrite(svdiff, 1, diffsiz, fil);  // cnt and sz swapped
1523
    else
1523
    else
1524
        fwrite(svdiff, 1, diffsiz, fil);
1524
        fwrite(svdiff, 1, diffsiz, fil);
1525
1525
1526
    return diffsiz;
1526
    return diffsiz;
1527
}
1527
}
1528
1528
1529
int32_t sv_readdiff(int32_t fil)
1529
int32_t sv_readdiff(int32_t fil)
1530
{
1530
{
1531
    uint8_t *p=svsnapshot, *d=svdiff, i=0; //, tbuf[4];
1531
    uint8_t *p=svsnapshot, *d=svdiff, i=0; //, tbuf[4];
1532
    int32_t diffsiz;
1532
    int32_t diffsiz;
1533
1533
1534
#if 0  // handled by the caller
1534
#if 0  // handled by the caller
1535
    if (kread(fil, tbuf, 4)!=4)
1535
    if (kread(fil, tbuf, 4)!=4)
1536
        return -1;
1536
        return -1;
1537
    if (Bmemcmp(tbuf, "dIfF", 4))
1537
    if (Bmemcmp(tbuf, "dIfF", 4))
1538
        return 4;
1538
        return 4;
1539
#endif
1539
#endif
1540
    if (kread(fil, &diffsiz, sizeof(uint32_t))!=sizeof(uint32_t))
1540
    if (kread(fil, &diffsiz, sizeof(uint32_t))!=sizeof(uint32_t))
1541
        return -1;
1541
        return -1;
1542
    if (savegame_diffcompress)
1542
    if (savegame_diffcompress)
1543
    {
1543
    {
1544
        if (kdfread(svdiff, 1, diffsiz, fil) != diffsiz)  // cnt and sz swapped
1544
        if (kdfread(svdiff, 1, diffsiz, fil) != diffsiz)  // cnt and sz swapped
1545
            return -2;
1545
            return -2;
1546
    }
1546
    }
1547
    else
1547
    else
1548
    {
1548
    {
1549
        if (kread(fil, svdiff, diffsiz) != diffsiz)
1549
        if (kread(fil, svdiff, diffsiz) != diffsiz)
1550
            return -2;
1550
            return -2;
1551
    }
1551
    }
1552
1552
1553
    if (applydiff(svgm_udnetw, &p, &d)) return -3;
1553
    if (applydiff(svgm_udnetw, &p, &d)) return -3;
1554
    if (applydiff(svgm_secwsp, &p, &d)) return -4;
1554
    if (applydiff(svgm_secwsp, &p, &d)) return -4;
1555
    if (applydiff(svgm_script, &p, &d)) return -5;
1555
    if (applydiff(svgm_script, &p, &d)) return -5;
1556
    if (applydiff(svgm_anmisc, &p, &d)) return -6;
1556
    if (applydiff(svgm_anmisc, &p, &d)) return -6;
1557
#if !defined LUNATIC
1557
#if !defined LUNATIC
1558
    if (applydiff(svgm_vars, &p, &d)) return -7;
1558
    if (applydiff(svgm_vars, &p, &d)) return -7;
1559
#endif
1559
#endif
1560
1560
1561
    if (p!=svsnapshot+svsnapsiz)
1561
    if (p!=svsnapshot+svsnapsiz)
1562
        i|=1;
1562
        i|=1;
1563
    if (d!=svdiff+diffsiz)
1563
    if (d!=svdiff+diffsiz)
1564
        i|=2;
1564
        i|=2;
1565
    if (i)
1565
    if (i)
1566
        OSD_Printf("sv_readdiff: p=%p, svsnapshot+svsnapsiz=%p; d=%p, svdiff+diffsiz=%p",
1566
        OSD_Printf("sv_readdiff: p=%p, svsnapshot+svsnapsiz=%p; d=%p, svdiff+diffsiz=%p",
1567
                   p, svsnapshot+svsnapsiz, d, svdiff+diffsiz);
1567
                   p, svsnapshot+svsnapsiz, d, svdiff+diffsiz);
1568
    return i;
1568
    return i;
1569
}
1569
}
1570
1570
1571
// SVGM data description
1571
// SVGM data description
1572
static void sv_postudload()
1572
static void sv_postudload()
1573
{
1573
{
1574
//    Bmemcpy(&boardfilename[0], &currentboardfilename[0], BMAX_PATH);  // DON'T do this in demos!
1574
//    Bmemcpy(&boardfilename[0], &currentboardfilename[0], BMAX_PATH);  // DON'T do this in demos!
1575
#if 1
1575
#if 1
1576
    ud.m_level_number = ud.level_number;
1576
    ud.m_level_number = ud.level_number;
1577
    ud.m_volume_number = ud.volume_number;
1577
    ud.m_volume_number = ud.volume_number;
1578
    ud.m_player_skill = ud.player_skill;
1578
    ud.m_player_skill = ud.player_skill;
1579
    ud.m_respawn_monsters = ud.respawn_monsters;
1579
    ud.m_respawn_monsters = ud.respawn_monsters;
1580
    ud.m_respawn_items = ud.respawn_items;
1580
    ud.m_respawn_items = ud.respawn_items;
1581
    ud.m_respawn_inventory = ud.respawn_inventory;
1581
    ud.m_respawn_inventory = ud.respawn_inventory;
1582
    ud.m_monsters_off = ud.monsters_off;
1582
    ud.m_monsters_off = ud.monsters_off;
1583
    ud.m_coop = ud.coop;
1583
    ud.m_coop = ud.coop;
1584
    ud.m_marker = ud.marker;
1584
    ud.m_marker = ud.marker;
1585
    ud.m_ffire = ud.ffire;
1585
    ud.m_ffire = ud.ffire;
1586
    ud.m_noexits = ud.noexits;
1586
    ud.m_noexits = ud.noexits;
1587
#endif
1587
#endif
1588
}
1588
}
1589
//static int32_t lockclock_dummy;
1589
//static int32_t lockclock_dummy;
1590
1590
1591
#ifdef USE_OPENGL
1591
#ifdef USE_OPENGL
1592
static void sv_prespriteextsave()
1592
static void sv_prespriteextsave()
1593
{
1593
{
1594
    int32_t i;
1594
    int32_t i;
1595
    for (i=0; i<MAXSPRITES; i++)
1595
    for (i=0; i<MAXSPRITES; i++)
1596
        if (spriteext[i].mdanimtims)
1596
        if (spriteext[i].mdanimtims)
1597
        {
1597
        {
1598
            spriteext[i].mdanimtims -= mdtims;
1598
            spriteext[i].mdanimtims -= mdtims;
1599
            if (spriteext[i].mdanimtims==0)
1599
            if (spriteext[i].mdanimtims==0)
1600
                spriteext[i].mdanimtims++;
1600
                spriteext[i].mdanimtims++;
1601
        }
1601
        }
1602
}
1602
}
1603
static void sv_postspriteext()
1603
static void sv_postspriteext()
1604
{
1604
{
1605
    int32_t i;
1605
    int32_t i;
1606
    for (i=0; i<MAXSPRITES; i++)
1606
    for (i=0; i<MAXSPRITES; i++)
1607
        if (spriteext[i].mdanimtims)
1607
        if (spriteext[i].mdanimtims)
1608
            spriteext[i].mdanimtims += mdtims;
1608
            spriteext[i].mdanimtims += mdtims;
1609
}
1609
}
1610
#endif
1610
#endif
1611
1611
1612
#ifdef YAX_ENABLE
1612
#ifdef YAX_ENABLE
1613
void sv_postyaxload(void)
1613
void sv_postyaxload(void)
1614
{
1614
{
1615
    yax_update(numyaxbunches>0 ? 2 : 1);
1615
    yax_update(numyaxbunches>0 ? 2 : 1);
1616
}
1616
}
1617
#endif
1617
#endif
1618
1618
1619
#if !defined LUNATIC
1619
#if !defined LUNATIC
1620
static void sv_calcbitptrsize()
1620
static void sv_calcbitptrsize()
1621
{
1621
{
1622
    savegame_bitptrsize = (g_scriptSize+7)>>3;
1622
    savegame_bitptrsize = (g_scriptSize+7)>>3;
1623
}
1623
}
1624
static void sv_prescriptsave_once()
1624
static void sv_prescriptsave_once()
1625
{
1625
{
1626
    int32_t i;
1626
    int32_t i;
1627
    for (i=0; i<g_scriptSize; i++)
1627
    for (i=0; i<g_scriptSize; i++)
1628
        if (bitptr[i>>3]&(BITPTR_POINTER<<(i&7)))
1628
        if (bitptr[i>>3]&(BITPTR_POINTER<<(i&7)))
1629
            script[i] = (intptr_t *)script[i] - script;
1629
            script[i] = (intptr_t *)script[i] - script;
1630
1630
1631
   G_Util_PtrToIdx2(&g_tile[0].execPtr, MAXTILES, sizeof(tiledata_t), script, P2I_FWD_NON0);
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);
1632
   G_Util_PtrToIdx2(&g_tile[0].loadPtr, MAXTILES, sizeof(tiledata_t), script, P2I_FWD_NON0);
1633
}
1633
}
1634
static void sv_prescriptload_once()
1634
static void sv_prescriptload_once()
1635
{
1635
{
1636
    if (script)
1636
    if (script)
1637
        Bfree(script);
1637
        Bfree(script);
1638
    script = (intptr_t *)Xmalloc(g_scriptSize * sizeof(script[0]));
1638
    script = (intptr_t *)Xmalloc(g_scriptSize * sizeof(script[0]));
1639
}
1639
}
1640
static void sv_postscript_once()
1640
static void sv_postscript_once()
1641
{
1641
{
1642
    int32_t i;
1642
    int32_t i;
1643
1643
1644
   G_Util_PtrToIdx2(&g_tile[0].execPtr, MAXTILES, sizeof(tiledata_t), script, P2I_BACK_NON0);
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);
1645
   G_Util_PtrToIdx2(&g_tile[0].loadPtr, MAXTILES, sizeof(tiledata_t), script, P2I_BACK_NON0);
1646
1646
1647
    for (i=0; i<g_scriptSize; i++)
1647
    for (i=0; i<g_scriptSize; i++)
1648
        if (bitptr[i>>3]&(BITPTR_POINTER<<(i&7)))
1648
        if (bitptr[i>>3]&(BITPTR_POINTER<<(i&7)))
1649
            script[i] = (intptr_t)(script + script[i]);
1649
            script[i] = (intptr_t)(script + script[i]);
1650
}
1650
}
1651
#endif
1651
#endif
1652
1652
1653
static void sv_preactordatasave()
1653
static void sv_preactordatasave()
1654
{
1654
{
1655
    int32_t i;
1655
    int32_t i;
1656
1656
1657
    for (i=0; i<MAXSPRITES; i++)
1657
    for (i=0; i<MAXSPRITES; i++)
1658
    {
1658
    {
1659
        actor[i].lightptr = NULL;
1659
        actor[i].lightptr = NULL;
1660
        actor[i].lightId = -1;
1660
        actor[i].lightId = -1;
1661
    }
1661
    }
1662
}
1662
}
1663
1663
1664
static void sv_postactordata()
1664
static void sv_postactordata()
1665
{
1665
{
1666
    int32_t i;
1666
    int32_t i;
1667
1667
1668
    for (i=0; i<MAXSPRITES; i++)
1668
    for (i=0; i<MAXSPRITES; i++)
1669
    {
1669
    {
1670
        actor[i].lightptr = NULL;
1670
        actor[i].lightptr = NULL;
1671
        actor[i].lightId = -1;
1671
        actor[i].lightId = -1;
1672
    }
1672
    }
1673
}
1673
}
1674
1674
1675
static void sv_preanimateptrsave()
1675
static void sv_preanimateptrsave()
1676
{
1676
{
1677
    G_Util_PtrToIdx(animateptr, g_animateCount, sector, P2I_FWD);
1677
    G_Util_PtrToIdx(animateptr, g_animateCount, sector, P2I_FWD);
1678
}
1678
}
1679
static void sv_postanimateptr()
1679
static void sv_postanimateptr()
1680
{
1680
{
1681
    G_Util_PtrToIdx(animateptr, g_animateCount, sector, P2I_BACK);
1681
    G_Util_PtrToIdx(animateptr, g_animateCount, sector, P2I_BACK);
1682
}
1682
}
1683
static void sv_prequote()
1683
static void sv_prequote()
1684
{
1684
{
1685
    if (!savegame_quotes)
1685
    if (!savegame_quotes)
1686
    {
1686
    {
1687
        void *ptr = Xcalloc(MAXQUOTES, MAXQUOTELEN);
1687
        void *ptr = Xcalloc(MAXQUOTES, MAXQUOTELEN);
1688
        savegame_quotes = (char(*)[MAXQUOTELEN])ptr;
1688
        savegame_quotes = (char(*)[MAXQUOTELEN])ptr;
1689
    }
1689
    }
1690
}
1690
}
1691
static void sv_quotesave()
1691
static void sv_quotesave()
1692
{
1692
{
1693
    int32_t i;
1693
    int32_t i;
1694
    Bmemset(savegame_quotedef, 0, sizeof(savegame_quotedef));
1694
    Bmemset(savegame_quotedef, 0, sizeof(savegame_quotedef));
1695
    for (i=0; i<MAXQUOTES; i++)
1695
    for (i=0; i<MAXQUOTES; i++)
1696
        if (ScriptQuotes[i])
1696
        if (ScriptQuotes[i])
1697
        {
1697
        {
1698
            savegame_quotedef[i>>3] |= 1<<(i&7);
1698
            savegame_quotedef[i>>3] |= 1<<(i&7);
1699
            Bmemcpy(savegame_quotes[i], ScriptQuotes[i], MAXQUOTELEN);
1699
            Bmemcpy(savegame_quotes[i], ScriptQuotes[i], MAXQUOTELEN);
1700
        }
1700
        }
1701
}
1701
}
1702
static void sv_quoteload()
1702
static void sv_quoteload()
1703
{
1703
{
1704
    int32_t i;
1704
    int32_t i;
1705
    for (i=0; i<MAXQUOTES; i++)
1705
    for (i=0; i<MAXQUOTES; i++)
1706
    {
1706
    {
1707
        if (savegame_quotedef[i>>3]&(1<<(i&7)))
1707
        if (savegame_quotedef[i>>3]&(1<<(i&7)))
1708
        {
1708
        {
1709
            C_AllocQuote(i);
1709
            C_AllocQuote(i);
1710
            Bmemcpy(ScriptQuotes[i], savegame_quotes[i], MAXQUOTELEN);
1710
            Bmemcpy(ScriptQuotes[i], savegame_quotes[i], MAXQUOTELEN);
1711
        }
1711
        }
1712
    }
1712
    }
1713
}
1713
}
1714
static void sv_prequoteredef()
1714
static void sv_prequoteredef()
1715