raze-gles/polymer/eduke32/source/savegame.c

2341 lines
74 KiB
C
Raw Normal View History

//-------------------------------------------------------------------------
/*
Copyright (C) 2010 EDuke32 developers and contributors
This file is part of EDuke32.
EDuke32 is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//-------------------------------------------------------------------------
#include "duke3d.h"
#include "gamedef.h"
#include "premap.h"
#include "menus.h"
#include "prlights.h"
#include "savegame.h"
extern char *bitptr;
#define BITPTR_POINTER 1
// For storing pointers in files.
// back_p==0: ptr -> "small int"
// back_p==1: "small int" -> ptr
//
// mode: see enum in savegame.h
void G_Util_PtrToIdx(void *ptr, uint32_t len, const void *base, int32_t mode)
{
uint32_t i;
intptr_t *iptr = ptr, ibase = (intptr_t)base;
int32_t back_p = mode&P2I_BACK_BIT;
int32_t onlynon0_p = mode&P2I_ONLYNON0_BIT;
// TODO: convert to proper offsets/indices for (a step towards) cross-
// compatibility between 32- and 64-bit systems in the netplay.
// REMEMBER to bump BYTEVERSION then.
for (i=0; i<len; i++)
// WARNING: C std doesn't say that bit pattern of NULL is necessarily 0!
if (!onlynon0_p || iptr[i])
{
if (!back_p)
iptr[i] -= ibase;
else
iptr[i] += ibase;
}
}
// TODO: sync with TROR special interpolations? (e.g. upper floor of subway)
void G_ResetInterpolations(void)
{
int32_t k, i;
g_numInterpolations = 0;
startofdynamicinterpolations = 0;
k = headspritestat[STAT_EFFECTOR];
while (k >= 0)
{
switch (sprite[k].lotag)
{
case 31:
G_SetInterpolation(&sector[sprite[k].sectnum].floorz);
break;
case 32:
G_SetInterpolation(&sector[sprite[k].sectnum].ceilingz);
break;
case 25:
G_SetInterpolation(&sector[sprite[k].sectnum].floorz);
G_SetInterpolation(&sector[sprite[k].sectnum].ceilingz);
break;
case 17:
G_SetInterpolation(&sector[sprite[k].sectnum].floorz);
G_SetInterpolation(&sector[sprite[k].sectnum].ceilingz);
break;
case 0:
case 5:
case 6:
case 11:
case 14:
case 15:
case 16:
case 26:
case 30:
Sect_SetInterpolation(sprite[k].sectnum);
break;
}
k = nextspritestat[k];
}
for (i=g_numInterpolations-1; i>=0; i--) bakipos[i] = *curipos[i];
for (i = g_animateCount-1; i>=0; i--)
G_SetInterpolation(animateptr[i]);
}
void ReadSaveGameHeaders(void)
{
int32_t dummy,j;
int32_t i;
char fn[13];
int32_t fil;
Bstrcpy(fn,"egam_.sav");
for (i=0; i<10; i++)
{
int32_t oldver=0;
fn[4] = i+'0';
if ((fil = kopen4loadfrommod(fn,0)) == -1) continue;
if (kdfread(&j,sizeof(int32_t),1,fil) != 1)
{
kclose(fil);
continue;
}
if (kdfread(g_szBuf,j,1,fil) != 1)
{
kclose(fil);
continue;
}
if (kdfread(&dummy,sizeof(dummy),1,fil) != 1)
{
kclose(fil);
continue;
}
if (dummy != BYTEVERSION)
{
oldver = 1;
// kclose(fil);
// continue;
}
if (kdfread(&dummy,sizeof(dummy),1,fil) != 1)
{
kclose(fil);
continue;
}
if (kdfread(&ud.savegame[i][0],21,1,fil) != 1)
{
ud.savegame[i][0] = 0;
ud.savegame[i][20] = 0;
ud.savegame[i][21] = 0;
}
else ud.savegame[i][19] = 0;
if (oldver)
{
// Bmemset(ud.savegame[i], 0, 20);
ud.savegame[i][0] = 0;
ud.savegame[i][20] = 32;
ud.savegame[i][21] = 0;
}
kclose(fil);
}
}
int32_t G_LoadSaveHeader(char spot,struct savehead_ *saveh)
{
char fn[13];
int32_t fil;
int32_t bv;
strcpy(fn, "egam0.sav");
fn[4] = spot+'0';
if ((fil = kopen4loadfrommod(fn,0)) == -1) return(-1);
walock[TILE_LOADSHOT] = 255;
if (kdfread(&bv,sizeof(bv),1,fil) != 1) goto corrupt;
if (kdfread(g_szBuf,bv,1,fil) != 1) goto corrupt;
g_szBuf[bv]=0;
// AddLog(g_szBuf);
if (kdfread(&bv,sizeof(bv),1,fil) != 1) goto corrupt;
/* if (bv != BYTEVERSION)
{
P_DoQuote(QUOTE_SAVE_BAD_VERSION,g_player[myconnectindex].ps);
kclose(fil);
return 1;
}*/
saveh->byteversion = bv;
if (kdfread(&saveh->numplr,sizeof(int32_t),1,fil) != 1) goto corrupt;
if (kdfread(saveh->name,21,1,fil) != 1) goto corrupt;
if (kdfread(&saveh->volnum,sizeof(int32_t),1,fil) != 1) goto corrupt;
if (kdfread(&saveh->levnum,sizeof(int32_t),1,fil) != 1) goto corrupt;
if (kdfread(&saveh->plrskl,sizeof(int32_t),1,fil) != 1) goto corrupt;
if (kdfread(saveh->boardfn,BMAX_PATH,1,fil) != 1) goto corrupt;
if (waloff[TILE_LOADSHOT] == 0) allocache(&waloff[TILE_LOADSHOT],320*200,&walock[TILE_LOADSHOT]);
tilesizx[TILE_LOADSHOT] = 200;
tilesizy[TILE_LOADSHOT] = 320;
if (kdfread((char *)waloff[TILE_LOADSHOT],320,200,fil) != 200) goto corrupt;
invalidatetile(TILE_LOADSHOT,0,255);
kclose(fil);
return(0);
corrupt:
kclose(fil);
return 1;
}
int32_t G_LoadPlayer(int32_t spot)
{
char fn[13];
char mpfn[13];
char *fnptr;
#if !defined SAMESIZE_ACTOR_T
char *scriptptrs;
#endif
int32_t fil, bv, i, x;
intptr_t j;
int32_t nump;
strcpy(fn, "egam0.sav");
strcpy(mpfn, "egamA_00.sav");
fnptr = fn;
fn[4] = spot + '0';
if ((fil = kopen4loadfrommod(fnptr,0)) == -1) return(-1);
ready2send = 0;
if (kdfread(&bv,sizeof(bv),1,fil) != 1) goto corrupt;
if (kdfread(g_szBuf,bv,1,fil) != 1) goto corrupt;
g_szBuf[bv]=0;
// AddLog(g_szBuf);
if (kdfread(&bv,sizeof(bv),1,fil) != 1) return -1;
if (bv != BYTEVERSION)
{
P_DoQuote(QUOTE_SAVE_BAD_VERSION,g_player[myconnectindex].ps);
kclose(fil);
ototalclock = totalclock;
ready2send = 1;
return 1;
}
if (kdfread(&nump,sizeof(nump),1,fil) != 1) return -1;
if (nump != ud.multimode)
{
kclose(fil);
ototalclock = totalclock;
ready2send = 1;
P_DoQuote(QUOTE_SAVE_BAD_PLAYERS,g_player[myconnectindex].ps);
return 1;
}
else ud.multimode = nump;
if (numplayers > 1)
{
pub = NUMPAGES;
pus = NUMPAGES;
G_UpdateScreenArea();
G_DrawBackground();
menutext(160,100,0,0,"LOADING...");
nextpage();
}
Net_WaitForServer();
FX_StopAllSounds();
S_ClearSoundLocks();
if (numplayers > 1)
{
if (kdfread(&buf,21,1,fil) != 1) goto corrupt;
}
else
{
if (kdfread(&ud.savegame[spot][0],21,1,fil) != 1) goto corrupt;
ud.savegame[spot][19] = 0;
}
if (kdfread(&ud.volume_number,sizeof(ud.volume_number),1,fil) != 1) goto corrupt;
if (kdfread(&ud.level_number,sizeof(ud.level_number),1,fil) != 1) goto corrupt;
if (kdfread(&ud.player_skill,sizeof(ud.player_skill),1,fil) != 1) goto corrupt;
if (kdfread(&boardfilename[0],BMAX_PATH,1,fil) != 1) goto corrupt;
currentboardfilename[0] = 0;
if (boardfilename[0])
strcpy(currentboardfilename, boardfilename);
else if (MapInfo[(ud.volume_number * MAXLEVELS) + ud.level_number].filename)
strcpy(currentboardfilename, MapInfo[(ud.volume_number * MAXLEVELS) + ud.level_number].filename);
if (currentboardfilename[0])
{
char *p;
p = Bstrrchr(currentboardfilename,'.');
if (!p) strcat(currentboardfilename,".mhk");
else
{
p[1]='m';
p[2]='h';
p[3]='k';
p[4]=0;
}
loadmaphack(currentboardfilename);
}
Bmemcpy(&currentboardfilename[0],&boardfilename[0],BMAX_PATH);
ud.m_level_number = ud.level_number;
ud.m_volume_number = ud.volume_number;
ud.m_player_skill = ud.player_skill;
//Fake read because lseek won't work with compression
walock[TILE_LOADSHOT] = 1;
if (waloff[TILE_LOADSHOT] == 0) allocache(&waloff[TILE_LOADSHOT],320*200,&walock[TILE_LOADSHOT]);
tilesizx[TILE_LOADSHOT] = 200;
tilesizy[TILE_LOADSHOT] = 320;
if (kdfread((char *)waloff[TILE_LOADSHOT],320,200,fil) != 200) goto corrupt;
invalidatetile(TILE_LOADSHOT,0,255);
if (kdfread(&numwalls,sizeof(numwalls),1,fil) != 1) goto corrupt;
if (kdfread(&wall[0],sizeof(walltype),MAXWALLS,fil) != MAXWALLS) goto corrupt;
if (kdfread(&numsectors,sizeof(numsectors),1,fil) != 1) goto corrupt;
if (kdfread(&sector[0],sizeof(sectortype),MAXSECTORS,fil) != MAXSECTORS) goto corrupt;
#ifdef YAX_ENABLE
if (kdfread(&numyaxbunches,sizeof(numyaxbunches),1,fil) != 1) goto corrupt;
if (numyaxbunches > 0)
{
if (kdfread(yax_bunchnum,sizeof(yax_bunchnum[0]),numsectors,fil) != numsectors) goto corrupt;
if (kdfread(yax_nextwall,sizeof(yax_nextwall[0]),numwalls,fil) != numwalls) goto corrupt;
yax_update(2);
}
else
yax_update(1);
#endif
if (kdfread(&sprite[0],sizeof(spritetype),MAXSPRITES,fil) != MAXSPRITES) goto corrupt;
if (kdfread(&spriteext[0],sizeof(spriteext_t),MAXSPRITES,fil) != MAXSPRITES) goto corrupt;
#ifdef POLYMER
if (kdfread(&lightcount,sizeof(lightcount),1,fil) != 1) goto corrupt;
if (kdfread(&prlights[0],sizeof(_prlight),lightcount,fil) != lightcount) goto corrupt;
#else
if (kdfread(&i,sizeof(int32_t),1,fil) != 1) goto corrupt;
#endif // POLYMER
#ifdef USE_OPENGL
for (i=0; i<MAXSPRITES; i++)
if (spriteext[i].mdanimtims)
spriteext[i].mdanimtims+=mdtims;
#endif
if (kdfread(&headspritesect[0],sizeof(headspritesect[0]),MAXSECTORS+1,fil) != MAXSECTORS+1) goto corrupt;
if (kdfread(&prevspritesect[0],sizeof(prevspritesect[0]),MAXSPRITES,fil) != MAXSPRITES) goto corrupt;
if (kdfread(&nextspritesect[0],sizeof(nextspritesect[0]),MAXSPRITES,fil) != MAXSPRITES) goto corrupt;
if (kdfread(&headspritestat[STAT_DEFAULT],sizeof(headspritestat[STAT_DEFAULT]),MAXSTATUS+1,fil) != MAXSTATUS+1) goto corrupt;
if (kdfread(&prevspritestat[STAT_DEFAULT],sizeof(prevspritestat[STAT_DEFAULT]),MAXSPRITES,fil) != MAXSPRITES) goto corrupt;
if (kdfread(&nextspritestat[STAT_DEFAULT],sizeof(nextspritestat[STAT_DEFAULT]),MAXSPRITES,fil) != MAXSPRITES) goto corrupt;
if (kdfread(&g_numCyclers,sizeof(g_numCyclers),1,fil) != 1) goto corrupt;
if (kdfread(&cyclers[0][0],sizeof(cyclers[0][0])*6,MAXCYCLERS,fil) != MAXCYCLERS) goto corrupt;
for (i=0; i<nump; i++)
if (kdfread(g_player[i].ps,sizeof(DukePlayer_t),1,fil) != 1) goto corrupt;
if (kdfread(&g_playerSpawnPoints,sizeof(g_playerSpawnPoints),1,fil) != 1) goto corrupt;
if (kdfread(&g_numAnimWalls,sizeof(g_numAnimWalls),1,fil) != 1) goto corrupt;
if (kdfread(&animwall,sizeof(animwall),1,fil) != 1) goto corrupt;
if (kdfread(&msx[0],sizeof(int32_t),sizeof(msx)/sizeof(int32_t),fil) != sizeof(msx)/sizeof(int32_t)) goto corrupt;
if (kdfread(&msy[0],sizeof(int32_t),sizeof(msy)/sizeof(int32_t),fil) != sizeof(msy)/sizeof(int32_t)) goto corrupt;
if (kdfread((int16_t *)&g_spriteDeleteQueuePos,sizeof(int16_t),1,fil) != 1) goto corrupt;
if (kdfread((int16_t *)&g_spriteDeleteQueueSize,sizeof(int16_t),1,fil) != 1) goto corrupt;
if (kdfread((int16_t *)&SpriteDeletionQueue[0],sizeof(int16_t),g_spriteDeleteQueueSize,fil) != g_spriteDeleteQueueSize) goto corrupt;
if (kdfread(&g_mirrorCount,sizeof(int16_t),1,fil) != 1) goto corrupt;
if (kdfread(&g_mirrorWall[0],sizeof(int16_t),64,fil) != 64) goto corrupt;
if (kdfread(&g_mirrorSector[0],sizeof(int16_t),64,fil) != 64) goto corrupt;
if (kdfread(&show2dsector[0],sizeof(uint8_t),MAXSECTORS>>3,fil) != (MAXSECTORS>>3)) goto corrupt;
if (kdfread(&ActorType[0],sizeof(uint8_t),MAXTILES,fil) != MAXTILES) goto corrupt;
if (kdfread(&g_numClouds,sizeof(g_numClouds),1,fil) != 1) goto corrupt;
if (kdfread(&clouds[0],sizeof(int16_t)<<7,1,fil) != 1) goto corrupt;
if (kdfread(&cloudx[0],sizeof(int16_t)<<7,1,fil) != 1) goto corrupt;
if (kdfread(&cloudy[0],sizeof(int16_t)<<7,1,fil) != 1) goto corrupt;
if (kdfread(&g_scriptSize,sizeof(g_scriptSize),1,fil) != 1) goto corrupt;
if (!g_scriptSize) goto corrupt;
Bfree(bitptr);
bitptr = Bcalloc(1,(((g_scriptSize+7)>>3)+1) * sizeof(uint8_t));
if (kdfread(&bitptr[0],sizeof(uint8_t),(g_scriptSize+7)>>3,fil) != ((g_scriptSize+7)>>3)) goto corrupt;
if (script != NULL)
Bfree(script);
script = Bcalloc(1,g_scriptSize * sizeof(intptr_t));
if (kdfread(&script[0],sizeof(script),g_scriptSize,fil) != g_scriptSize) goto corrupt;
for (i=0; i<g_scriptSize; i++)
if (bitptr[i>>3]&(BITPTR_POINTER<<(i&7)))
{
j = (intptr_t)script[i]+(intptr_t)&script[0];
script[i] = j;
}
if (kdfread(&actorscrptr[0],sizeof(actorscrptr[0]),MAXTILES,fil) != MAXTILES) goto corrupt;
G_Util_PtrToIdx(actorscrptr, MAXTILES, script, P2I_BACK_NON0);
if (kdfread(&actorLoadEventScrptr[0],sizeof(&actorLoadEventScrptr[0]),MAXTILES,fil) != MAXTILES) goto corrupt;
G_Util_PtrToIdx(actorLoadEventScrptr, MAXTILES, script, P2I_BACK_NON0);
#if !defined SAMESIZE_ACTOR_T
scriptptrs = Bmalloc(MAXSPRITES * sizeof(scriptptrs));
if (kdfread(&scriptptrs[0],sizeof(scriptptrs),MAXSPRITES,fil) != MAXSPRITES) goto corrupt;
#endif
if (kdfread(&actor[0],sizeof(actor_t),MAXSPRITES,fil) != MAXSPRITES) goto corrupt;
for (i=0; i<MAXSPRITES; i++)
{
#if !defined SAMESIZE_ACTOR_T
j = (intptr_t)script;
if (scriptptrs[i]&1) T2 += j;
if (scriptptrs[i]&2) T5 += j;
if (scriptptrs[i]&4) T6 += j;
#endif
}
#if !defined SAMESIZE_ACTOR_T
Bfree(scriptptrs);
#endif
if (kdfread(&lockclock,sizeof(lockclock),1,fil) != 1) goto corrupt;
if (kdfread(&pskybits,sizeof(pskybits),1,fil) != 1) goto corrupt;
if (kdfread(&pskyoff[0],sizeof(pskyoff[0]),MAXPSKYTILES,fil) != MAXPSKYTILES) goto corrupt;
if (kdfread(&g_animateCount,sizeof(g_animateCount),1,fil) != 1) goto corrupt;
if (kdfread(&animatesect[0],sizeof(animatesect[0]),MAXANIMATES,fil) != MAXANIMATES) goto corrupt;
if (kdfread(&animateptr[0],sizeof(animateptr[0]),MAXANIMATES,fil) != MAXANIMATES) goto corrupt;
G_Util_PtrToIdx(animateptr, g_animateCount, sector, P2I_BACK);
if (kdfread(&animategoal[0],sizeof(animategoal[0]),MAXANIMATES,fil) != MAXANIMATES) goto corrupt;
if (kdfread(&animatevel[0],sizeof(animatevel[0]),MAXANIMATES,fil) != MAXANIMATES) goto corrupt;
if (kdfread(&g_earthquakeTime,sizeof(g_earthquakeTime),1,fil) != 1) goto corrupt;
if (kdfread(&ud.from_bonus,sizeof(ud.from_bonus),1,fil) != 1) goto corrupt;
if (kdfread(&ud.secretlevel,sizeof(ud.secretlevel),1,fil) != 1) goto corrupt;
if (kdfread(&ud.respawn_monsters,sizeof(ud.respawn_monsters),1,fil) != 1) goto corrupt;
ud.m_respawn_monsters = ud.respawn_monsters;
if (kdfread(&ud.respawn_items,sizeof(ud.respawn_items),1,fil) != 1) goto corrupt;
ud.m_respawn_items = ud.respawn_items;
if (kdfread(&ud.respawn_inventory,sizeof(ud.respawn_inventory),1,fil) != 1) goto corrupt;
ud.m_respawn_inventory = ud.respawn_inventory;
if (kdfread(&ud.god,sizeof(ud.god),1,fil) != 1) goto corrupt;
if (kdfread(&ud.auto_run,sizeof(ud.auto_run),1,fil) != 1) goto corrupt;
if (kdfread(&ud.crosshair,sizeof(ud.crosshair),1,fil) != 1) goto corrupt;
if (kdfread(&ud.monsters_off,sizeof(ud.monsters_off),1,fil) != 1) goto corrupt;
ud.m_monsters_off = ud.monsters_off;
if (kdfread(&ud.last_level,sizeof(ud.last_level),1,fil) != 1) goto corrupt;
if (kdfread(&ud.eog,sizeof(ud.eog),1,fil) != 1) goto corrupt;
if (kdfread(&ud.coop,sizeof(ud.coop),1,fil) != 1) goto corrupt;
ud.m_coop = ud.coop;
if (kdfread(&ud.marker,sizeof(ud.marker),1,fil) != 1) goto corrupt;
ud.m_marker = ud.marker;
if (kdfread(&ud.ffire,sizeof(ud.ffire),1,fil) != 1) goto corrupt;
ud.m_ffire = ud.ffire;
if (kdfread(&camsprite,sizeof(camsprite),1,fil) != 1) goto corrupt;
if (kdfread(connectpoint2,sizeof(connectpoint2),1,fil) != 1) goto corrupt;
if (kdfread(&g_numPlayerSprites,sizeof(g_numPlayerSprites),1,fil) != 1) goto corrupt;
for (i=0; i<MAXPLAYERS; i++)
if (kdfread((int16_t *)&g_player[i].frags[0],sizeof(g_player[i].frags),1,fil) != 1) goto corrupt;
if (kdfread(&randomseed,sizeof(randomseed),1,fil) != 1) goto corrupt;
if (kdfread(&g_globalRandom,sizeof(g_globalRandom),1,fil) != 1) goto corrupt;
if (kdfread(&parallaxyscale,sizeof(parallaxyscale),1,fil) != 1) goto corrupt;
if (kdfread(&SpriteProjectile[0],sizeof(projectile_t),MAXSPRITES,fil) != MAXSPRITES) goto corrupt;
if (kdfread(&ProjectileData[0],sizeof(projectile_t),MAXTILES,fil) != MAXTILES) goto corrupt;
if (kdfread(&DefaultProjectileData[0],sizeof(projectile_t),MAXTILES,fil) != MAXTILES) goto corrupt;
if (kdfread(&SpriteFlags[0],sizeof(SpriteFlags[0]),MAXTILES,fil) != MAXTILES) goto corrupt;
if (kdfread(&SpriteCacheList[0],sizeof(SpriteCacheList[0]),MAXTILES,fil) != MAXTILES) goto corrupt;
if (kdfread(&i,sizeof(int32_t),1,fil) != 1) goto corrupt;
while (i != MAXQUOTES)
{
if (ScriptQuotes[i] != NULL)
Bfree(ScriptQuotes[i]);
ScriptQuotes[i] = Bcalloc(MAXQUOTELEN,sizeof(uint8_t));
if (kdfread((char *)ScriptQuotes[i],MAXQUOTELEN,1,fil) != 1) goto corrupt;
if (kdfread(&i,sizeof(int32_t),1,fil) != 1) goto corrupt;
}
if (kdfread(&g_numQuoteRedefinitions,sizeof(g_numQuoteRedefinitions),1,fil) != 1) goto corrupt;
for (i=0; i<g_numQuoteRedefinitions; i++)
{
if (ScriptQuoteRedefinitions[i] != NULL)
Bfree(ScriptQuoteRedefinitions[i]);
ScriptQuoteRedefinitions[i] = Bcalloc(MAXQUOTELEN,sizeof(uint8_t));
if (kdfread((char *)ScriptQuoteRedefinitions[i],MAXQUOTELEN,1,fil) != 1) goto corrupt;
}
if (kdfread(&DynamicTileMap[0],sizeof(DynamicTileMap[0]),MAXTILES,fil) != MAXTILES) goto corrupt;
if (kdfread(&ud.noexits,sizeof(ud.noexits),1,fil) != 1) goto corrupt;
ud.m_noexits = ud.noexits;
if (Gv_ReadSave(fil, 0)) goto corrupt;
kclose(fil);
if (g_player[myconnectindex].ps->over_shoulder_on != 0)
{
g_cameraDistance = 0;
g_cameraClock = 0;
g_player[myconnectindex].ps->over_shoulder_on = 1;
}
screenpeek = myconnectindex;
clearbufbyte(gotpic,sizeof(gotpic),0L);
S_ClearSoundLocks();
G_CacheMapData();
i = g_musicIndex;
g_musicIndex = (ud.volume_number*MAXLEVELS) + ud.level_number;
if (boardfilename[0] != 0 && ud.level_number == 7 && ud.volume_number == 0)
{
char *p;
char levname[BMAX_PATH];
int32_t fil;
strcpy(levname, boardfilename);
// usermap music based on map filename
Bcorrectfilename(levname,0);
p = Bstrrchr(levname,'.');
if (!p) strcat(levname,".ogg");
else
{
p[1]='o';
p[2]='g';
p[3]='g';
p[4]=0;
}
fil = kopen4loadfrommod(levname,0);
if (fil > -1)
{
kclose(fil);
if (MapInfo[ud.level_number].alt_musicfn == NULL)
MapInfo[ud.level_number].alt_musicfn = Bcalloc(Bstrlen(levname)+1,sizeof(uint8_t));
else if ((Bstrlen(levname)+1) > sizeof(MapInfo[ud.level_number].alt_musicfn))
MapInfo[ud.level_number].alt_musicfn = Brealloc(MapInfo[ud.level_number].alt_musicfn,(Bstrlen(levname)+1));
Bstrcpy(MapInfo[ud.level_number].alt_musicfn,levname);
}
else if (MapInfo[ud.level_number].alt_musicfn != NULL)
{
Bfree(MapInfo[ud.level_number].alt_musicfn);
MapInfo[ud.level_number].alt_musicfn = NULL;
}
p[1]='m';
p[2]='i';
p[3]='d';
p[4]=0;
fil = kopen4loadfrommod(levname,0);
if (fil == -1)
Bsprintf(levname,"dethtoll.mid");
else kclose(fil);
if (MapInfo[ud.level_number].musicfn == NULL)
MapInfo[ud.level_number].musicfn = Bcalloc(Bstrlen(levname)+1,sizeof(uint8_t));
else if ((Bstrlen(levname)+1) > sizeof(MapInfo[ud.level_number].musicfn))
MapInfo[ud.level_number].musicfn = Brealloc(MapInfo[ud.level_number].musicfn,(Bstrlen(levname)+1));
Bstrcpy(MapInfo[ud.level_number].musicfn,levname);
}
if (ud.config.MusicToggle)
{
if (MapInfo[(uint8_t)g_musicIndex].musicfn != NULL &&
(i != g_musicIndex || MapInfo[MAXVOLUMES*MAXLEVELS+2].alt_musicfn))
{
S_StopMusic();
S_PlayMusic(&MapInfo[(uint8_t)g_musicIndex].musicfn[0],g_musicIndex);
}
S_PauseMusic(0);
}
g_player[myconnectindex].ps->gm = MODE_GAME;
ud.recstat = 0;
if (g_player[myconnectindex].ps->jetpack_on)
A_PlaySound(DUKE_JETPACK_IDLE,g_player[myconnectindex].ps->i);
g_restorePalette = 1;
P_UpdateScreenPal(g_player[myconnectindex].ps);
G_UpdateScreenArea();
FX_SetReverb(0);
if (ud.lockout == 0)
{
for (x=0; x<g_numAnimWalls; x++)
if (wall[animwall[x].wallnum].extra >= 0)
wall[animwall[x].wallnum].picnum = wall[animwall[x].wallnum].extra;
}
else
{
for (x=0; x<g_numAnimWalls; x++)
switch (DynamicTileMap[wall[animwall[x].wallnum].picnum])
{
case FEMPIC1__STATIC:
wall[animwall[x].wallnum].picnum = BLANKSCREEN;
break;
case FEMPIC2__STATIC:
case FEMPIC3__STATIC:
wall[animwall[x].wallnum].picnum = SCREENBREAK6;
break;
}
}
G_ResetInterpolations();
g_showShareware = 0;
everyothertime = 0;
for (i=0; i<MAXPLAYERS; i++)
g_player[i].playerquitflag = 1;
Net_ResetPrediction();
ready2send = 1;
G_ClearFIFO();
Net_WaitForServer();
G_ResetTimers();
#ifdef POLYMER
if (getrendermode() == 4)
polymer_loadboard();
#endif
// this light pointer nulling needs to be outside the getrendermode check
// because we might be loading the savegame using another renderer but
// change to Polymer later
for (i=0; i<MAXSPRITES; i++)
{
actor[i].lightptr = NULL;
actor[i].lightId = -1;
}
return(0);
corrupt:
Bsprintf(tempbuf,"Save game file \"%s\" is corrupt or of the wrong version.",fnptr);
G_GameExit(tempbuf);
return -1;
}
int32_t G_SavePlayer(int32_t spot)
{
int32_t i;
intptr_t j;
char fn[13];
char mpfn[13];
char *fnptr;
#if !defined SAMESIZE_ACTOR_T
char *scriptptrs;
#endif
FILE *fil;
int32_t bv = BYTEVERSION;
strcpy(fn, "egam0.sav");
strcpy(mpfn, "egamA_00.sav");
Net_WaitForServer();
fnptr = fn;
fn[4] = spot + '0';
{
char temp[BMAX_PATH];
if (g_modDir[0] != '/')
Bsprintf(temp,"%s/%s",g_modDir,fnptr);
else Bsprintf(temp,"%s",fnptr);
if ((fil = fopen(temp,"wb")) == 0) return(-1);
}
ready2send = 0;
Bsprintf(g_szBuf,"EDuke32");
i=strlen(g_szBuf);
dfwrite(&i,sizeof(i),1,fil);
dfwrite(g_szBuf,i,1,fil);
dfwrite(&bv,sizeof(bv),1,fil);
dfwrite(&ud.multimode,sizeof(ud.multimode),1,fil);
dfwrite(&ud.savegame[spot][0],21,1,fil);
dfwrite(&ud.volume_number,sizeof(ud.volume_number),1,fil);
dfwrite(&ud.level_number,sizeof(ud.level_number),1,fil);
dfwrite(&ud.player_skill,sizeof(ud.player_skill),1,fil);
dfwrite(&currentboardfilename[0],BMAX_PATH,1,fil);
if (!waloff[TILE_SAVESHOT])
{
walock[TILE_SAVESHOT] = 254;
allocache(&waloff[TILE_SAVESHOT],200*320,&walock[TILE_SAVESHOT]);
clearbuf((void *)waloff[TILE_SAVESHOT],(200*320)/4,0);
walock[TILE_SAVESHOT] = 1;
}
dfwrite((char *)waloff[TILE_SAVESHOT],320,200,fil);
dfwrite(&numwalls,sizeof(numwalls),1,fil);
dfwrite(&wall[0],sizeof(walltype),MAXWALLS,fil);
dfwrite(&numsectors,sizeof(numsectors),1,fil);
dfwrite(&sector[0],sizeof(sectortype),MAXSECTORS,fil);
#ifdef YAX_ENABLE
dfwrite(&numyaxbunches,sizeof(numyaxbunches),1,fil);
if (numyaxbunches > 0)
{
dfwrite(yax_bunchnum,sizeof(yax_bunchnum[0]),numsectors,fil);
dfwrite(yax_nextwall,sizeof(yax_nextwall[0]),numwalls,fil);
}
#endif
dfwrite(&sprite[0],sizeof(spritetype),MAXSPRITES,fil);
#ifdef USE_OPENGL
for (i=0; i<MAXSPRITES; i++)
if (spriteext[i].mdanimtims)
{
spriteext[i].mdanimtims=spriteext[i].mdanimtims-mdtims;
if (!spriteext[i].mdanimtims)
spriteext[i].mdanimtims++;
}
#endif
dfwrite(&spriteext[0],sizeof(spriteext_t),MAXSPRITES,fil);
#ifdef POLYMER
dfwrite(&lightcount,sizeof(lightcount),1,fil);
dfwrite(&prlights[0],sizeof(_prlight),lightcount,fil);
#else
i = 0;
dfwrite(&i,sizeof(int32_t),1,fil);
#endif // POLYMER
#ifdef USE_OPENGL
for (i=0; i<MAXSPRITES; i++)if (spriteext[i].mdanimtims)spriteext[i].mdanimtims+=mdtims;
#endif
dfwrite(&headspritesect[0],sizeof(headspritesect[0]),MAXSECTORS+1,fil);
dfwrite(&prevspritesect[0],sizeof(prevspritesect[0]),MAXSPRITES,fil);
dfwrite(&nextspritesect[0],sizeof(nextspritesect[0]),MAXSPRITES,fil);
dfwrite(&headspritestat[STAT_DEFAULT],sizeof(headspritestat[0]),MAXSTATUS+1,fil);
dfwrite(&prevspritestat[STAT_DEFAULT],sizeof(prevspritestat[0]),MAXSPRITES,fil);
dfwrite(&nextspritestat[STAT_DEFAULT],sizeof(nextspritestat[0]),MAXSPRITES,fil);
dfwrite(&g_numCyclers,sizeof(g_numCyclers),1,fil);
dfwrite(&cyclers[0][0],sizeof(cyclers[0][0])*6,MAXCYCLERS,fil);
for (i=0; i<ud.multimode; i++)
dfwrite(g_player[i].ps,sizeof(DukePlayer_t),1,fil);
dfwrite(&g_playerSpawnPoints,sizeof(g_playerSpawnPoints),1,fil);
dfwrite(&g_numAnimWalls,sizeof(g_numAnimWalls),1,fil);
dfwrite(&animwall,sizeof(animwall),1,fil);
dfwrite(&msx[0],sizeof(int32_t),sizeof(msx)/sizeof(int32_t),fil);
dfwrite(&msy[0],sizeof(int32_t),sizeof(msy)/sizeof(int32_t),fil);
dfwrite(&g_spriteDeleteQueuePos,sizeof(int16_t),1,fil);
dfwrite(&g_spriteDeleteQueueSize,sizeof(int16_t),1,fil);
dfwrite(&SpriteDeletionQueue[0],sizeof(int16_t),g_spriteDeleteQueueSize,fil);
dfwrite(&g_mirrorCount,sizeof(int16_t),1,fil);
dfwrite(&g_mirrorWall[0],sizeof(int16_t),64,fil);
dfwrite(&g_mirrorSector[0],sizeof(int16_t),64,fil);
dfwrite(&show2dsector[0],sizeof(uint8_t),MAXSECTORS>>3,fil);
dfwrite(&ActorType[0],sizeof(uint8_t),MAXTILES,fil);
dfwrite(&g_numClouds,sizeof(g_numClouds),1,fil);
dfwrite(&clouds[0],sizeof(int16_t)<<7,1,fil);
dfwrite(&cloudx[0],sizeof(int16_t)<<7,1,fil);
dfwrite(&cloudy[0],sizeof(int16_t)<<7,1,fil);
dfwrite(&g_scriptSize,sizeof(g_scriptSize),1,fil);
for (i=0; i<g_scriptSize; i++)
{
if (bitptr[i>>3]&(BITPTR_POINTER<<(i&7)))
{
j = (intptr_t)script[i] - (intptr_t)&script[0];
script[i] = j;
}
}
dfwrite(&bitptr[0],sizeof(bitptr[0]),(g_scriptSize+7)>>3,fil);
dfwrite(&script[0],sizeof(script[0]),g_scriptSize,fil);
for (i=0; i<g_scriptSize; i++)
if (bitptr[i>>3]&(BITPTR_POINTER<<(i&7)))
{
j = script[i]+(intptr_t)&script[0];
script[i] = j;
}
G_Util_PtrToIdx(actorscrptr, MAXTILES, script, P2I_FWD_NON0);
dfwrite(&actorscrptr[0],sizeof(actorscrptr[0]),MAXTILES,fil);
G_Util_PtrToIdx(actorscrptr, MAXTILES, script, P2I_BACK_NON0);
G_Util_PtrToIdx(actorLoadEventScrptr, MAXTILES, script, P2I_FWD_NON0);
dfwrite(&actorLoadEventScrptr[0],sizeof(actorLoadEventScrptr[0]),MAXTILES,fil);
G_Util_PtrToIdx(actorLoadEventScrptr, MAXTILES, script, P2I_BACK_NON0);
#if !defined SAMESIZE_ACTOR_T
scriptptrs = Bcalloc(1, MAXSPRITES * sizeof(scriptptrs));
for (i=0; i<MAXSPRITES; i++)
{
scriptptrs[i] = 0;
if (actorscrptr[PN] == 0) continue;
j = (intptr_t)&script[0];
if (T2 >= j && T2 < (intptr_t)(&script[g_scriptSize]))
{
scriptptrs[i] |= 1;
T2 -= j;
}
if (T5 >= j && T5 < (intptr_t)(&script[g_scriptSize]))
{
scriptptrs[i] |= 2;
T5 -= j;
}
if (T6 >= j && T6 < (intptr_t)(&script[g_scriptSize]))
{
scriptptrs[i] |= 4;
T6 -= j;
}
}
dfwrite(&scriptptrs[0],sizeof(scriptptrs),MAXSPRITES,fil);
#endif
dfwrite(&actor[0],sizeof(actor_t),MAXSPRITES,fil);
#if !defined SAMESIZE_ACTOR_T
for (i=0; i<MAXSPRITES; i++)
{
if (actorscrptr[PN] == 0) continue;
j = (intptr_t)&script[0];
if (scriptptrs[i]&1)
T2 += j;
if (scriptptrs[i]&2)
T5 += j;
if (scriptptrs[i]&4)
T6 += j;
}
Bfree(scriptptrs);
#endif
dfwrite(&lockclock,sizeof(lockclock),1,fil);
dfwrite(&pskybits,sizeof(pskybits),1,fil);
dfwrite(&pskyoff[0],sizeof(pskyoff[0]),MAXPSKYTILES,fil);
dfwrite(&g_animateCount,sizeof(g_animateCount),1,fil);
dfwrite(&animatesect[0],sizeof(animatesect[0]),MAXANIMATES,fil);
G_Util_PtrToIdx(animateptr, g_animateCount, sector, P2I_FWD);
dfwrite(&animateptr[0],sizeof(animateptr[0]),MAXANIMATES,fil);
G_Util_PtrToIdx(animateptr, g_animateCount, sector, P2I_BACK);
dfwrite(&animategoal[0],sizeof(animategoal[0]),MAXANIMATES,fil);
dfwrite(&animatevel[0],sizeof(animatevel[0]),MAXANIMATES,fil);
dfwrite(&g_earthquakeTime,sizeof(g_earthquakeTime),1,fil);
dfwrite(&ud.from_bonus,sizeof(ud.from_bonus),1,fil);
dfwrite(&ud.secretlevel,sizeof(ud.secretlevel),1,fil);
dfwrite(&ud.respawn_monsters,sizeof(ud.respawn_monsters),1,fil);
dfwrite(&ud.respawn_items,sizeof(ud.respawn_items),1,fil);
dfwrite(&ud.respawn_inventory,sizeof(ud.respawn_inventory),1,fil);
dfwrite(&ud.god,sizeof(ud.god),1,fil);
dfwrite(&ud.auto_run,sizeof(ud.auto_run),1,fil);
dfwrite(&ud.crosshair,sizeof(ud.crosshair),1,fil);
dfwrite(&ud.monsters_off,sizeof(ud.monsters_off),1,fil);
dfwrite(&ud.last_level,sizeof(ud.last_level),1,fil);
dfwrite(&ud.eog,sizeof(ud.eog),1,fil);
dfwrite(&ud.coop,sizeof(ud.coop),1,fil);
dfwrite(&ud.marker,sizeof(ud.marker),1,fil);
dfwrite(&ud.ffire,sizeof(ud.ffire),1,fil);
dfwrite(&camsprite,sizeof(camsprite),1,fil);
dfwrite(connectpoint2,sizeof(connectpoint2),1,fil);
dfwrite(&g_numPlayerSprites,sizeof(g_numPlayerSprites),1,fil);
for (i=0; i<MAXPLAYERS; i++)
dfwrite((int16_t *)&g_player[i].frags[0],sizeof(g_player[i].frags),1,fil);
dfwrite(&randomseed,sizeof(randomseed),1,fil);
dfwrite(&g_globalRandom,sizeof(g_globalRandom),1,fil);
dfwrite(&parallaxyscale,sizeof(parallaxyscale),1,fil);
dfwrite(&SpriteProjectile[0],sizeof(projectile_t),MAXSPRITES,fil);
dfwrite(&ProjectileData[0],sizeof(projectile_t),MAXTILES,fil);
dfwrite(&DefaultProjectileData[0],sizeof(projectile_t),MAXTILES,fil);
dfwrite(&SpriteFlags[0],sizeof(SpriteFlags[0]),MAXTILES,fil);
dfwrite(&SpriteCacheList[0],sizeof(SpriteCacheList[0]),MAXTILES,fil);
for (i=0; i<MAXQUOTES; i++)
{
if (ScriptQuotes[i] != NULL)
{
dfwrite(&i,sizeof(int32_t),1,fil);
dfwrite(ScriptQuotes[i],MAXQUOTELEN, 1, fil);
}
}
dfwrite(&i,sizeof(int32_t),1,fil);
dfwrite(&g_numQuoteRedefinitions,sizeof(g_numQuoteRedefinitions),1,fil);
for (i=0; i<g_numQuoteRedefinitions; i++)
{
if (ScriptQuoteRedefinitions[i] != NULL)
dfwrite(ScriptQuoteRedefinitions[i],MAXQUOTELEN, 1, fil);
}
dfwrite(&DynamicTileMap[0],sizeof(DynamicTileMap[0]),MAXTILES,fil);
dfwrite(&ud.noexits,sizeof(ud.noexits),1,fil);
Gv_WriteSave(fil, 0);
fclose(fil);
if ((!g_netServer && ud.multimode < 2))
{
strcpy(ScriptQuotes[QUOTE_RESERVED4],"GAME SAVED");
P_DoQuote(QUOTE_RESERVED4,g_player[myconnectindex].ps);
}
ready2send = 1;
Net_WaitForServer();
ototalclock = totalclock;
return(0);
}
////////// GENERIC SAVING/LOADING SYSTEM //////////
typedef struct dataspec_
{
uint32_t flags;
void *ptr;
uint32_t size;
intptr_t cnt;
} dataspec_t;
#ifdef YAX_ENABLE
# define SV_MAJOR_VER 1
#else
# define SV_MAJOR_VER 0
#endif
#ifdef SAMESIZE_ACTOR_T
# define SV_MINOR_VER 4
#else
# define SV_MINOR_VER 3
#endif
#define SV_DEFAULTCOMPRTHRES 8
static uint8_t savegame_diffcompress; // 0:none, 1:Ken's LZW in cache1d.c
static uint8_t savegame_comprthres;
#define DS_DYNAMIC 1 // dereference .ptr one more time
#define DS_STRING 2
#define DS_CMP 4
// 8
#define DS_CNT(x) ((sizeof(x))<<3) // .cnt is pointer to...
#define DS_CNT16 16
#define DS_CNT32 32
#define DS_CNTMASK (8|DS_CNT16|DS_CNT32|64)
// 64
#define DS_LOADFN 128 // .ptr is function that is run when loading
#define DS_SAVEFN 256 // .ptr is function that is run when saving
#define DS_NOCHK 1024 // don't check for diffs (and don't write out in dump) since assumed constant throughout demo
#define DS_PROTECTFN 512
#define DS_END (0x70000000)
static int32_t ds_getcnt(const dataspec_t *sp)
{
switch (sp->flags&DS_CNTMASK)
{
case 0: return sp->cnt;
case DS_CNT16: return *((int16_t *)sp->cnt);
case DS_CNT32: return *((int32_t *)sp->cnt);
default: return -1;
}
}
static void ds_get(const dataspec_t *sp, const void **ptr, int32_t *cnt)
{
*cnt = ds_getcnt(sp);
if (sp->flags&DS_DYNAMIC)
*ptr = *((void **)sp->ptr);
else
*ptr = sp->ptr;
}
// write state to file and/or to dump
static uint8_t *writespecdata(const dataspec_t *spec, FILE *fil, uint8_t *dump)
{
int32_t cnt;
const void *ptr;
const dataspec_t *sp=spec;
for (; sp->flags!=DS_END; sp++)
{
if (sp->flags&(DS_SAVEFN|DS_LOADFN))
{
if (sp->flags&DS_SAVEFN)
(*(void ( *)(void))sp->ptr)();
continue;
}
if (!fil && (sp->flags&(DS_NOCHK|DS_CMP|DS_STRING)))
continue;
if (sp->flags&DS_STRING)
{
fwrite(sp->ptr, Bstrlen(sp->ptr), 1, fil); // not null-terminated!
continue;
}
ds_get(sp, &ptr, &cnt);
if (cnt < 0) { OSD_Printf("wsd: cnt=%d, f=0x%x.\n",cnt,sp->flags); continue; }
if (fil)
{
if (((sp->flags&DS_CNTMASK)==0 && sp->size*cnt<=savegame_comprthres)
|| (sp->flags&DS_CMP))
fwrite(ptr, sp->size, cnt, fil);
else
dfwrite((void *)ptr, sp->size, cnt, fil);
}
if (dump && (sp->flags&(DS_NOCHK|DS_CMP))==0)
{
Bmemcpy(dump, ptr, sp->size*cnt);
dump += sp->size*cnt;
}
}
return dump;
}
// let havedump=dumpvar&&*dumpvar
// (fil>=0 && havedump): first restore dump from file, then restore state from dump
// (fil<0 && havedump): only restore state from dump
// (fil>=0 && !havedump): only restore state from file
static int32_t readspecdata(const dataspec_t *spec, int32_t fil, uint8_t **dumpvar)
{
int32_t cnt, i, j;
void *ptr;
uint8_t *dump=dumpvar?*dumpvar:NULL, *mem;
const dataspec_t *sp=spec;
static char cmpstrbuf[32];
for (; sp->flags!=DS_END; sp++)
{
if (fil < 0 && sp->flags&(DS_NOCHK|DS_STRING|DS_CMP)) // we're updating
continue;
if (sp->flags&(DS_LOADFN|DS_SAVEFN))
{
if (sp->flags&DS_LOADFN)
(*(void ( *)())sp->ptr)();
continue;
}
if (sp->flags&(DS_STRING|DS_CMP)) // DS_STRING and DS_CMP is for static data only
{
if (sp->flags&(DS_STRING))
i = Bstrlen(sp->ptr);
else
i = sp->size*sp->cnt;
j=kread(fil, cmpstrbuf, i);
if (j!=i || Bmemcmp(sp->ptr, cmpstrbuf, i))
{
OSD_Printf("rsd: spec=%p, sp=%p ", spec, sp);
if (j!=i)
OSD_Printf(" kread returned %d, expected %d.\n", j, i);
else
OSD_Printf(" sp->ptr and cmpstrbuf not identical!\n");
return -1;
}
continue;
}
ds_get(sp, (const void **)&ptr, &cnt);
if (cnt < 0) { OSD_Printf("rsd: cnt<0... wtf?\n"); return -1; }
if (fil>=0)
{
mem = (dump && (sp->flags&DS_NOCHK)==0) ? dump : ptr;
if ((sp->flags&DS_CNTMASK)==0 && sp->size*cnt<=savegame_comprthres)
{
i = kread(fil, mem, cnt*sp->size);
j = cnt*sp->size;
}
else
{
i = kdfread(mem, sp->size, cnt, fil);
j = cnt;
}
if (i!=j)
{
OSD_Printf("rsd: spec=%p, sp=%p, mem=%p\n", spec, sp, mem);
OSD_Printf(" %s: read %d, expected %d!\n",
((sp->flags&DS_CNTMASK)==0 && sp->size*cnt<=savegame_comprthres)?
"UNCOMP":"COMPR", i, j);
if (i==-1)
OSD_Printf(" read: %s\n", strerror(errno));
return -1;
}
}
if (dump && (sp->flags&DS_NOCHK)==0)
{
Bmemcpy(ptr, dump, sp->size*cnt);
dump += sp->size*cnt;
}
}
if (dumpvar)
*dumpvar = dump;
return 0;
}
#define UINT(bits) uint##bits##_t
#define BYTES(bits) (bits>>3)
#define VAL(bits,p) (*(UINT(bits) *)(p))
static void docmpsd(const void *ptr, void *dump, uint32_t size, uint32_t cnt, uint8_t **diffvar)
{
uint8_t *retdiff = *diffvar;
// Hail to the C preprocessor, baby!
#define CPSINGLEVAL(Datbits) \
if (VAL(Datbits, ptr) != VAL(Datbits, dump)) \
{ \
VAL(Datbits, retdiff) = VAL(Datbits, dump) = VAL(Datbits, ptr); \
*diffvar = retdiff+BYTES(Datbits); \
}
if (cnt==1)
switch (size)
{
case 8: CPSINGLEVAL(64); return;
case 4: CPSINGLEVAL(32); return;
case 2: CPSINGLEVAL(16); return;
case 1: CPSINGLEVAL(8); return;
}
#define CPELTS(Idxbits, Datbits) do \
{ \
for (i=0; i<nelts; i++) \
{ \
if (*p!=*op) \
{ \
*op = *p; \
VAL(Idxbits, retdiff) = i; \
retdiff += BYTES(Idxbits); \
VAL(Datbits, retdiff) = *p; \
retdiff += BYTES(Datbits); \
} \
p++; \
op++; \
} \
VAL(Idxbits, retdiff) = -1; \
retdiff += BYTES(Idxbits); \
} while (0)
#define CPDATA(Datbits) do \
{ \
const UINT(Datbits) *p=ptr; \
UINT(Datbits) *op=dump; \
uint32_t i, nelts=(size*cnt)/BYTES(Datbits); \
if (nelts>65536) \
CPELTS(32,Datbits); \
else if (nelts>256) \
CPELTS(16,Datbits); \
else \
CPELTS(8,Datbits); \
} while (0)
if (size==8)
CPDATA(64);
else if ((size&3)==0)
CPDATA(32);
else if ((size&1)==0)
CPDATA(16);
else
CPDATA(8);
*diffvar = retdiff;
return;
#undef CPELTS
#undef CPSINGLEVAL
#undef CPDATA
}
// get the number of elements to be monitored for changes
static int32_t getnumvar(const dataspec_t *spec)
{
int32_t n=0;
for (; spec->flags!=DS_END; spec++)
n += (spec->flags&(DS_STRING|DS_CMP|DS_NOCHK|DS_SAVEFN|DS_LOADFN) ? 0 : 1);
return n;
}
// update dump at *dumpvar with new state and write diff to *diffvar
static void cmpspecdata(const dataspec_t *spec, uint8_t **dumpvar, uint8_t **diffvar)
{
const void *ptr;
uint8_t *dump=*dumpvar, *diff=*diffvar, *tmptr;
const dataspec_t *sp=spec;
int32_t cnt, eltnum=0, nbytes=(getnumvar(spec)+7)>>3, l=Bstrlen(spec->ptr);
Bmemcpy(diff, spec->ptr, l);
diff+=l;
while (nbytes--)
*(diff++) = 0; // the bitmap of indices which elements of spec have changed go here
for (sp++; sp->flags!=DS_END; sp++)
{
if ((sp->flags&(DS_NOCHK|DS_STRING|DS_CMP)))
continue;
if (sp->flags&(DS_LOADFN|DS_SAVEFN))
{
if ((sp->flags&(DS_PROTECTFN))==0)
(*(void ( *)())sp->ptr)();
continue;
}
ds_get(sp, &ptr, &cnt);
if (cnt < 0) { OSD_Printf("csd: cnt=%d, f=0x%x\n", cnt, sp->flags); continue; }
tmptr = diff;
docmpsd(ptr, dump, sp->size, cnt, &diff);
if (diff != tmptr)
(*diffvar + l)[eltnum>>3] |= 1<<(eltnum&7);
dump += sp->size*cnt;
eltnum++;
}
*diffvar = diff;
*dumpvar = dump;
return;
}
#define VALOFS(bits,p,ofs) (*(((UINT(bits) *)(p)) + (ofs)))
// apply diff to dump, not to state! state is restored from dump afterwards.
static int32_t applydiff(const dataspec_t *spec, uint8_t **dumpvar, uint8_t **diffvar)
{
uint8_t *dumptr=*dumpvar, *diffptr=*diffvar;
const dataspec_t *sp=spec;
int32_t cnt, eltnum=-1, nbytes=(getnumvar(spec)+7)>>3, l=Bstrlen(spec->ptr);
if (Bmemcmp(diffptr, spec->ptr, l)) // check STRING magic (sync check)
return 1;
diffptr += l+nbytes;
for (sp++; sp->flags!=DS_END; sp++)
{
if ((sp->flags&(DS_NOCHK|DS_STRING|DS_CMP|DS_LOADFN|DS_SAVEFN)))
continue;
cnt = ds_getcnt(sp);
if (cnt < 0) return 1;
eltnum++;
if (((*diffvar + l)[eltnum>>3]&(1<<(eltnum&7))) == 0)
{
dumptr += sp->size*cnt;
continue;
}
// ----------
#define CPSINGLEVAL(Datbits) \
VAL(Datbits, dumptr) = VAL(Datbits, diffptr); \
diffptr += BYTES(Datbits); \
dumptr += BYTES(Datbits)
if (cnt==1)
{
switch (sp->size)
{
case 8: CPSINGLEVAL(64); continue;
case 4: CPSINGLEVAL(32); continue;
case 2: CPSINGLEVAL(16); continue;
case 1: CPSINGLEVAL(8); continue;
}
}
#define CPELTS(Idxbits, Datbits) do \
{ \
UINT(Idxbits) idx; \
goto readidx_##Idxbits##_##Datbits; \
do \
{ \
VALOFS(Datbits, dumptr, idx) = VAL(Datbits, diffptr); \
diffptr += BYTES(Datbits); \
readidx_##Idxbits##_##Datbits: \
idx = VAL(Idxbits, diffptr); \
diffptr += BYTES(Idxbits); \
} while ((int##Idxbits##_t)idx != -1); \
} while (0)
#define CPDATA(Datbits) do \
{ \
uint32_t nelts=(sp->size*cnt)/BYTES(Datbits); \
if (nelts>65536) \
CPELTS(32,Datbits); \
else if (nelts>256) \
CPELTS(16,Datbits); \
else \
CPELTS(8,Datbits); \
} while (0)
if (sp->size==8)
CPDATA(64);
else if ((sp->size&3)==0)
CPDATA(32);
else if ((sp->size&1)==0)
CPDATA(16);
else
CPDATA(8);
dumptr += sp->size*cnt;
// ----------
#undef CPELTS
#undef CPSINGLEVAL
#undef CPDATA
}
*diffvar = diffptr;
*dumpvar = dumptr;
return 0;
}
#undef VAL
#undef VALOFS
#undef BYTES
#undef UINT
// calculate size needed for dump
static uint32_t calcsz(const dataspec_t *spec)
{
const dataspec_t *sp=spec;
int32_t cnt;
uint32_t dasiz=0;
for (; sp->flags!=DS_END; sp++)
{
// DS_STRINGs are used as sync checks in the diffs but not in the dump
if ((sp->flags&(DS_CMP|DS_NOCHK|DS_SAVEFN|DS_LOADFN|DS_STRING)))
continue;
cnt = ds_getcnt(sp);
if (cnt<=0) continue;
dasiz += cnt*sp->size;
}
return dasiz;
}
static void sv_postudload();
#ifdef USE_OPENGL
static void sv_prespriteextsave();
static void sv_postspriteext();
#endif
#ifdef YAX_ENABLE
static void sv_postyaxload();
#endif
static void sv_calcbitptrsize();
static void sv_prescriptsave_once();
static void sv_prescriptload_once();
static void sv_postscript_once();
static void sv_preactordatasave();
static void sv_postactordata();
static void sv_preanimateptrsave();
static void sv_postanimateptr();
static void sv_prequote();
static void sv_quotesave();
static void sv_quoteload();
static void sv_prequoteredef();
static void sv_quoteredefsave();
static void sv_quoteredefload();
static void sv_postquoteredef();
static void sv_restsave();
static void sv_restload();
#define SVARDATALEN \
((sizeof(g_player[0].user_name)+sizeof(g_player[0].pcolor)+sizeof(g_player[0].pteam) \
+sizeof(g_player[0].frags)+sizeof(DukePlayer_t))*MAXPLAYERS + sizeof(_prlight)*PR_MAXLIGHTS + sizeof(lightcount))
#if !defined SAMESIZE_ACTOR_T
static uint8_t savegame_bitmap[MAXSPRITES>>3][3];
#endif
static uint32_t savegame_bitptrsize;
static uint8_t savegame_quotedef[MAXQUOTES>>3];
static char(*savegame_quotes)[MAXQUOTELEN];
static char(*savegame_quoteredefs)[MAXQUOTELEN];
static uint8_t savegame_restdata[SVARDATALEN];
static const dataspec_t svgm_udnetw[] =
{
{ DS_STRING, "blK:udnt", 0, 1 },
{ 0, &ud.multimode, sizeof(ud.multimode), 1 },
{ 0, &g_numPlayerSprites, sizeof(g_numPlayerSprites), 1 },
{ 0, &g_playerSpawnPoints, sizeof(g_playerSpawnPoints), 1 },
{ DS_NOCHK, &ud.volume_number, sizeof(ud.volume_number), 1 },
{ DS_NOCHK, &ud.level_number, sizeof(ud.level_number), 1 },
{ DS_NOCHK, &ud.player_skill, sizeof(ud.player_skill), 1 },
{ DS_NOCHK, &ud.from_bonus, sizeof(ud.from_bonus), 1 },
{ DS_NOCHK, &ud.secretlevel, sizeof(ud.secretlevel), 1 },
{ DS_NOCHK, &ud.respawn_monsters, sizeof(ud.respawn_monsters), 1 },
{ DS_NOCHK, &ud.respawn_items, sizeof(ud.respawn_items), 1 },
{ DS_NOCHK, &ud.respawn_inventory, sizeof(ud.respawn_inventory), 1 },
{ 0, &ud.god, sizeof(ud.god), 1 },
{ 0, &ud.auto_run, sizeof(ud.auto_run), 1 },
// { DS_NOCHK, &ud.crosshair, sizeof(ud.crosshair), 1 },
{ DS_NOCHK, &ud.monsters_off, sizeof(ud.monsters_off), 1 },
{ DS_NOCHK, &ud.last_level, sizeof(ud.last_level), 1 },
{ 0, &ud.eog, sizeof(ud.eog), 1 },
{ DS_NOCHK, &ud.coop, sizeof(ud.coop), 1 },
{ DS_NOCHK, &ud.marker, sizeof(ud.marker), 1 },
{ DS_NOCHK, &ud.ffire, sizeof(ud.ffire), 1 },
{ DS_NOCHK, &ud.noexits, sizeof(ud.noexits), 1 },
{ DS_NOCHK, &ud.playerai, sizeof(ud.playerai), 1 },
{ 0, &ud.pause_on, sizeof(ud.pause_on), 1 },
{ DS_NOCHK, &currentboardfilename[0], BMAX_PATH, 1 },
{ DS_LOADFN, (void *)&sv_postudload, 0, 1 },
{ 0, connectpoint2, sizeof(connectpoint2), 1 },
{ 0, &randomseed, sizeof(randomseed), 1 },
{ 0, &g_globalRandom, sizeof(g_globalRandom), 1 },
// { 0, &lockclock_dummy, sizeof(lockclock), 1 },
{ DS_END, 0, 0, 0 }
};
static const dataspec_t svgm_secwsp[] =
{
{ DS_STRING, "blK:swsp", 0, 1 },
{ DS_NOCHK, &numwalls, sizeof(numwalls), 1 },
{ DS_DYNAMIC|DS_CNT(numwalls), &wall, sizeof(walltype), (intptr_t)&numwalls },
{ DS_NOCHK, &numsectors, sizeof(numsectors), 1 },
{ DS_DYNAMIC|DS_CNT(numsectors), &sector, sizeof(sectortype), (intptr_t)&numsectors },
{ DS_DYNAMIC, &sprite, sizeof(spritetype), MAXSPRITES },
#ifdef YAX_ENABLE
{ DS_NOCHK, &numyaxbunches, sizeof(numyaxbunches), 1 },
{ DS_CNT(numsectors), yax_bunchnum, sizeof(yax_bunchnum[0]), (intptr_t)&numsectors },
{ DS_CNT(numwalls), yax_nextwall, sizeof(yax_nextwall[0]), (intptr_t)&numwalls },
{ DS_LOADFN|DS_PROTECTFN, (void *)&sv_postyaxload, 0, 1 },
#endif
{ 0, &headspritesect[0], sizeof(headspritesect[0]), MAXSECTORS+1 },
{ 0, &prevspritesect[0], sizeof(prevspritesect[0]), MAXSPRITES },
{ 0, &nextspritesect[0], sizeof(nextspritesect[0]), MAXSPRITES },
{ 0, &headspritestat[0], sizeof(headspritestat[0]), MAXSTATUS+1 },
{ 0, &prevspritestat[0], sizeof(prevspritestat[0]), MAXSPRITES },
{ 0, &nextspritestat[0], sizeof(nextspritestat[0]), MAXSPRITES },
#ifdef USE_OPENGL
{ DS_SAVEFN, (void *)&sv_prespriteextsave, 0, 1 },
#endif
{ DS_DYNAMIC, &spriteext, sizeof(spriteext_t), MAXSPRITES },
#ifdef USE_OPENGL
{ DS_SAVEFN|DS_LOADFN, (void *)&sv_postspriteext, 0, 1 },
#endif
{ DS_NOCHK, &SpriteFlags[0], sizeof(SpriteFlags[0]), MAXTILES },
{ DS_NOCHK, &SpriteCacheList[0], sizeof(SpriteCacheList[0]), MAXTILES },
{ 0, &DynamicTileMap[0], sizeof(DynamicTileMap[0]), MAXTILES }, // NOCHK?
{ DS_NOCHK, &ActorType[0], sizeof(uint8_t), MAXTILES },
{ DS_NOCHK, &g_numCyclers, sizeof(g_numCyclers), 1 },
{ DS_CNT(g_numCyclers), &cyclers[0][0], sizeof(cyclers[0]), (intptr_t)&g_numCyclers },
{ DS_NOCHK, &g_numAnimWalls, sizeof(g_numAnimWalls), 1 },
{ DS_CNT(g_numAnimWalls), &animwall, sizeof(animwall[0]), (intptr_t)&g_numAnimWalls },
{ DS_NOCHK, &g_mirrorCount, sizeof(g_mirrorCount), 1 },
{ DS_NOCHK, &g_mirrorWall[0], sizeof(g_mirrorWall[0]), sizeof(g_mirrorWall)/sizeof(g_mirrorWall[0]) },
{ DS_NOCHK, &g_mirrorSector[0], sizeof(g_mirrorSector[0]), sizeof(g_mirrorSector)/sizeof(g_mirrorSector[0]) },
// projectiles
{ 0, &SpriteProjectile[0], sizeof(projectile_t), MAXSPRITES },
{ 0, &ProjectileData[0], sizeof(projectile_t), MAXTILES },
{ DS_NOCHK, &DefaultProjectileData[0], sizeof(projectile_t), MAXTILES },
{ 0, &everyothertime, sizeof(everyothertime), 1 },
{ DS_END, 0, 0, 0 }
};
static const dataspec_t svgm_script[] =
{
{ DS_STRING, "blK:scri", 0, 1 },
{ DS_NOCHK, &g_scriptSize, sizeof(g_scriptSize), 1 },
{ DS_SAVEFN|DS_LOADFN|DS_NOCHK, (void *)&sv_calcbitptrsize, 0, 1 },
{ DS_DYNAMIC|DS_CNT(savegame_bitptrsize)|DS_NOCHK, &bitptr, sizeof(bitptr[0]), (intptr_t)&savegame_bitptrsize },
{ DS_SAVEFN|DS_NOCHK, (void *)&sv_prescriptsave_once, 0, 1 },
{ DS_NOCHK, &actorscrptr[0], sizeof(actorscrptr[0]), MAXTILES },
{ DS_LOADFN|DS_NOCHK, (void *)&sv_prescriptload_once, 0, 1 },
{ DS_DYNAMIC|DS_CNT(g_scriptSize)|DS_NOCHK, &script, sizeof(script[0]), (intptr_t)&g_scriptSize },
{ DS_NOCHK, &actorLoadEventScrptr[0], sizeof(actorLoadEventScrptr[0]), MAXTILES },
// { DS_NOCHK, &apScriptGameEvent[0], sizeof(apScriptGameEvent[0]), MAXGAMEEVENTS },
{ DS_SAVEFN|DS_LOADFN|DS_NOCHK, (void *)&sv_postscript_once, 0, 1 },
{ DS_SAVEFN, (void *)&sv_preactordatasave, 0, 1 },
#if !defined SAMESIZE_ACTOR_T
{ 0, &savegame_bitmap, sizeof(savegame_bitmap), 1 },
#endif
{ 0, &actor[0], sizeof(actor_t), MAXSPRITES },
{ DS_SAVEFN|DS_LOADFN, (void *)&sv_postactordata, 0, 1 },
{ DS_END, 0, 0, 0 }
};
static const dataspec_t svgm_anmisc[] =
{
{ DS_STRING, "blK:anms", 0, 1 },
{ 0, &g_animateCount, sizeof(g_animateCount), 1 },
{ 0, &animatesect[0], sizeof(animatesect[0]), MAXANIMATES },
{ 0, &animategoal[0], sizeof(animategoal[0]), MAXANIMATES },
{ 0, &animatevel[0], sizeof(animatevel[0]), MAXANIMATES },
{ DS_SAVEFN, (void *)&sv_preanimateptrsave, 0, 1 },
{ 0, &animateptr[0], sizeof(animateptr[0]), MAXANIMATES },
{ DS_SAVEFN|DS_LOADFN , (void *)&sv_postanimateptr, 0, 1 },
{ 0, &camsprite, sizeof(camsprite), 1 },
{ 0, &msx[0], sizeof(msx[0]), sizeof(msx)/sizeof(msx[0]) },
{ 0, &msy[0], sizeof(msy[0]), sizeof(msy)/sizeof(msy[0]) },
{ 0, &g_spriteDeleteQueuePos, sizeof(g_spriteDeleteQueuePos), 1 },
{ DS_NOCHK, &g_spriteDeleteQueueSize, sizeof(g_spriteDeleteQueueSize), 1 },
{ DS_CNT(g_spriteDeleteQueueSize), &SpriteDeletionQueue[0], sizeof(int16_t), (intptr_t)&g_spriteDeleteQueueSize },
{ 0, &show2dsector[0], sizeof(uint8_t), MAXSECTORS>>3 },
{ DS_NOCHK, &g_numClouds, sizeof(g_numClouds), 1 },
{ 0, &clouds[0], sizeof(clouds), 1 },
{ 0, &cloudx[0], sizeof(cloudx), 1 },
{ 0, &cloudy[0], sizeof(cloudy), 1 },
{ DS_NOCHK, &parallaxyscale, sizeof(parallaxyscale), 1 },
{ 0, &pskybits, sizeof(pskybits), 1 },
{ 0, &pskyoff[0], sizeof(pskyoff[0]), MAXPSKYTILES },
{ 0, &g_earthquakeTime, sizeof(g_earthquakeTime), 1 },
{ DS_SAVEFN|DS_LOADFN|DS_NOCHK, (void *)sv_prequote, 0, 1 },
{ DS_SAVEFN, (void *)&sv_quotesave, 0, 1 },
{ DS_NOCHK, &savegame_quotedef, sizeof(savegame_quotedef), 1 }, // quotes can change during runtime, but new quote numbers cannot be allocated
{ DS_DYNAMIC, &savegame_quotes, MAXQUOTELEN, MAXQUOTES },
{ DS_LOADFN, (void *)&sv_quoteload, 0, 1 },
{ DS_NOCHK, &g_numQuoteRedefinitions, sizeof(g_numQuoteRedefinitions), 1 },
{ DS_NOCHK|DS_SAVEFN|DS_LOADFN, (void *)&sv_prequoteredef, 0, 1 },
{ DS_NOCHK|DS_SAVEFN, (void *)&sv_quoteredefsave, 0, 1 }, // quote redefinitions replace quotes at runtime, but cannot be changed after CON compilation
{ DS_NOCHK|DS_DYNAMIC|DS_CNT(g_numQuoteRedefinitions), &savegame_quoteredefs, MAXQUOTELEN, (intptr_t)&g_numQuoteRedefinitions },
{ DS_NOCHK|DS_LOADFN, (void *)&sv_quoteredefload, 0, 1 },
{ DS_NOCHK|DS_SAVEFN|DS_LOADFN, (void *)&sv_postquoteredef, 0, 1 },
{ DS_SAVEFN, (void *)&sv_restsave, 0, 1 },
{ 0, savegame_restdata, 1, sizeof(savegame_restdata) }, // sz/cnt swapped for kdfread
{ DS_LOADFN, (void *)&sv_restload, 0, 1 },
{ DS_STRING, "savegame_end", 0, 1 },
{ DS_END, 0, 0, 0 }
};
static dataspec_t *svgm_vars=NULL;
static uint8_t *dosaveplayer2(int32_t spot, FILE *fil, uint8_t *mem);
static int32_t doloadplayer2(int32_t spot, int32_t fil, uint8_t **memptr);
static void postloadplayer1();
static void postloadplayer2();
// SVGM snapshot system
static uint32_t svsnapsiz;
static uint8_t *svsnapshot, *svinitsnap;
static uint32_t svdiffsiz;
static uint8_t *svdiff;
#include "gamedef.h"
#define SV_SKIPMASK (/*GAMEVAR_SYSTEM|*/GAMEVAR_READONLY|GAMEVAR_INTPTR| \
GAMEVAR_SHORTPTR|GAMEVAR_CHARPTR /*|GAMEVAR_NORESET*/ |GAMEVAR_SPECIAL)
// setup gamevar data spec for snapshotting and diffing... gamevars must be loaded when called
static void sv_makevarspec()
{
static char *magic = "blK:vars";
int32_t i, j, numsavedvars=0, per;
if (svgm_vars)
Bfree(svgm_vars);
for (i=0; i<g_gameVarCount; i++)
numsavedvars += (aGameVars[i].dwFlags&SV_SKIPMASK) ? 0 : 1;
svgm_vars = Bmalloc((numsavedvars+g_gameArrayCount+2)*sizeof(dataspec_t));
svgm_vars[0].flags = DS_STRING;
svgm_vars[0].ptr = magic;
svgm_vars[0].cnt = 1;
j=1;
for (i=0; i<g_gameVarCount; i++)
{
if (aGameVars[i].dwFlags&SV_SKIPMASK)
continue;
per = aGameVars[i].dwFlags&GAMEVAR_USER_MASK;
svgm_vars[j].flags = 0;
svgm_vars[j].ptr = (per==0) ? &aGameVars[i].val.lValue : aGameVars[i].val.plValues;
svgm_vars[j].size = sizeof(intptr_t);
svgm_vars[j].cnt = (per==0) ? 1 : (per==GAMEVAR_PERPLAYER ? MAXPLAYERS : MAXSPRITES);
j++;
}
for (i=0; i<g_gameArrayCount; i++)
{
svgm_vars[j].flags = 0;
svgm_vars[j].ptr = aGameArrays[i].plValues;
svgm_vars[j].size = sizeof(aGameArrays[0].plValues[0]);
svgm_vars[j].cnt = aGameArrays[i].size; // assumed constant throughout demo, i.e. no RESIZEARRAY
j++;
}
svgm_vars[j].flags = DS_END;
}
void sv_freemem()
{
if (svsnapshot)
Bfree(svsnapshot), svsnapshot=NULL;
if (svinitsnap)
Bfree(svinitsnap), svinitsnap=NULL;
if (svdiff)
Bfree(svdiff), svdiff=NULL;
// if (svgm_vars)
// Bfree(svgm_vars), svgm_vars=NULL;
}
static int32_t doallocsnap(int32_t allocinit)
{
sv_freemem();
svsnapshot = Bmalloc(svsnapsiz);
if (allocinit)
svinitsnap = Bmalloc(svsnapsiz);
svdiffsiz = svsnapsiz; // theoretically it's less than could be needed in the worst case, but practically it's overkill
svdiff = Bmalloc(svdiffsiz);
if (svsnapshot==NULL || (allocinit && svinitsnap==NULL) || svdiff==NULL)
{
sv_freemem();
return 1;
}
return 0;
}
int32_t sv_saveandmakesnapshot(FILE *fil, int32_t recdiffs, int32_t diffcompress, int32_t synccompress)
{
uint8_t *p, tb;
uint16_t ts;
fwrite("EDuke32demo", 11, 1, fil);//0 11b
tb = SV_MAJOR_VER;
fwrite(&tb, sizeof(tb), 1, fil); //11 1b
tb = SV_MINOR_VER;
fwrite(&tb, sizeof(tb), 1, fil); //12 1b
tb = sizeof(intptr_t);
fwrite(&tb, sizeof(tb), 1, fil); //13 1b
ts = BYTEVERSION;
fwrite(&ts, sizeof(ts), 1, fil); //14 2b
savegame_comprthres = SV_DEFAULTCOMPRTHRES;
fwrite(&savegame_comprthres, sizeof(savegame_comprthres), 1, fil); //16 1b
tb = recdiffs;
fwrite(&tb, sizeof(tb), 1, fil); //17 1b
savegame_diffcompress = (uint8_t)diffcompress;
fwrite(&savegame_diffcompress, sizeof(savegame_diffcompress), 1, fil); //18 1b
tb = (uint8_t)synccompress;
fwrite(&tb, sizeof(tb), 1, fil); //19 1b
sv_makevarspec();
svsnapsiz = 0;
fwrite(&svsnapsiz, sizeof(svsnapsiz), 1, fil); // 20 4b record count for demos
svsnapsiz = calcsz(svgm_vars);
svsnapsiz += calcsz(svgm_udnetw) + calcsz(svgm_secwsp) + calcsz(svgm_script) + calcsz(svgm_anmisc);
fwrite(&svsnapsiz, sizeof(svsnapsiz), 1, fil); // 24 4b
OSD_Printf("sv_saveandmakesnapshot: size: %d bytes.\n", svsnapsiz);
if (doallocsnap(0))
{
OSD_Printf("sv_saveandmakesnapshot: failed allocating memory.\n");
return 1;
}
p = dosaveplayer2(-1, fil, svsnapshot);
if (p != svsnapshot+svsnapsiz)
OSD_Printf("sv_saveandmakesnapshot: ptr-(snapshot end)=%d!\n", (int32_t)(p-(svsnapshot+svsnapsiz)));
return 0;
}
int32_t sv_loadsnapshot(int32_t fil, int32_t *ret_hasdiffs, int32_t *ret_demoticcnt, int32_t *ret_synccompress)
{
uint8_t *p, tmpbuf[11];
int32_t i;
if (kread(fil, tmpbuf, 11) != 11) goto corrupt;
if (Bmemcmp(tmpbuf, "EDuke32demo", 11))
{
OSD_Printf("Missing demo header.\n");
return 1;
}
if (kread(fil, tmpbuf, 9) != 9) goto corrupt;
if (tmpbuf[0] != SV_MAJOR_VER || tmpbuf[1] != SV_MINOR_VER || *(uint16_t *)&tmpbuf[3] != BYTEVERSION)
{
OSD_Printf("Incompatible demo version. Expected %d.%d.%d, found %d.%d.%d\n",
SV_MAJOR_VER, SV_MINOR_VER, BYTEVERSION,
(int)tmpbuf[0], (int)tmpbuf[1], (int)(*(uint16_t *)&tmpbuf[3]));
return 2;
}
if (tmpbuf[2] != sizeof(intptr_t))
{
OSD_Printf("Demo incompatible. Expected pointer size %d, found %d.\n",
(int32_t)sizeof(intptr_t), (int32_t)tmpbuf[2]);
return 3;
}
savegame_comprthres = tmpbuf[5];
*ret_hasdiffs = (int32_t)tmpbuf[6];
savegame_diffcompress = tmpbuf[7];
*ret_synccompress = (int32_t)tmpbuf[8];
if (kread(fil, ret_demoticcnt, sizeof(*ret_demoticcnt)) != sizeof(*ret_demoticcnt)) goto corrupt;
if (kread(fil, &svsnapsiz, sizeof(svsnapsiz)) != sizeof(svsnapsiz)) goto corrupt;
OSD_Printf("sv_loadsnapshot: size: %d bytes.\n", svsnapsiz);
if (doallocsnap(1))
{
OSD_Printf("sv_loadsnapshot: failed allocating memory.\n");
return 4;
}
p = svsnapshot;
i = doloadplayer2(-1, fil, &p);
if (i)
{
OSD_Printf("sv_loadsnapshot: doloadplayer2() returned %d.\n", i);
sv_freemem();
return 5;
}
Bmemcpy(svinitsnap, svsnapshot, svsnapsiz);
postloadplayer1();
postloadplayer2();
if (p != svsnapshot+svsnapsiz)
{
OSD_Printf("sv_loadsnapshot: internal error: p-(snapshot end)=%d!\n",
(int32_t)(p-(svsnapshot+svsnapsiz)));
sv_freemem();
return 6;
}
return 0;
corrupt:
OSD_Printf("Demo header corrupt.\n");
return 8;
}
uint32_t sv_writediff(FILE *fil)
{
uint8_t *p=svsnapshot, *d=svdiff;
uint32_t diffsiz;
cmpspecdata(svgm_udnetw, &p, &d);
cmpspecdata(svgm_secwsp, &p, &d);
cmpspecdata(svgm_script, &p, &d);
cmpspecdata(svgm_anmisc, &p, &d);
cmpspecdata(svgm_vars, &p, &d);
if (p != svsnapshot+svsnapsiz)
OSD_Printf("sv_writediff: dump+siz=%p, p=%p!\n", svsnapshot+svsnapsiz, p);
diffsiz = d-svdiff;
fwrite("dIfF",4,1,fil);
fwrite(&diffsiz, sizeof(diffsiz), 1, fil);
if (savegame_diffcompress)
dfwrite(svdiff, 1, diffsiz, fil); // cnt and sz swapped
else
fwrite(svdiff, 1, diffsiz, fil);
return diffsiz;
}
int32_t sv_readdiff(int32_t fil)
{
uint8_t *p=svsnapshot, *d=svdiff, i=0; //, tbuf[4];
int32_t diffsiz;
#if 0 // handled by the caller
if (kread(fil, tbuf, 4)!=4)
return -1;
if (Bmemcmp(tbuf, "dIfF", 4))
return 4;
#endif
if (kread(fil, &diffsiz, sizeof(uint32_t))!=sizeof(uint32_t))
return -1;
if (savegame_diffcompress)
{
if (kdfread(svdiff, 1, diffsiz, fil) != diffsiz) // cnt and sz swapped
return -2;
}
else
{
if (kread(fil, svdiff, diffsiz) != diffsiz)
return -2;
}
if (applydiff(svgm_udnetw, &p, &d)) return -3;
if (applydiff(svgm_secwsp, &p, &d)) return -4;
if (applydiff(svgm_script, &p, &d)) return -5;
if (applydiff(svgm_anmisc, &p, &d)) return -6;
if (applydiff(svgm_vars, &p, &d)) return -7;
if (p!=svsnapshot+svsnapsiz)
i|=1;
if (d!=svdiff+diffsiz)
i|=2;
if (i)
OSD_Printf("sv_readdiff: p=%p, svsnapshot+svsnapsiz=%p; d=%p, svdiff+diffsiz=%p",
p, svsnapshot+svsnapsiz, d, svdiff+diffsiz);
return i;
}
// SVGM data description
static void sv_postudload()
{
// Bmemcpy(&boardfilename[0], &currentboardfilename[0], BMAX_PATH);
#if 0
ud.m_level_number = ud.level_number;
ud.m_volume_number = ud.volume_number;
ud.m_player_skill = ud.player_skill;
ud.m_respawn_monsters = ud.respawn_monsters;
ud.m_respawn_items = ud.respawn_items;
ud.m_respawn_inventory = ud.respawn_inventory;
ud.m_monsters_off = ud.monsters_off;
ud.m_coop = ud.coop;
ud.m_marker = ud.marker;
ud.m_ffire = ud.ffire;
ud.m_noexits = ud.noexits;
#endif
}
//static int32_t lockclock_dummy;
#ifdef USE_OPENGL
static void sv_prespriteextsave()
{
int32_t i;
for (i=0; i<MAXSPRITES; i++)
if (spriteext[i].mdanimtims)
{
spriteext[i].mdanimtims -= mdtims;
if (spriteext[i].mdanimtims==0)
spriteext[i].mdanimtims++;
}
}
static void sv_postspriteext()
{
int32_t i;
for (i=0; i<MAXSPRITES; i++)
if (spriteext[i].mdanimtims)
spriteext[i].mdanimtims += mdtims;
}
#endif
#ifdef YAX_ENABLE
static void sv_postyaxload()
{
yax_update(numyaxbunches>0 ? 2 : 1);
}
#endif
static void sv_calcbitptrsize()
{
savegame_bitptrsize = (g_scriptSize+7)>>3;
}
static void sv_prescriptsave_once()
{
int32_t i;
for (i=0; i<g_scriptSize; i++)
if (bitptr[i>>3]&(BITPTR_POINTER<<(i&7)))
script[i] = (intptr_t)((intptr_t *)script[i] - &script[0]);
G_Util_PtrToIdx(actorscrptr, MAXTILES, script, P2I_FWD_NON0);
G_Util_PtrToIdx(actorLoadEventScrptr, MAXTILES, script, P2I_FWD_NON0);
}
static void sv_prescriptload_once()
{
if (script)
Bfree(script);
script = Bmalloc(g_scriptSize * sizeof(script[0]));
}
static void sv_postscript_once()
{
int32_t i;
G_Util_PtrToIdx(actorscrptr, MAXTILES, script, P2I_BACK_NON0);
G_Util_PtrToIdx(actorLoadEventScrptr, MAXTILES, script, P2I_BACK_NON0);
for (i=0; i<g_scriptSize; i++)
if (bitptr[i>>3]&(BITPTR_POINTER<<(i&7)))
script[i] = (intptr_t)(script[i] + &script[0]);
}
static void sv_preactordatasave()
{
int32_t i;
#if !defined SAMESIZE_ACTOR_T
intptr_t j=(intptr_t)&script[0], k=(intptr_t)&script[g_scriptSize];
Bmemset(savegame_bitmap, 0, sizeof(savegame_bitmap));
#endif
for (i=0; i<MAXSPRITES; i++)
{
actor[i].lightptr = NULL;
actor[i].lightId = -1;
#if !defined SAMESIZE_ACTOR_T
if (sprite[i].statnum==MAXSTATUS || actorscrptr[PN]==NULL) continue;
if (T2 >= j && T2 < k) savegame_bitmap[i>>3][0] |= 1<<(i&7), T2 -= j;
if (T5 >= j && T5 < k) savegame_bitmap[i>>3][1] |= 1<<(i&7), T5 -= j;
if (T6 >= j && T6 < k) savegame_bitmap[i>>3][2] |= 1<<(i&7), T6 -= j;
#endif
}
}
static void sv_postactordata()
{
int32_t i;
#if POLYMER
if (getrendermode() == 4)
polymer_resetlights();
#endif
for (i=0; i<MAXSPRITES; i++)
{
actor[i].lightptr = NULL;
actor[i].lightId = -1;
#if !defined SAMESIZE_ACTOR_T
if (sprite[i].statnum==MAXSTATUS || actorscrptr[PN]==NULL) continue;
if (savegame_bitmap[i>>3][0]&(1<<(i&7))) T2 += (intptr_t)script;
if (savegame_bitmap[i>>3][1]&(1<<(i&7))) T5 += (intptr_t)script;
if (savegame_bitmap[i>>3][2]&(1<<(i&7))) T6 += (intptr_t)script;
#endif
}
}
static void sv_preanimateptrsave()
{
G_Util_PtrToIdx(animateptr, g_animateCount, sector, P2I_FWD);
}
static void sv_postanimateptr()
{
G_Util_PtrToIdx(animateptr, g_animateCount, sector, P2I_BACK);
}
static void sv_prequote()
{
if (!savegame_quotes)
savegame_quotes = Bcalloc(MAXQUOTES, MAXQUOTELEN);
}
static void sv_quotesave()
{
int32_t i;
Bmemset(savegame_quotedef, 0, sizeof(savegame_quotedef));
for (i=0; i<MAXQUOTES; i++)
if (ScriptQuotes[i])
{
savegame_quotedef[i>>3] |= 1<<(i&7);
Bmemcpy(savegame_quotes[i], ScriptQuotes[i], MAXQUOTELEN);
}
}
static void sv_quoteload()
{
int32_t i;
for (i=0; i<MAXQUOTES; i++)
{
if (savegame_quotedef[i>>3]&(1<<(i&7)))
{
if (!ScriptQuotes[i])
ScriptQuotes[i] = Bcalloc(1,MAXQUOTELEN);
Bmemcpy(ScriptQuotes[i], savegame_quotes[i], MAXQUOTELEN);
}
}
}
static void sv_prequoteredef()
{
// "+1" needed for dfwrite which doesn't handle the src==NULL && cnt==0 case
savegame_quoteredefs = Bcalloc(g_numQuoteRedefinitions+1, MAXQUOTELEN);
}
static void sv_quoteredefsave()
{
int32_t i;
for (i=0; i<g_numQuoteRedefinitions; i++)
if (ScriptQuoteRedefinitions[i])
Bmemcpy(savegame_quoteredefs[i], ScriptQuoteRedefinitions[i], MAXQUOTELEN);
}
static void sv_quoteredefload()
{
int32_t i;
for (i=0; i<g_numQuoteRedefinitions; i++)
{
if (!ScriptQuoteRedefinitions[i])
ScriptQuoteRedefinitions[i] = Bcalloc(1,MAXQUOTELEN);
Bmemcpy(ScriptQuoteRedefinitions[i], savegame_quoteredefs[i], MAXQUOTELEN);
}
}
static void sv_postquoteredef()
{
Bfree(savegame_quoteredefs), savegame_quoteredefs=NULL;
}
static void sv_restsave()
{
int32_t i;
uint8_t *mem = savegame_restdata;
DukePlayer_t dummy_ps;
Bmemset(&dummy_ps, 0, sizeof(DukePlayer_t));
#define CPDAT(ptr,sz) Bmemcpy(mem, ptr, sz), mem+=sz
for (i=0; i<MAXPLAYERS; i++)
{
CPDAT(g_player[i].user_name, 32);
CPDAT(&g_player[i].pcolor, sizeof(g_player[0].pcolor));
CPDAT(&g_player[i].pteam, sizeof(g_player[0].pteam));
CPDAT(&g_player[i].frags[0], sizeof(g_player[0].frags));
if (g_player[i].ps)
CPDAT(g_player[i].ps, sizeof(DukePlayer_t));
else
CPDAT(&dummy_ps, sizeof(DukePlayer_t));
}
#if 0 // POLYMER this will have to wait...
CPDAT(&lightcount, sizeof(lightcount));
for (i=0; i<lightcount; i++)
{
CPDAT(&prlights[i], sizeof(_prlight));
((prlight_ *)(mem-sizeof(_prlight)))->planelist = NULL;
}
#endif
Bmemset(mem, 0, (savegame_restdata+SVARDATALEN)-mem);
#undef CPDAT
}
static void sv_restload()
{
int32_t i;
uint8_t *mem = savegame_restdata;
DukePlayer_t dummy_ps;
#define CPDAT(ptr,sz) Bmemcpy(ptr, mem, sz), mem+=sz
for (i=0; i<MAXPLAYERS; i++)
{
CPDAT(g_player[i].user_name, 32);
CPDAT(&g_player[i].pcolor, sizeof(g_player[0].pcolor));
CPDAT(&g_player[i].pteam, sizeof(g_player[0].pteam));
CPDAT(&g_player[i].frags[0], sizeof(g_player[0].frags));
if (g_player[i].ps)
CPDAT(g_player[i].ps, sizeof(DukePlayer_t));
else
CPDAT(&dummy_ps, sizeof(DukePlayer_t));
}
#if 0 // POLYMER
CPDAT(&lightcount, sizeof(lightcount));
for (i=0; i<lightcount; i++)
CPDAT(&prlights[i], sizeof(_prlight));
#endif
#undef CPDAT
}
#define SAVEWR(ptr, sz, cnt) do { if (fil) dfwrite(ptr,sz,cnt,fil); } while (0)
#define SAVEWRU(ptr, sz, cnt) do { if (fil) fwrite(ptr,sz,cnt,fil); } while (0)
#define PRINTSIZE(name) OSD_Printf(#name ": %d\n", (int32_t)(mem-tmem)), tmem=mem
static uint8_t *dosaveplayer2(int32_t spot, FILE *fil, uint8_t *mem)
{
uint8_t *tmem = mem;
mem=writespecdata(svgm_udnetw, fil, mem); // user settings, players & net
PRINTSIZE(ud);
if (spot>=0)
{
SAVEWRU(&ud.savegame[spot][0], 21, 1);
SAVEWRU("1", 1, 1);
if (!waloff[TILE_SAVESHOT])
{
walock[TILE_SAVESHOT] = 254;
allocache(&waloff[TILE_SAVESHOT],200*320,&walock[TILE_SAVESHOT]);
clearbuf((void *)waloff[TILE_SAVESHOT],(200*320)/4,0);
walock[TILE_SAVESHOT] = 1;
}
SAVEWR((char *)waloff[TILE_SAVESHOT], 320, 200);
}
else
{
char buf[21];
const time_t t=time(NULL);
struct tm *st;
Bsprintf(buf, "Eduke32 demo");
if (t>=0 && (st = localtime(&t)))
Bsprintf(buf, "Edemo32 %04d%02d%02d", st->tm_year+1900, st->tm_mon+1, st->tm_mday);
SAVEWRU(&buf, 21, 1);
SAVEWRU("\0", 1, 1); // demos don't save screenshot
}
mem=writespecdata(svgm_secwsp, fil, mem); // sector, wall, sprite
PRINTSIZE(sws);
mem=writespecdata(svgm_script, fil, mem); // script
PRINTSIZE(script);
mem=writespecdata(svgm_anmisc, fil, mem); // animates, quotes & misc.
PRINTSIZE(animisc);
Gv_WriteSave(fil, 1); // gamevars
mem=writespecdata(svgm_vars, 0, mem);
PRINTSIZE(vars);
return mem;
}
#define LOADRD(ptr, sz, cnt) (kdfread(ptr,sz,cnt,fil)!=(cnt))
#define LOADRDU(ptr, sz, cnt) (kread(fil,ptr,(sz)*(cnt))!=(sz)*(cnt))
static int32_t doloadplayer2(int32_t spot, int32_t fil, uint8_t **memptr)
{
uint8_t *mem = memptr ? *memptr : NULL, *tmem=mem;
char tbuf[21];
int32_t i;
if (readspecdata(svgm_udnetw, fil, &mem))
return -2;
PRINTSIZE(ud);
if (spot >= 0 && ud.multimode!=numplayers)
return 2;
if (spot<0 || numplayers > 1)
{
if (LOADRDU(&tbuf, 21, 1)) return -3;
}
else if (LOADRDU(&ud.savegame[spot][0], 21, 1)) return -3;
else ud.savegame[spot][19] = 0;
if (LOADRDU(tbuf, 1, 1)) return -3;
if (tbuf[0])
{
//Fake read because lseek won't work with compression
walock[TILE_LOADSHOT] = 1;
if (waloff[TILE_LOADSHOT] == 0) allocache(&waloff[TILE_LOADSHOT],320*200,&walock[TILE_LOADSHOT]);
tilesizx[TILE_LOADSHOT] = 200;
tilesizy[TILE_LOADSHOT] = 320;
if (LOADRD((char *)waloff[TILE_LOADSHOT], 320, 200)) return -3;
invalidatetile(TILE_LOADSHOT,0,255);
}
if (readspecdata(svgm_secwsp, fil, &mem)) return -4;
PRINTSIZE(sws);
if (readspecdata(svgm_script, fil, &mem)) return -5;
PRINTSIZE(script);
if (readspecdata(svgm_anmisc, fil, &mem)) return -6;
PRINTSIZE(animisc);
if (Gv_ReadSave(fil, 1)) return -7;
sv_makevarspec();
if (mem)
for (i=1; svgm_vars[i].flags!=DS_END; i++)
{
Bmemcpy(mem, svgm_vars[i].ptr, svgm_vars[i].size*svgm_vars[i].cnt); // careful! works because there are no DS_DYNAMIC's!
mem += svgm_vars[i].size*svgm_vars[i].cnt;
}
PRINTSIZE(vars);
if (memptr)
*memptr = mem;
return 0;
}
int32_t sv_updatestate(int32_t frominit)
{
uint8_t *p = svsnapshot, *pbeg=p;
if (frominit)
Bmemcpy(svsnapshot, svinitsnap, svsnapsiz);
if (readspecdata(svgm_udnetw, -1, &p)) return -2;
if (readspecdata(svgm_secwsp, -1, &p)) return -4;
if (readspecdata(svgm_script, -1, &p)) return -5;
if (readspecdata(svgm_anmisc, -1, &p)) return -6;
if (readspecdata(svgm_vars, -1, &p)) return -8;
if (p != pbeg+svsnapsiz)
OSD_Printf("sv_updatestate: ptr-(snapshot end)=%d\n", (int32_t)(p-(pbeg+svsnapsiz)));
if (frominit)
{
postloadplayer1();
postloadplayer2();
}
return 0;
}
static void postloadplayer1()
{
int32_t i;
//1
if (g_player[myconnectindex].ps->over_shoulder_on != 0)
{
g_cameraDistance = 0;
g_cameraClock = 0;
g_player[myconnectindex].ps->over_shoulder_on = 1;
}
screenpeek = myconnectindex;
//2
//3
P_UpdateScreenPal(g_player[myconnectindex].ps);
//4
#if 0
if (ud.lockout == 0)
{
for (i=0; i<g_numAnimWalls; i++)
if (wall[animwall[i].wallnum].extra >= 0)
wall[animwall[i].wallnum].picnum = wall[animwall[i].wallnum].extra;
}
else
{
for (i=0; i<g_numAnimWalls; i++)
switch (DynamicTileMap[wall[animwall[i].wallnum].picnum])
{
case FEMPIC1__STATIC:
wall[animwall[i].wallnum].picnum = BLANKSCREEN;
break;
case FEMPIC2__STATIC:
case FEMPIC3__STATIC:
wall[animwall[i].wallnum].picnum = SCREENBREAK6;
break;
}
}
#endif
//5
G_ResetInterpolations();
//6
g_showShareware = 0;
// everyothertime = 0;
for (i=0; i<MAXPLAYERS; i++)
g_player[i].playerquitflag = 1;
}
static void postloadplayer2()
{
//7
G_ResetTimers();
#ifdef POLYMER
if (getrendermode() == 4)
{
polymer_loadboard();
polymer_resetlights();
}
#elif 0
if (getrendermode() == 4)
{
int32_t i = 0;
polymer_loadboard();
while (i < MAXSPRITES)
{
if (actor[i].lightptr)
{
polymer_deletelight(actor[i].lightId);
actor[i].lightptr = NULL;
actor[i].lightId = -1;
}
i++;
}
}
#endif
}
////////// END GENERIC SAVING/LOADING SYSTEM //////////