raze/polymer/eduke32/source/savegame.c
helixhorned ee4d060b1f * New m32script commands:
- sethighlightsector <sectnum> <on?>
 - updatehighlightsector
 - collectsectors <<array_to_collect_sectnums>> <initial_sector> <<num_collected_sectors>> <sector_filtering_state>

The latter does a breadth-first search starting from an initial sector and collects nextsectors only when the filtering state, given a sectnum as RETURN input, writes a nonzero value into RETURN. As a usage example, a.m32 includes the state 'collect_teleporting_sectors', that collects all sectors containing an SE7 and highlights them afterwards. This way, it should be possible to retrofit old maps with TROR by distributing small scripts that do most of the work (right now, joining has to be done by hand, though).


* corruption checker: for the 'nextwall inconsistent with nextsector' corruption, suggest an alternative fix by searching fitting nextwalls and changing the nextwall of the corrupt wall (as opposed to the nextsector). It will display with a leading '?' in the listing, and can be demanded by suffixing 'corruptcheck tryfix' with it. For example,

corruptcheck tryfix 9-21 ?

would fix some corruptions in Billy Boy's cranium.map without introducing drawing errors.


* fix demo playback (tueidj figured this out)


git-svn-id: https://svn.eduke32.com/eduke32@1927 1a8010ca-5511-0410-912e-c29ae57300e0
2011-07-03 22:51:28 +00:00

2338 lines
75 KiB
C

//-------------------------------------------------------------------------
/*
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"
extern char *bitptr;
#define BITPTR_POINTER 1
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++)
{
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)
{
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;
}
else ud.savegame[i][19] = 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;
}*/
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)
{
int32_t k;
char fn[13];
char mpfn[13];
char *fnptr, *scriptptrs;
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;
scriptptrs = Bcalloc(1,g_scriptSize * sizeof(scriptptrs));
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;
for (i=0; i<MAXTILES; i++)
if (actorscrptr[i])
{
j = (intptr_t)actorscrptr[i]+(intptr_t)&script[0];
actorscrptr[i] = (intptr_t *)j;
}
if (kdfread(&actorLoadEventScrptr[0],sizeof(&actorLoadEventScrptr[0]),MAXTILES,fil) != MAXTILES) goto corrupt;
for (i=0; i<MAXTILES; i++)
if (actorLoadEventScrptr[i])
{
j = (intptr_t)actorLoadEventScrptr[i]+(intptr_t)&script[0];
actorLoadEventScrptr[i] = (intptr_t *)j;
}
scriptptrs = Brealloc(scriptptrs, MAXSPRITES * sizeof(scriptptrs));
if (kdfread(&scriptptrs[0],sizeof(scriptptrs),MAXSPRITES,fil) != MAXSPRITES) goto corrupt;
if (kdfread(&actor[0],sizeof(actor_t),MAXSPRITES,fil) != MAXSPRITES) goto corrupt;
for (i=0; i<MAXSPRITES; i++)
{
j = (intptr_t)(&script[0]);
if (scriptptrs[i]&1) T2 += j;
if (scriptptrs[i]&2) T5 += j;
if (scriptptrs[i]&4) T6 += j;
actor[i].projectile = &SpriteProjectile[i];
}
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;
for (i = g_animateCount-1; i>=0; i--) animateptr[i] = (int32_t *)((intptr_t)animateptr[i]+(intptr_t)(&sector[0]));
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_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]);
g_showShareware = 0;
everyothertime = 0;
// clearbufbyte(playerquitflag,MAXPLAYERS,0x01010101);
for (i=0; i<MAXPLAYERS; i++)
clearbufbyte(&g_player[i].playerquitflag,1,0x01010101);
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, *scriptptrs;
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);
scriptptrs = Bcalloc(1, g_scriptSize * sizeof(scriptptrs));
for (i=0; i<g_scriptSize; i++)
{
if (bitptr[i>>3]&(BITPTR_POINTER<<(i&7)))
{
// scriptptrs[i] = 1;
j = (intptr_t)script[i] - (intptr_t)&script[0];
script[i] = j;
}
// else scriptptrs[i] = 0;
}
// dfwrite(&scriptptrs[0],sizeof(scriptptrs),g_scriptSize,fil);
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;
}
for (i=0; i<MAXTILES; i++)
if (actorscrptr[i])
{
j = (intptr_t)actorscrptr[i]-(intptr_t)&script[0];
actorscrptr[i] = (intptr_t *)j;
}
dfwrite(&actorscrptr[0],sizeof(actorscrptr[0]),MAXTILES,fil);
for (i=0; i<MAXTILES; i++)
if (actorscrptr[i])
{
j = (intptr_t)actorscrptr[i]+(intptr_t)&script[0];
actorscrptr[i] = (intptr_t *)j;
}
for (i=0; i<MAXTILES; i++)
if (actorLoadEventScrptr[i])
{
j = (intptr_t)actorLoadEventScrptr[i]-(intptr_t)&script[0];
actorLoadEventScrptr[i] = (intptr_t *)j;
}
dfwrite(&actorLoadEventScrptr[0],sizeof(actorLoadEventScrptr[0]),MAXTILES,fil);
for (i=0; i<MAXTILES; i++)
if (actorLoadEventScrptr[i])
{
j = (intptr_t)actorLoadEventScrptr[i]+(intptr_t)&script[0];
actorLoadEventScrptr[i] = (intptr_t *)j;
}
Bfree(scriptptrs);
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);
dfwrite(&actor[0],sizeof(actor_t),MAXSPRITES,fil);
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;
}
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);
for (i = g_animateCount-1; i>=0; i--) animateptr[i] = (int32_t *)((intptr_t)animateptr[i]-(intptr_t)(&sector[0]));
dfwrite(&animateptr[0],sizeof(animateptr[0]),MAXANIMATES,fil);
for (i = g_animateCount-1; i>=0; i--) animateptr[i] = (int32_t *)((intptr_t)animateptr[i]+(intptr_t)(&sector[0]));
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
#define SV_MINOR_VER 3
#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<=(int32_t)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("rds: 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");
// *(int32_t *)0 = 1;
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<=(int32_t)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 ", spec, sp, mem);
OSD_Printf("rsd: %s: read %d, expected %d!\n",
((sp->flags&DS_CNTMASK)==0 && sp->size *cnt<=(int32_t)savegame_comprthres)?
"UNCOMP":"COMPR", i, j);
if (i==-1)
perror("read");
// *(int32_t *)0 = 1;
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)
{
// *(int32_t *)0 = 1;
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))
static uint8_t savegame_bitmap[MAXSPRITES>>3][3];
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 },
{ 0, &savegame_bitmap, sizeof(savegame_bitmap), 1 },
{ 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==0) || 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]);
for (i=0; i<MAXTILES; i++)
if (actorscrptr[i])
actorscrptr[i] = (intptr_t *)(actorscrptr[i]-&script[0]);
for (i=0; i<MAXTILES; i++)
if (actorLoadEventScrptr[i])
actorLoadEventScrptr[i] = (intptr_t *)(actorLoadEventScrptr[i]-&script[0]);
}
static void sv_prescriptload_once()
{
if (script)
Bfree(script);
script = Bmalloc(g_scriptSize * sizeof(script[0]));
}
static void sv_postscript_once()
{
int32_t i;
for (i=0; i<MAXTILES; i++)
if (actorLoadEventScrptr[i])
actorLoadEventScrptr[i] = (intptr_t)actorLoadEventScrptr[i] + &script[0];
for (i=0; i<MAXTILES; i++)
if (actorscrptr[i])
actorscrptr[i] = (intptr_t)actorscrptr[i] + &script[0];
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;
intptr_t j=(intptr_t)&script[0], k=(intptr_t)&script[g_scriptSize];
Bmemset(savegame_bitmap, 0, sizeof(savegame_bitmap));
for (i=0; i<MAXSPRITES; i++)
{
actor[i].lightptr = NULL;
actor[i].lightId = -1;
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;
}
}
static void sv_postactordata()
{
int32_t i;
intptr_t j=(intptr_t)&script[0];
#if POLYMER
if (getrendermode() == 4)
polymer_resetlights();
#endif
for (i=0; i<MAXSPRITES; i++)
{
actor[i].lightptr = NULL;
actor[i].lightId = -1;
actor[i].projectile = &SpriteProjectile[i];
if (sprite[i].statnum==MAXSTATUS || actorscrptr[PN]==NULL) continue;
if (savegame_bitmap[i>>3][0]&(1<<(i&7))) T2 += j;
if (savegame_bitmap[i>>3][1]&(1<<(i&7))) T5 += j;
if (savegame_bitmap[i>>3][2]&(1<<(i&7))) T6 += j;
}
}
static void sv_preanimateptrsave()
{
int32_t i;
for (i=g_animateCount-1; i>=0; i--)
animateptr[i] = (int32_t *)((intptr_t)animateptr[i]-(intptr_t)&sector[0]);
}
static void sv_postanimateptr()
{
int32_t i;
for (i=g_animateCount-1; i>=0; i--)
animateptr[i] = (int32_t *)((intptr_t)animateptr[i]+(intptr_t)&sector[0]);
}
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();
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_numInterpolations = 0;
startofdynamicinterpolations = 0;
i = headspritestat[STAT_EFFECTOR];
while (i >= 0)
{
switch (sprite[i].lotag)
{
case 31:
G_SetInterpolation(&sector[sprite[i].sectnum].floorz);
break;
case 32:
G_SetInterpolation(&sector[sprite[i].sectnum].ceilingz);
break;
case 25:
G_SetInterpolation(&sector[sprite[i].sectnum].floorz);
G_SetInterpolation(&sector[sprite[i].sectnum].ceilingz);
break;
case 17:
G_SetInterpolation(&sector[sprite[i].sectnum].floorz);
G_SetInterpolation(&sector[sprite[i].sectnum].ceilingz);
break;
case 0:
case 5:
case 6:
case 11:
case 14:
case 15:
case 16:
case 26:
case 30:
Sect_SetInterpolation(sprite[i].sectnum);
break;
}
i = nextspritestat[i];
}
for (i=g_numInterpolations-1; i>=0; i--) bakipos[i] = *curipos[i];
for (i = g_animateCount-1; i>=0; i--)
G_SetInterpolation(animateptr[i]);
//6
g_showShareware = 0;
// everyothertime = 0;
// clearbufbyte(playerquitflag,MAXPLAYERS,0x01010101);
for (i=0; i<MAXPLAYERS; i++)
clearbufbyte(&g_player[i].playerquitflag,1,0x01010101);
}
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 //////////