From eb5f59ca7f4f2d55897bb4ef9dc36eeef177cd7a Mon Sep 17 00:00:00 2001 From: helixhorned Date: Sun, 24 Jan 2010 23:33:17 +0000 Subject: [PATCH] Improved demo system. Kick ass and watch :)\ Features: * start recording in mid-game (ScrollLock) * saves interleaved diffs for later sync correction * supports fast-forward/rewind Some work still needed to trace down remaining sync problems. Also be sure to check the demo* cvars. git-svn-id: https://svn.eduke32.com/eduke32@1595 1a8010ca-5511-0410-912e-c29ae57300e0 --- polymer/eduke32/Makefile | 28 +- polymer/eduke32/build/Makefile | 8 + polymer/eduke32/build/include/polymer.h | 3 +- polymer/eduke32/build/src/engine.c | 66 ++ polymer/eduke32/build/src/osd.c | 4 - polymer/eduke32/source/actors.c | 33 +- polymer/eduke32/source/astub.c | 2 - polymer/eduke32/source/config.c | 21 + polymer/eduke32/source/funct.h | 4 +- polymer/eduke32/source/game.c | 686 ++++++++---- polymer/eduke32/source/gameexec.c | 39 +- polymer/eduke32/source/gamevars.c | 43 +- polymer/eduke32/source/osdcmds.c | 10 + polymer/eduke32/source/premap.c | 58 +- polymer/eduke32/source/savegame.c | 1358 ++++++++++++++++++++++- 15 files changed, 2051 insertions(+), 312 deletions(-) diff --git a/polymer/eduke32/Makefile b/polymer/eduke32/Makefile index d583406ae..800073ab8 100644 --- a/polymer/eduke32/Makefile +++ b/polymer/eduke32/Makefile @@ -22,10 +22,15 @@ BUILD32_ON_64 = 0 # Debugging/Build options RELEASE?=1 DEBUGANYWAY?=0 +KRANDDEBUG?=0 NOSOUND?=0 OPTLEVEL?=2 PROFILER?=0 +ifneq (0,$(KRANDDEBUG)) + RELEASE=0 +endif + # Build locations SRC=source RSRC=rsrc @@ -42,6 +47,10 @@ ifneq (0,$(RELEASE)) else # Debugging enabled debug=-ggdb -O0 -DDEBUGGINGAIDS + ifneq (0,$(KRANDDEBUG)) + debug+=-fno-inline -fno-inline-functions -fno-inline-functions-called-once + debug+=-DKRANDDEBUG=1 + endif endif ifneq (0,$(DEBUGANYWAY)) @@ -199,6 +208,11 @@ EDITOROBJS+= $(OBJ)/sounds_mapster32.$o OURCFLAGS+= $(BUILDCFLAGS) OURCXXFLAGS+= $(BUILDCFLAGS) +MISCLINKOPTS= +ifneq (0,$(KRANDDEBUG)) + MISCLINKOPTS=-Wl,-Map=$@.memmap +endif + ifeq ($(PRETTY_OUTPUT),1) .SILENT: endif @@ -215,16 +229,16 @@ all: notice: $(BUILD_STARTED) - + eduke32$(EXESUFFIX): $(GAMEOBJS) $(EOBJ)/$(ENGINELIB) $(JAUDIOLIBDIR)/$(JAUDIOLIB) $(ENETDIR)/$(ENETLIB) $(LINK_STATUS) - if $(CC) -o $@ $^ $(LIBS) $(STDCPPLIB); then $(LINK_OK); else $(LINK_FAILED); fi + if $(CC) -o $@ $^ $(LIBS) $(STDCPPLIB) $(MISCLINKOPTS); then $(LINK_OK); else $(LINK_FAILED); fi ifeq (1,$(RELEASE)) ifeq (0,$(DEBUGANYWAY)) $(STRIP) eduke32$(EXESUFFIX) endif endif - + mapster32$(EXESUFFIX): $(EDITOROBJS) $(EOBJ)/$(EDITORLIB) $(EOBJ)/$(ENGINELIB) $(JAUDIOLIBDIR)/$(JAUDIOLIB) $(ENETDIR)/$(ENETLIB) $(LINK_STATUS) if $(CC) $(CFLAGS) $(OURCFLAGS) -o $@ $^ $(LIBS) $(STDCPPLIB); then $(LINK_OK); else $(LINK_FAILED); fi @@ -252,13 +266,13 @@ ifeq ($(PRETTY_OUTPUT),1) printf "\033[K\033[0;35mChanging dir to \033[1;35m$(CURDIR)/$(EROOT)\033[0;35m \033[0m\n" endif $(MAKE) -C $(EROOT)/ "OBJ=../$(EOBJ)" \ - SUPERBUILD=$(SUPERBUILD) POLYMOST=$(POLYMOST) DEBUGANYWAY=$(DEBUGANYWAY)\ + SUPERBUILD=$(SUPERBUILD) POLYMOST=$(POLYMOST) DEBUGANYWAY=$(DEBUGANYWAY) KRANDDEBUG=$(KRANDDEBUG)\ USE_OPENGL=$(USE_OPENGL) BUILD32_ON_64=$(BUILD32_ON_64) \ NOASM=$(NOASM) RELEASE=$(RELEASE) OPTLEVEL=$(OPTLEVEL) $@ ifeq ($(PRETTY_OUTPUT),1) printf "\033[K\033[0;35mChanging dir to \033[1;35m$(CURDIR)\033[0;35m \033[0m\n" endif - + $(EOBJ)/$(ENGINELIB): enginelib $(EOBJ)/$(EDITORLIB): editorlib $(JAUDIOLIBDIR)/$(JAUDIOLIB): @@ -304,11 +318,11 @@ $(OBJ)/%.$o: $(SRC)/misc/%.rc $(OBJ)/%.$o: $(SRC)/util/%.c $(COMPILE_STATUS) if $(CC) $(CFLAGS) $(OURCFLAGS) -c $< -o $@; then $(COMPILE_OK); else $(COMPILE_FAILED); fi - + $(OBJ)/%.$o: $(RSRC)/%.c $(COMPILE_STATUS) if $(CC) $(CFLAGS) $(OURCFLAGS) -c $< -o $@; then $(COMPILE_OK); else $(COMPILE_FAILED); fi - + $(OBJ)/game_banner.$o: $(RSRC)/game_banner.c $(OBJ)/editor_banner.$o: $(RSRC)/editor_banner.c $(RSRC)/game_banner.c: $(RSRC)/game.bmp diff --git a/polymer/eduke32/build/Makefile b/polymer/eduke32/build/Makefile index 4ceb4e4df..0ea3dee0a 100644 --- a/polymer/eduke32/build/Makefile +++ b/polymer/eduke32/build/Makefile @@ -38,9 +38,14 @@ endif # RELEASE?=1 DEBUGANYWAY?=0 +KRANDDEBUG?=0 EFENCE?=0 OPTLEVEL ?= 2 +ifneq (0,$(KRANDDEBUG)) + RELEASE=0 +endif + # SDK locations for Windows - adjust to match your setup # DXROOT=c:/sdks/directx/dx61 @@ -69,6 +74,9 @@ ifneq ($(RELEASE),0) else # Debugging enabled debug=-ggdb -O0 -DDEBUGGINGAIDS -DNOSDLPARACHUTE + ifneq (0,$(KRANDDEBUG)) + debug+=-DKRANDDEBUG=1 + endif endif ifneq (0,$(DEBUGANYWAY)) diff --git a/polymer/eduke32/build/include/polymer.h b/polymer/eduke32/build/include/polymer.h index e6dfceafe..0c5252768 100644 --- a/polymer/eduke32/build/include/polymer.h +++ b/polymer/eduke32/build/include/polymer.h @@ -294,6 +294,7 @@ typedef struct s_pranimatespritesinfo { int32_t polymer_init(void); void polymer_uninit(void); void polymer_glinit(void); +void polymer_resetlights(void); void polymer_loadboard(void); void polymer_drawrooms(int32_t daposx, int32_t daposy, int32_t daposz, int16_t daang, int32_t dahoriz, int16_t dacursectnum); void polymer_drawmasks(void); @@ -355,7 +356,7 @@ static void polymer_compileprogram(int32_t programbits); // LIGHTS static void polymer_removelight(int16_t lighti); static void polymer_updatelights(void); -static void polymer_resetlights(void); +//static void polymer_resetlights(void); static inline void polymer_resetplanelights(_prplane* plane); static void polymer_addplanelight(_prplane* plane, int16_t lighti); static inline void polymer_deleteplanelight(_prplane* plane, int16_t lighti); diff --git a/polymer/eduke32/build/src/engine.c b/polymer/eduke32/build/src/engine.c index 55b61e7b5..5a9810273 100644 --- a/polymer/eduke32/build/src/engine.c +++ b/polymer/eduke32/build/src/engine.c @@ -9500,6 +9500,58 @@ void getmousevalues(int32_t *mousx, int32_t *mousy, int32_t *bstatus) } +#if KRANDDEBUG +# include +# define KRD_MAXCALLS 262144 +# define KRD_DEPTH 8 +static int krd_numcalls=0; +static int krd_randomseed[KRD_MAXCALLS]; +static int krd_totalclock[KRD_MAXCALLS]; +static void *krd_fromwhere[KRD_MAXCALLS][KRD_DEPTH]; +static int32_t krd_enabled=0; + +void krd_enable() +{ + krd_enabled = 1; +} + +int32_t krd_print(const char *filename) +{ + FILE *krd_fp = fopen(filename, "w"); + int i, j, k, ototalclk=0; + + krd_enabled = 0; + if (!krd_fp) { printf("KRANDDEBUG: Couldn't open file!"); return 1; } + + for (i=0; i=KRD_DEPTH || krd_fromwhere[i][j]==NULL) + { + fprintf(krd_fp, "\n"); + break; + } + fprintf(krd_fp, " [%p]", krd_fromwhere[i][j]); + } + } + + krd_numcalls = 0; + + fclose(krd_fp); + return 0; +} +#endif + // // krand // @@ -9507,6 +9559,20 @@ int32_t krand(void) { // randomseed = (randomseed*27584621)+1; randomseed = (randomseed * 1664525ul) + 221297ul; + +#if KRANDDEBUG + if (krd_enabled) + if (krd_numcalls < KRD_MAXCALLS) + { + int32_t i; + krd_randomseed[krd_numcalls] = randomseed; + krd_totalclock[krd_numcalls] = totalclock; + for (i=backtrace(krd_fromwhere[krd_numcalls],KRD_DEPTH); i>16); } diff --git a/polymer/eduke32/build/src/osd.c b/polymer/eduke32/build/src/osd.c index 30e25d96c..6c0deaa9c 100644 --- a/polymer/eduke32/build/src/osd.c +++ b/polymer/eduke32/build/src/osd.c @@ -1379,8 +1379,6 @@ void OSD_Draw(void) // and write it to the log file // -extern int32_t premap_quickenterlevel; - static inline void linefeed(void) { Bmemmove(osdtext+osdcols, osdtext, TEXTSIZE-osdcols); @@ -1398,8 +1396,6 @@ void OSD_Printf(const char *fmt, ...) if (!osdinited) OSD_Init(); - if (premap_quickenterlevel) return; - va_start(va, fmt); Bvsnprintf(tmpstr, 8192, fmt, va); va_end(va); diff --git a/polymer/eduke32/source/actors.c b/polymer/eduke32/source/actors.c index 52ef00cd7..ef940bb55 100644 --- a/polymer/eduke32/source/actors.c +++ b/polymer/eduke32/source/actors.c @@ -25,6 +25,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "gamedef.h" #include "compat.h" +#if KRANDDEBUG +# define ACTOR_INLINE +# define ACTOR_STATIC +#else +# define ACTOR_INLINE inline +# define ACTOR_STATIC static +#endif + + #define KILLIT(KX) {deletesprite(KX);goto BOLT;} extern int32_t g_numEnvSoundsPlaying; @@ -456,7 +465,7 @@ int32_t A_MoveSprite(int32_t spritenum, const vec3_t *change, uint32_t cliptype) return(retval); } -inline int32_t A_SetSprite(int32_t i,uint32_t cliptype) +ACTOR_INLINE int32_t A_SetSprite(int32_t i,uint32_t cliptype) { vec3_t davect = {(sprite[i].xvel*(sintable[(sprite[i].ang+512)&2047]))>>14, (sprite[i].xvel*(sintable[sprite[i].ang&2047]))>>14, @@ -724,7 +733,7 @@ inline void G_AddGameLight(int32_t radius, int32_t srcsprite, int32_t zoffset, i } // sleeping monsters, etc -static void G_MoveZombieActors(void) +ACTOR_STATIC void G_MoveZombieActors(void) { int32_t x, px, py, sx, sy; int32_t i = headspritestat[STAT_ZOMBIEACTOR], j, p, nexti; @@ -1020,7 +1029,7 @@ BOLT: int32_t otherp; -static void G_MovePlayers(void) +ACTOR_STATIC void G_MovePlayers(void) { int32_t i = headspritestat[STAT_PLAYER], nexti; int32_t otherx; @@ -1153,7 +1162,7 @@ BOLT: } } -static void G_MoveFX(void) +ACTOR_STATIC void G_MoveFX(void) { int32_t i = headspritestat[STAT_FX], j, nexti, p; int32_t x, ht; @@ -1253,7 +1262,7 @@ BOLT: } } -static void G_MoveFallers(void) +ACTOR_STATIC void G_MoveFallers(void) { int32_t i = headspritestat[STAT_FALLER], nexti, sect, j; spritetype *s; @@ -1351,7 +1360,7 @@ BOLT: } } -static void G_MoveStandables(void) +ACTOR_STATIC void G_MoveStandables(void) { int32_t i = headspritestat[STAT_STANDABLE], j, k, nexti, nextj, p=0, sect, switchpicnum; int32_t l=0, x; @@ -2312,7 +2321,7 @@ BOLT: } } -static void A_DoProjectileBounce(int32_t i) +ACTOR_STATIC void A_DoProjectileBounce(int32_t i) { int32_t dax, day, daz = 4096; spritetype *s = &sprite[i]; @@ -2349,7 +2358,7 @@ static void A_DoProjectileBounce(int32_t i) s->ang = getangle(xvect,yvect); } -static void G_MoveWeapons(void) +ACTOR_STATIC void G_MoveWeapons(void) { int32_t i = headspritestat[STAT_PROJECTILE], j=0, k, f, nexti, p, q; vec3_t davect; @@ -3092,7 +3101,7 @@ BOLT: } } -static void G_MoveTransports(void) +ACTOR_STATIC void G_MoveTransports(void) { int32_t warpspriteto; int32_t i = headspritestat[STAT_TRANSPORT], j, k, l, p, sect, sectlotag, nexti, nextj; @@ -3447,7 +3456,7 @@ static int16_t A_FindLocator(int32_t n,int32_t sn) return -1; } -static void G_MoveActors(void) +ACTOR_STATIC void G_MoveActors(void) { int32_t x, m, l; intptr_t *t; @@ -4796,7 +4805,7 @@ BOLT: } -static void G_MoveMisc(void) // STATNUM 5 +ACTOR_STATIC void G_MoveMisc(void) // STATNUM 5 { int16_t i, j, nexti, sect, p; int32_t l, x; @@ -5359,7 +5368,7 @@ BOLT: } } -static void G_MoveEffectors(void) //STATNUM 3 +ACTOR_STATIC void G_MoveEffectors(void) //STATNUM 3 { int32_t q=0, m, x, st, j; intptr_t *t,l; diff --git a/polymer/eduke32/source/astub.c b/polymer/eduke32/source/astub.c index a3e4e5b47..ea13edd03 100644 --- a/polymer/eduke32/source/astub.c +++ b/polymer/eduke32/source/astub.c @@ -78,8 +78,6 @@ static int16_t g_definedsndnum[MAXSOUNDS]; // maps parse order index to g_sound static int16_t g_sndnum[MAXSOUNDS]; // maps current order index to g_sounds index int32_t g_numsounds = 0; -int32_t premap_quickenterlevel=0; - #if !defined(_WIN32) static int32_t usecwd = 0; #endif diff --git a/polymer/eduke32/source/config.c b/polymer/eduke32/source/config.c index 5f9e909c8..c6168c331 100644 --- a/polymer/eduke32/source/config.c +++ b/polymer/eduke32/source/config.c @@ -606,6 +606,8 @@ extern palette_t DefaultCrosshairColors; extern char g_modDir[BMAX_PATH]; extern int32_t r_maxfps; extern int32_t g_noSetup; +extern int32_t demorec_diffs_cvar, demoplay_diffs; +extern int32_t demorec_difftics_cvar, demorec_diffcompress_cvar, demorec_synccompress_cvar; int32_t CONFIG_ReadSetup(void) { @@ -879,6 +881,19 @@ int32_t CONFIG_ReadSetup(void) SCRIPT_GetNumber(ud.config.scripthandle, "Misc", "WeaponScale",&ud.weaponscale); SCRIPT_GetNumber(ud.config.scripthandle, "Misc", "WeaponSway",&ud.weaponsway); + { + SCRIPT_GetNumber(ud.config.scripthandle, "Misc", "DemoRecDiffs",&demorec_diffs_cvar); + SCRIPT_GetNumber(ud.config.scripthandle, "Misc", "DemoRecDiffTics",&demorec_difftics_cvar); + SCRIPT_GetNumber(ud.config.scripthandle, "Misc", "DemoRecDiffCompress",&demorec_diffcompress_cvar); + SCRIPT_GetNumber(ud.config.scripthandle, "Misc", "DemoRecSyncCompress",&demorec_synccompress_cvar); + SCRIPT_GetNumber(ud.config.scripthandle, "Misc", "DemoPlayDiffs",&demoplay_diffs); + demorec_diffs_cvar = !!demorec_diffs_cvar; + demorec_difftics_cvar = min(max(2, demorec_difftics_cvar), 60*(TICRATE/TICSPERFRAME)); + demorec_diffcompress_cvar = min(max(0, demorec_diffcompress_cvar), 1); + demorec_synccompress_cvar = min(max(0, demorec_synccompress_cvar), 1); + demoplay_diffs = !!demoplay_diffs; + } + // weapon choices are defaulted in G_CheckCommandLine, which may override them if (!g_forceWeaponChoice) for (i=0; i<10; i++) @@ -1071,6 +1086,12 @@ void CONFIG_WriteSetup(void) SCRIPT_PutNumber(ud.config.scripthandle, "Misc", "WeaponScale",ud.weaponscale,FALSE,FALSE); SCRIPT_PutNumber(ud.config.scripthandle, "Misc", "WeaponSway",ud.weaponsway,FALSE,FALSE); + SCRIPT_PutNumber(ud.config.scripthandle, "Misc", "DemoRecDiffs",demorec_diffs_cvar,FALSE,FALSE); + SCRIPT_PutNumber(ud.config.scripthandle, "Misc", "DemoRecDiffTics",demorec_difftics_cvar,FALSE,FALSE); + SCRIPT_PutNumber(ud.config.scripthandle, "Misc", "DemoRecDiffCompress",demorec_diffcompress_cvar,FALSE,FALSE); + SCRIPT_PutNumber(ud.config.scripthandle, "Misc", "DemoRecSyncCompress",demorec_synccompress_cvar,FALSE,FALSE); + SCRIPT_PutNumber(ud.config.scripthandle, "Misc", "DemoPlayDiffs",demoplay_diffs,FALSE,FALSE); + SCRIPT_PutNumber(ud.config.scripthandle, "Setup","ConfigVersion",BYTEVERSION_JF,FALSE,FALSE); SCRIPT_PutNumber(ud.config.scripthandle, "Setup", "ForceSetup",ud.config.ForceSetup,FALSE,FALSE); SCRIPT_PutNumber(ud.config.scripthandle, "Setup", "NoAutoLoad",ud.config.NoAutoLoad,FALSE,FALSE); diff --git a/polymer/eduke32/source/funct.h b/polymer/eduke32/source/funct.h index 1918db87d..65673cb24 100644 --- a/polymer/eduke32/source/funct.h +++ b/polymer/eduke32/source/funct.h @@ -222,8 +222,8 @@ extern void Gv_DumpValues(void); extern void Gv_ResetSystemDefaults(void); extern void Gv_InitWeaponPointers(void); extern void Gv_Init(void); -extern void Gv_WriteSave(FILE *fil); -extern int32_t Gv_ReadSave(int32_t fil); +extern void Gv_WriteSave(FILE *fil, int32_t newbehav); +extern int32_t Gv_ReadSave(int32_t fil, int32_t newbehav); extern int32_t __fastcall Gv_GetVar(register int32_t id, register int32_t iActor, register int32_t iPlayer); extern void __fastcall Gv_SetVar(register int32_t id, register int32_t lValue, register int32_t iActor, register int32_t iPlayer); diff --git a/polymer/eduke32/source/game.c b/polymer/eduke32/source/game.c index 03a93624a..8580564f8 100644 --- a/polymer/eduke32/source/game.c +++ b/polymer/eduke32/source/game.c @@ -49,6 +49,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "enet/enet.h" #include "quicklz.h" +#if KRANDDEBUG +# define GAME_INLINE +# define GAME_STATIC +#else +# define GAME_INLINE inline +# define GAME_STATIC static +#endif + #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include @@ -106,9 +114,17 @@ static int32_t g_Debug = 0; extern int32_t numlumps; static FILE *g_demo_filePtr = (FILE *)NULL; -static int32_t g_demo_goalCnt=0, g_demo_startCnt=0, g_demo_soundToggle, g_demo_showStats=1; -static int32_t g_demo_paused=0; -static int32_t g_demo_recFilePtr, g_demo_totalCnt; +static int32_t g_demo_cnt, g_demo_goalCnt=0, g_demo_totalCnt, g_demo_soundToggle; +static int32_t g_demo_paused=0, g_demo_rewind=0, g_demo_showStats=1; +static int32_t g_demo_recFilePtr; + +static int32_t demo_hasdiffs, demorec_diffs=1, demorec_difftics = 2*(TICRATE/TICSPERFRAME); +int32_t demoplay_diffs=1, demorec_diffs_cvar=1, demorec_force_cvar=0; +int32_t demorec_difftics_cvar = 2*(TICRATE/TICSPERFRAME); +int32_t demorec_diffcompress_cvar=1; +int32_t demorec_synccompress_cvar=1; +int32_t demorec_seeds_cvar=1, demoplay_showsync=1; +static int32_t demo_synccompress=1, demorec_seeds=1, demo_hasseeds; int32_t g_restorePalette = 0, g_screenCapture = 0, g_noEnemies = 0; static int32_t g_noLogoAnim = 0; @@ -144,10 +160,10 @@ static int32_t nonsharedtimer; int32_t ticrandomseed; static void G_DrawCameraText(int16_t i); -static inline int32_t G_MoveLoop(void); +GAME_STATIC GAME_INLINE int32_t G_MoveLoop(void); static void G_DoOrderScreen(void); -static int32_t G_DoMoveThings(void); -static int32_t G_PlaybackDemo(void); +GAME_STATIC int32_t G_DoMoveThings(void); +GAME_STATIC int32_t G_PlaybackDemo(void); static char recbuf[180]; @@ -8450,7 +8466,7 @@ void G_CheatGetInv(void) int8_t cheatbuf[MAXCHEATLEN],cheatbuflen; -static void G_DoCheats(void) +GAME_STATIC void G_DoCheats(void) { int32_t ch, i, j, k=0, weapon; static int32_t z=0; @@ -8957,7 +8973,19 @@ static void G_ShowScores(void) #undef SCORESHEETOFFSET -static void G_HandleLocalKeys(void) + +static void demo_preparewarp() +{ + if (!g_demo_paused) + { + g_demo_soundToggle = ud.config.SoundToggle; + ud.config.SoundToggle = 0; + } + FX_StopAllSounds(); + S_ClearSoundLocks(); +} + +GAME_STATIC void G_HandleLocalKeys(void) { int32_t i,ch; int32_t j; @@ -9078,12 +9106,35 @@ static void G_HandleLocalKeys(void) P_DoQuote(83+ud.scrollmode,g_player[myconnectindex].ps); } + if (KB_UnBoundKeyPressed(sc_ScrollLock)) + { + KB_ClearKeyDown(sc_ScrollLock); + + switch (ud.recstat) + { + case 0: + G_OpenDemoWrite(); + break; + case 1: + G_CloseDemoWrite(); + break; + } + } + if (ud.recstat == 2) { if (KB_KeyPressed(sc_Space)) { KB_ClearKeyDown(sc_Space); + g_demo_paused = !g_demo_paused; + g_demo_rewind = 0; + + if (g_demo_paused) + { + FX_StopAllSounds(); + S_ClearSoundLocks(); + } } if (KB_KeyPressed(sc_Tab)) @@ -9092,6 +9143,7 @@ static void G_HandleLocalKeys(void) g_demo_showStats = !g_demo_showStats; } +#if 0 if (KB_KeyPressed(sc_kpad_Plus)) { if (g_timerTicsPerSecond != 240) @@ -9116,44 +9168,31 @@ static void G_HandleLocalKeys(void) inittimer(120); g_timerTicsPerSecond = 120; } +#endif if (KB_KeyPressed(sc_kpad_6)) { KB_ClearKeyDown(sc_kpad_6); - j = ALT_IS_PRESSED ? 30 : 10; - g_demo_goalCnt = g_demo_paused ? ud.reccnt-ud.multimode : ud.reccnt-(TICRATE/TICSPERFRAME)*ud.multimode*j; - g_demo_soundToggle = ud.config.SoundToggle; + j = (15< g_demo_totalCnt) g_demo_goalCnt = 0; else - { - ud.config.SoundToggle = 0; - FX_StopAllSounds(); - S_ClearSoundLocks(); - } - + demo_preparewarp(); } else if (KB_KeyPressed(sc_kpad_4)) { KB_ClearKeyDown(sc_kpad_4); - j = ALT_IS_PRESSED ? 30 : 10; - g_demo_goalCnt = g_demo_paused ? ud.reccnt+ud.multimode : ud.reccnt+(TICRATE/TICSPERFRAME)*ud.multimode*j; - g_demo_soundToggle = ud.config.SoundToggle; + j = (15< g_demo_startCnt) - g_demo_goalCnt = g_demo_startCnt; - - g_demo_goalCnt = -g_demo_goalCnt; - ud.config.SoundToggle = 0; - FX_StopAllSounds(); - S_ClearSoundLocks(); + demo_preparewarp(); } #if 0 @@ -9163,11 +9202,11 @@ static void G_HandleLocalKeys(void) KB_ClearKeyDown(sc_Return); ud.reccnt = 0; ud.recstat = 0; -// kclose(recfilep); + kclose(g_demo_recFilePtr); g_player[myconnectindex].ps->gm = MODE_GAME; - ready2send=0; +// ready2send=0; screenpeek=myconnectindex; - demo_paused=0; +// g_demo_paused=0; } #endif } @@ -11947,22 +11986,19 @@ MAIN_LOOP_RESTART: G_GameExit(" "); } -static int32_t demo_version; +extern int32_t sv_loadsnapshot(int32_t fil, int32_t *ret_hasdiffs, int32_t *ret_demoticcnt, int32_t *ret_synccompress); -static int32_t G_OpenDemoRead(int32_t g_whichDemo) // 0 = mine +GAME_STATIC int32_t G_OpenDemoRead(int32_t g_whichDemo) // 0 = mine { - char d[13]; - char ver; + char d[14]; int32_t i; - Bstrcpy(d, "demo_.dmo"); + Bstrcpy(d, "edemo_.edm"); if (g_whichDemo == 10) - d[4] = 'x'; + d[5] = 'x'; else - d[4] = '0' + g_whichDemo; - - ud.reccnt = 0; + d[5] = '0' + g_whichDemo; if (g_whichDemo == 1 && firstdemofile[0] != 0) { @@ -11970,192 +12006,257 @@ static int32_t G_OpenDemoRead(int32_t g_whichDemo) // 0 = mine } else if ((g_demo_recFilePtr = kopen4loadfrommod(d,g_loadFromGroupOnly)) == -1) return(0); - if (kread(g_demo_recFilePtr,&ud.reccnt,sizeof(int32_t)) != sizeof(int32_t)) goto corrupt; - if (kread(g_demo_recFilePtr,&ver,sizeof(uint8_t)) != sizeof(uint8_t)) goto corrupt; - - if (ver != BYTEVERSION /*&& ver != 116 && ver != 117*/) + i=sv_loadsnapshot(g_demo_recFilePtr, &demo_hasdiffs, &g_demo_totalCnt, &demo_synccompress); + if (i==0) { - /* old demo playback */ - if (ver == BYTEVERSION_JF) initprintf("Demo %s is for Regular edition.\n", d); - else if (ver == BYTEVERSION_JF+1) initprintf("Demo %s is for Atomic edition.\n", d); - else if (ver == BYTEVERSION_JF+2) initprintf("Demo %s is for Shareware version.\n", d); -// else OSD_Printf("Demo %s is of an incompatible version (%d).\n", d, ver); - kclose(g_demo_recFilePtr); - ud.reccnt=0; - demo_version = 0; - return 0; + demo_hasseeds = demo_synccompress&2; + demo_synccompress &= 1; + + i = g_demo_totalCnt/(TICRATE/TICSPERFRAME); + OSD_Printf("demo duration: %d min %d sec\n", i/60, i%60); + + g_demo_cnt=1; + ud.reccnt = 0; + + ud.god = ud.cashman = ud.eog = ud.showallmap = 0; + ud.clipping = ud.scrollmode = ud.overhead_on = ud.pause_on = 0; + +// G_NewGame(ud.volume_number,ud.level_number,ud.player_skill); +// G_ResetTimers(); + totalclock = ototalclock = lockclock = 0; + + return 1; } else { - demo_version = ver; - OSD_Printf("Demo %s is of version %d.\n", d, ver); + OSD_Printf(OSD_ERROR "There were errors opening demo %d (code: %d).\n", g_whichDemo, i); + kclose(g_demo_recFilePtr); + return 0; } - - if (kread(g_demo_recFilePtr,(char *)&ud.volume_number,sizeof(uint8_t)) != sizeof(uint8_t)) goto corrupt; - OSD_Printf("ud.volume_number: %d\n",ud.volume_number); - if (kread(g_demo_recFilePtr,(char *)&ud.level_number,sizeof(uint8_t)) != sizeof(uint8_t)) goto corrupt; - OSD_Printf("ud.level_number: %d\n",ud.level_number); - if (kread(g_demo_recFilePtr,(char *)&ud.player_skill,sizeof(uint8_t)) != sizeof(uint8_t)) goto corrupt; - OSD_Printf("ud.player_skill: %d\n",ud.player_skill); - if (kread(g_demo_recFilePtr,(char *)&ud.m_coop,sizeof(uint8_t)) != sizeof(uint8_t)) goto corrupt; - OSD_Printf("ud.m_coop: %d\n",ud.m_coop); - if (kread(g_demo_recFilePtr,(char *)&ud.m_ffire,sizeof(uint8_t)) != sizeof(uint8_t)) goto corrupt; - OSD_Printf("ud.m_ffire: %d\n",ud.m_ffire); - if (kread(g_demo_recFilePtr,(int16_t *)&ud.multimode,sizeof(int16_t)) != sizeof(int16_t)) goto corrupt; - OSD_Printf("ud.multimode: %d\n",ud.multimode); - if (kread(g_demo_recFilePtr,(int16_t *)&ud.m_monsters_off,sizeof(int16_t)) != sizeof(int16_t)) goto corrupt; - OSD_Printf("ud.m_monsters_off: %d\n",ud.m_monsters_off); - if (kread(g_demo_recFilePtr,(int32_t *)&ud.m_respawn_monsters,sizeof(int32_t)) != sizeof(int32_t)) goto corrupt; - OSD_Printf("ud.m_respawn_monsters: %d\n",ud.m_respawn_monsters); - if (kread(g_demo_recFilePtr,(int32_t *)&ud.m_respawn_items,sizeof(int32_t)) != sizeof(int32_t)) goto corrupt; - OSD_Printf("ud.m_respawn_items: %d\n",ud.m_respawn_items); - if (kread(g_demo_recFilePtr,(int32_t *)&ud.m_respawn_inventory,sizeof(int32_t)) != sizeof(int32_t)) goto corrupt; - OSD_Printf("ud.m_respawn_inventory: %d\n",ud.m_respawn_inventory); - if (kread(g_demo_recFilePtr,(int32_t *)&ud.playerai,sizeof(int32_t)) != sizeof(int32_t)) goto corrupt; - OSD_Printf("ud.playerai: %d\n",ud.playerai); - if (kread(g_demo_recFilePtr,(int32_t *)&i,sizeof(int32_t)) != sizeof(int32_t)) goto corrupt; - - if (kread(g_demo_recFilePtr,(char *)boardfilename,sizeof(boardfilename)) != sizeof(boardfilename)) goto corrupt; - - if (boardfilename[0] != 0) - { - ud.m_level_number = 7; - ud.m_volume_number = 0; - } - - if (kread(g_demo_recFilePtr,(int32_t *)&ud.m_noexits,sizeof(int32_t)) != sizeof(int32_t)) goto corrupt; - - for (i=0; iaim_mode,sizeof(int32_t)) != sizeof(int32_t)) goto corrupt; - if (kread(g_demo_recFilePtr,(int32_t *)&g_player[i].ps->auto_aim,sizeof(int32_t)) != sizeof(int32_t)) goto corrupt; // JBF 20031126 - if (kread(g_demo_recFilePtr,(int32_t *)&g_player[i].ps->weaponswitch,sizeof(int32_t)) != sizeof(int32_t)) goto corrupt; - if (kread(g_demo_recFilePtr,(int32_t *)&g_player[i].pcolor,sizeof(int32_t)) != sizeof(int32_t)) goto corrupt; - g_player[i].ps->palookup = g_player[i].pcolor; - if (kread(g_demo_recFilePtr,(int32_t *)&g_player[i].pteam,sizeof(int32_t)) != sizeof(int32_t)) goto corrupt; - g_player[i].ps->team = g_player[i].pteam; - } - i = ud.reccnt/((TICRATE/TICSPERFRAME)*ud.multimode); - OSD_Printf("demo duration: %d min %d sec\n", i/60, i%60); - g_demo_startCnt = ud.reccnt; - - ud.god = ud.cashman = ud.eog = ud.showallmap = 0; - ud.clipping = ud.scrollmode = ud.overhead_on = ud.pause_on = 0; - - G_NewGame(ud.volume_number,ud.level_number,ud.player_skill); - return(1); +#if 0 corrupt: OSD_Printf(OSD_ERROR "Demo %d header is corrupt.\n",g_whichDemo); ud.reccnt = 0; kclose(g_demo_recFilePtr); return 0; +#endif } +#if KRANDDEBUG +extern int32_t krd_enable(); +extern int32_t krd_print(const char *filename); +#endif + void G_OpenDemoWrite(void) { - char d[13]; - int32_t dummylong = 0, demonum=1; - char ver; - int16_t i; + char d[14]; + int32_t i, demonum=1; + extern int32_t sv_saveandmakesnapshot(FILE* fil, int32_t recdiffs, int32_t diffcompress, int32_t synccompress); if (ud.recstat == 2) kclose(g_demo_recFilePtr); - ver = BYTEVERSION; + if ((g_player[myconnectindex].ps->gm&MODE_GAME) && g_player[myconnectindex].ps->dead_flag) + { + Bstrcpy(ScriptQuotes[122], "CANNOT START DEMO RECORDING WHEN DEAD!"); + P_DoQuote(122, g_player[myconnectindex].ps); + ud.recstat = 0; + return; + } + + if (demorec_diffs_cvar && !demorec_force_cvar) + for (i=1; i>12) && i=0 && script[i+1]aim_mode,sizeof(int32_t),1,g_demo_filePtr); - fwrite((int32_t *)&g_player[i].ps->auto_aim,sizeof(int32_t),1,g_demo_filePtr); // JBF 20031126 - fwrite(&g_player[i].ps->weaponswitch,sizeof(int32_t),1,g_demo_filePtr); - fwrite(&g_player[i].pcolor,sizeof(int32_t),1,g_demo_filePtr); - fwrite(&g_player[i].pteam,sizeof(int32_t),1,g_demo_filePtr); + Bstrcpy(ScriptQuotes[122], "FAILED STARTING DEMO RECORDING. SEE OSD FOR DETAILS."); + P_DoQuote(122, g_player[myconnectindex].ps); + Bfclose(g_demo_filePtr), g_demo_filePtr=NULL; + ud.recstat = 0; + return; } + demorec_seeds = demorec_seeds_cvar; + demorec_diffs = demorec_diffs_cvar; + demo_synccompress = demorec_synccompress_cvar; + demorec_difftics = demorec_difftics_cvar; - g_demo_totalCnt = 0; + Bstrcpy(ScriptQuotes[122], "DEMO RECORDING STARTED"); + P_DoQuote(122, g_player[myconnectindex].ps); + + ud.reccnt = 0; + ud.recstat = 1; // + +#if KRANDDEBUG + krd_enable(); +#endif + + g_demo_cnt = 1; +} + + +static uint8_t g_demo_seedbuf[RECSYNCBUFSIZ]; + +static void dowritesync() +{ + int16_t tmpreccnt; + + fwrite("sYnC", 4, 1, g_demo_filePtr); + tmpreccnt = (int16_t)ud.reccnt; + fwrite(&tmpreccnt, sizeof(int16_t), 1, g_demo_filePtr); + if (demorec_seeds) + fwrite(g_demo_seedbuf, 1, ud.reccnt, g_demo_filePtr); + + if (demo_synccompress) + dfwrite(recsync, sizeof(input_t), ud.reccnt, g_demo_filePtr); + else //if (demo_synccompress==0) + fwrite(recsync, sizeof(input_t), ud.reccnt, g_demo_filePtr); ud.reccnt = 0; } static void G_DemoRecord(void) { int16_t i; + extern uint32_t sv_writediff(FILE *fil); + + g_demo_cnt++; + + if (demorec_diffs && (g_demo_cnt%demorec_difftics == 1)) + { + sv_writediff(g_demo_filePtr); + demorec_difftics = demorec_difftics_cvar; + } + + if (demorec_seeds) + g_demo_seedbuf[ud.reccnt] = (uint8_t)(randomseed>>24); TRAVERSE_CONNECT(i) { - copybufbyte(g_player[i].sync,&recsync[ud.reccnt],sizeof(input_t)); + Bmemcpy(&recsync[ud.reccnt], g_player[i].sync, sizeof(input_t)); ud.reccnt++; - g_demo_totalCnt++; - if (ud.reccnt >= RECSYNCBUFSIZ) - { - dfwrite(recsync,sizeof(input_t)*ud.multimode,ud.reccnt/ud.multimode,g_demo_filePtr); - ud.reccnt = 0; - } } + + if (ud.reccnt > RECSYNCBUFSIZ-MAXPLAYERS || (demorec_diffs && (g_demo_cnt%demorec_difftics == 0))) + dowritesync(); } void G_CloseDemoWrite(void) { + extern void sv_freemem(); + if (ud.recstat == 1) { if (ud.reccnt > 0) - { - dfwrite(recsync,sizeof(input_t)*ud.multimode,ud.reccnt/ud.multimode,g_demo_filePtr); + dowritesync(); - fseek(g_demo_filePtr,SEEK_SET,0L); - fwrite(&g_demo_totalCnt,sizeof(int32_t),1,g_demo_filePtr); - ud.recstat = ud.m_recstat = 0; - } + fwrite("EnD!", 4, 1, g_demo_filePtr); + + if (fseek(g_demo_filePtr, 20, SEEK_SET)) + perror("G_CloseDemoWrite: fseek"); + else + fwrite(&g_demo_cnt, sizeof(g_demo_cnt), 1, g_demo_filePtr); + + ud.recstat = ud.m_recstat = 0; fclose(g_demo_filePtr); + + sv_freemem(); + + Bstrcpy(ScriptQuotes[122], "DEMO RECORDING STOPPED"); + P_DoQuote(122, g_player[myconnectindex].ps); } +#if KRANDDEBUG + krd_print("krandrec.log"); +#endif } static int32_t g_whichDemo = 1; -extern int32_t premap_quickenterlevel; +extern int32_t sv_updatestate(int32_t frominit); -static int32_t G_PlaybackDemo(void) +static void dorestoremodes(int32_t menu) { - int32_t i,j,k,l; - int32_t foundemo = 0; + if (menu) g_player[myconnectindex].ps->gm |= MODE_MENU; + else g_player[myconnectindex].ps->gm &= ~MODE_MENU; + g_player[myconnectindex].ps->gm &= ~MODE_GAME; + g_player[myconnectindex].ps->gm |= MODE_DEMO; +} + +static int32_t doupdatestate(int32_t frominit) +{ + int32_t j,k; + j = g_player[myconnectindex].ps->gm&MODE_MENU; + k = sv_updatestate(frominit); +// tmpdifftime = g_demo_cnt+12; + dorestoremodes(j); + if (k) OSD_Printf("sv_updatestate() returned %d.\n", k); + return k; +} + +#define CORRUPT(code) do { corruptcode=code; goto corrupt; } while(0) + +#define DOREADSYNC(code) do \ +{ \ + uint16_t si; \ + int32_t i; \ + if (kread(g_demo_recFilePtr, &si, sizeof(uint16_t)) != (int32_t)sizeof(uint16_t)) CORRUPT(code); \ + i = si; \ + if (demo_hasseeds) \ + { \ + if (kread(g_demo_recFilePtr, g_demo_seedbuf, i) != i) CORRUPT(code); \ + } \ + if (demo_synccompress) \ + { \ + if (kdfread(recsync, sizeof(input_t), i, g_demo_recFilePtr) != i) CORRUPT(code+1); \ + } \ + else \ + if (kread(g_demo_recFilePtr, recsync, sizeof(input_t)*i) != (int32_t)sizeof(input_t)*i) CORRUPT(code+2); \ + ud.reccnt = i; \ +} while (0) + + +GAME_STATIC int32_t G_PlaybackDemo(void) +{ + int32_t bigi, j, k, initsyncofs, lastsyncofs, lastsynctic, lastsyncclock; + int32_t foundemo = 0, corruptcode, outofsync=0; static int32_t in_menu = 0; +// static int32_t tmpdifftime=0; if (ready2send) return 0; RECHECK: +#if KRANDDEBUG + if (foundemo) + krd_print("krandplay.log"); +#endif + in_menu = g_player[myconnectindex].ps->gm&MODE_MENU; pub = NUMPAGES; @@ -12168,7 +12269,6 @@ RECHECK: if (g_whichDemo > 1) { g_whichDemo = 1; - premap_quickenterlevel=0; goto RECHECK; } fadepal(0,0,0, 0,63,7); @@ -12186,89 +12286,200 @@ RECHECK: g_whichDemo++; if (g_whichDemo == 10) g_whichDemo = 1; - if (G_EnterLevel(MODE_DEMO)) ud.recstat = foundemo = 0; + g_player[myconnectindex].ps->gm &= ~MODE_GAME; + g_player[myconnectindex].ps->gm |= MODE_DEMO; + +// if (G_EnterLevel(MODE_DEMO)) +// { +// OSD_Printf("G_PlaybackDemo: G_EnterLevel\n"); +// ud.recstat = foundemo = 0; +// } +// + lastsyncofs = ktell(g_demo_recFilePtr); + initsyncofs = lastsyncofs; + lastsynctic = g_demo_cnt; + lastsyncclock = totalclock; + outofsync = 0; +#if KRANDDEBUG + krd_enable(); +#endif } - if (!premap_quickenterlevel) + if (foundemo == 0 || in_menu || KB_KeyWaiting() || numplayers > 1) { - if (foundemo == 0 || in_menu || KB_KeyWaiting() || numplayers > 1) - { - FX_StopAllSounds(); - S_ClearSoundLocks(); - g_player[myconnectindex].ps->gm |= MODE_MENU; - } + FX_StopAllSounds(); + S_ClearSoundLocks(); + g_player[myconnectindex].ps->gm |= MODE_MENU; } ready2send = 0; - i = 0; + bigi = 0; KB_FlushKeyboardQueue(); - k = 0; - - while (ud.reccnt > 0 || foundemo == 0) +// OSD_Printf("ticcnt=%d, total=%d\n", g_demo_cnt, g_demo_totalCnt); + while (g_demo_cnt < g_demo_totalCnt || foundemo==0) { - if (foundemo && (!g_demo_paused || g_demo_goalCnt!=0)) + if (foundemo && (!g_demo_paused || g_demo_goalCnt)) { - if (g_demo_goalCnt < 0) + static int32_t t1=0; + if (t1==0 && g_demo_goalCnt) + t1=getticks(); + + if (g_demo_goalCnt>0 && g_demo_goalCnt < g_demo_cnt) // rewind { - g_demo_goalCnt = -g_demo_goalCnt; + k = g_player[myconnectindex].ps->gm&MODE_MENU; + if (g_demo_goalCnt > lastsynctic) + { + if (doupdatestate(0)==0) + { + g_demo_cnt = lastsynctic; + klseek(g_demo_recFilePtr, lastsyncofs, SEEK_SET); + ud.reccnt = 0; - if (g_whichDemo > 1) // load the same demo again and FF from beginning... yay! - g_whichDemo--; - foundemo = 0; - ud.reccnt = 0; - kclose(g_demo_recFilePtr); - premap_quickenterlevel=1; - goto RECHECK; + totalclock = ototalclock = lockclock = lastsyncclock; + } + else CORRUPT(-1); + } + else + { +//loadfrombeg: +// j = sv_loadsnapshot(g_demo_recFilePtr, &g_demo_totalCnt); + j = doupdatestate(1); + if (!j) + { + klseek(g_demo_recFilePtr, initsyncofs, SEEK_SET); + g_levelTextTime = 0; + + g_demo_cnt = 1; + ud.reccnt = 0; + + ud.god = ud.cashman = ud.eog = ud.showallmap = 0; + ud.clipping = ud.scrollmode = ud.overhead_on = ud.pause_on = 0; + + totalclock = ototalclock = lockclock = 0; + } + else CORRUPT(0); + } + dorestoremodes(k); } while (totalclock >= (lockclock+TICSPERFRAME) - || (ud.reccnt > (TICRATE/TICSPERFRAME)*2 && ud.pause_on) - || (g_demo_goalCnt>0 && g_demo_goalCnt (TICRATE/TICSPERFRAME)*2 && ud.pause_on) + || (g_demo_goalCnt>0 && g_demo_cnt= RECSYNCBUFSIZ)) + if (ud.reccnt<=0) { - i = 0; - l = min(ud.reccnt,RECSYNCBUFSIZ); - if (kdfread(recsync,sizeof(input_t)*ud.multimode,l/ud.multimode,g_demo_recFilePtr) != l/ud.multimode) + char tmpbuf[4]; + + if (ud.reccnt<0) { - OSD_Printf(OSD_ERROR "Demo %d is corrupt.\n", g_whichDemo-1); + OSD_Printf("G_PlaybackDemo: ud.reccnt<0!\n"); + CORRUPT(1); + } + + bigi = 0; +//reread: + if (kread(g_demo_recFilePtr, tmpbuf, 4) != 4) CORRUPT(2); + + if (Bmemcmp(tmpbuf, "sYnC", 4)==0) + DOREADSYNC(3); + else if (demo_hasdiffs && Bmemcmp(tmpbuf, "dIfF", 4)==0) + { + extern int32_t sv_readdiff(int32_t fil); + + k=sv_readdiff(g_demo_recFilePtr); + if (k) + { + OSD_Printf("sv_readdiff() returned %d.\n", k); + CORRUPT(6); + } + else + { + lastsyncofs = ktell(g_demo_recFilePtr); + lastsynctic = g_demo_cnt; + lastsyncclock = totalclock; + if (kread(g_demo_recFilePtr, tmpbuf, 4) != 4) CORRUPT(7); + if (Bmemcmp(tmpbuf, "sYnC", 4)) CORRUPT(8); + DOREADSYNC(9); + + if ((g_demo_goalCnt==0 && demoplay_diffs) || + (g_demo_goalCnt>0 && ud.reccnt/ud.multimode >= g_demo_goalCnt-g_demo_cnt)) + { + doupdatestate(0); + } + } + } + else if (Bmemcmp(tmpbuf, "EnD!", 4)==0) + goto nextdemo; + else CORRUPT(12); + + if (demo_hasseeds) + outofsync = (uint16_t)(randomseed>>24) != g_demo_seedbuf[bigi]; + + if (0) + { +corrupt: + OSD_Printf(OSD_ERROR "Demo %d is corrupt (code %d).\n", g_whichDemo-1, corruptcode); +nextdemo: foundemo = 0; ud.reccnt = 0; kclose(g_demo_recFilePtr); g_player[myconnectindex].ps->gm |= MODE_MENU; + if (g_demo_goalCnt>0) + { + g_demo_goalCnt=0; + ud.config.SoundToggle = g_demo_soundToggle; + } goto RECHECK; } } TRAVERSE_CONNECT(j) { - copybufbyte(&recsync[i],&inputfifo[0][j],sizeof(input_t)); + copybufbyte(&recsync[bigi], &inputfifo[0][j], sizeof(input_t)); g_player[j].movefifoend++; - i++; + bigi++; ud.reccnt--; } - G_DoMoveThings(); + g_demo_cnt++; + + if (!g_demo_paused) + { + // assumption that ud.multimode doesn't change in a demo may not be true + // sometime in the future v v v v v v v v v + if (g_demo_goalCnt==0 || !demo_hasdiffs || ud.reccnt/ud.multimode>=g_demo_goalCnt-g_demo_cnt) + G_DoMoveThings(); // increases lockclock by TICSPERFRAME + else + lockclock += TICSPERFRAME; + } + else + { + k = ud.config.SoundToggle; + ud.config.SoundToggle = 0; + G_DoMoveThings(); + ud.config.SoundToggle = k; + } + ototalclock += TICSPERFRAME; if (g_demo_goalCnt > 0) { - if (g_demo_goalCnt (TICRATE/TICSPERFRAME)*2 && ud.pause_on)) - totalclock += TICSPERFRAME; + totalclock += TICSPERFRAME; + +// OSD_Printf("t:%d, l+T:%d; cnt:%d, goal:%d%s", totalclock, (lockclock+TICSPERFRAME), +// g_demo_cnt, g_demo_goalCnt, g_demo_cnt>=g_demo_goalCnt?" ":"\n"); + if (g_demo_cnt>=g_demo_goalCnt) + { + g_demo_goalCnt = 0; + ud.config.SoundToggle = g_demo_soundToggle; + } } } - - if (g_demo_goalCnt > 0 && ud.reccnt<=g_demo_goalCnt) - { - g_demo_goalCnt = 0; - ud.config.SoundToggle = g_demo_soundToggle; - premap_quickenterlevel = 0; - } } - else if (foundemo && g_demo_paused && g_demo_goalCnt==0) + else if (foundemo && g_demo_paused) { - lockclock = ototalclock = totalclock; + totalclock = lockclock; } if (foundemo == 0) @@ -12278,27 +12489,45 @@ RECHECK: G_HandleLocalKeys(); // j = min(max((totalclock-lockclock)*(65536/TICSPERFRAME),0),65536); + j = min(max((totalclock - ototalclock) * (65536 / 4),0),65536); + if (g_demo_paused && g_demo_rewind) + j = 65536-j; + G_DrawRooms(screenpeek,j); G_DisplayRest(j); - if (g_demo_showStats && (g_player[myconnectindex].ps->gm&MODE_MENU) == 0) + if ((g_player[myconnectindex].ps->gm&MODE_MENU) == 0) { - j=(g_demo_startCnt-ud.reccnt)/(TICRATE/TICSPERFRAME); - Bsprintf(buf, "%02d:%02d", j/60, j%60); - gametext(18,16,buf,0,2+8+16); + if (demoplay_showsync && outofsync) + gametext(160,100,"OUT OF SYNC",0,2+8+16); - rotatesprite(60<<16,16<<16,32768,0,SLIDEBAR,0,0,2+8+16,0,0,(xdim*95)/320,ydim-1); - rotatesprite(90<<16,16<<16,32768,0,SLIDEBAR,0,0,2+8+16,(xdim*95)/320,0,(xdim*125)/320,ydim-1); - rotatesprite(120<<16,16<<16,32768,0,SLIDEBAR,0,0,2+8+16,(xdim*125)/320,0,(xdim*155)/320,ydim-1); - rotatesprite(150<<16,16<<16,32768,0,SLIDEBAR,0,0,2+8+16,(xdim*155)/320,0,xdim-1,ydim-1); + if (g_demo_showStats) + { +// if (g_demo_cnt 1) && g_player[myconnectindex].ps->gm) @@ -12388,10 +12617,13 @@ RECHECK: #endif if (g_player[myconnectindex].ps->gm&MODE_MENU) goto RECHECK; +#if KRANDDEBUG + krd_print("krandplay.log"); +#endif return 1; } -static inline int32_t G_MoveLoop() +GAME_STATIC GAME_INLINE int32_t G_MoveLoop() { /* if (numplayers > 1) @@ -12412,7 +12644,7 @@ static inline int32_t G_MoveLoop() return 0; } -static int32_t G_DoMoveThings(void) +GAME_STATIC int32_t G_DoMoveThings(void) { int32_t i, j; // char ch; diff --git a/polymer/eduke32/source/gameexec.c b/polymer/eduke32/source/gameexec.c index 8397c5c90..50a9e4fbe 100644 --- a/polymer/eduke32/source/gameexec.c +++ b/polymer/eduke32/source/gameexec.c @@ -32,6 +32,14 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "osdcmds.h" #include "osd.h" +#if KRANDDEBUG +# define GAMEEXEC_INLINE +# define GAMEEXEC_STATIC +#else +# define GAMEEXEC_INLINE inline +# define GAMEEXEC_STATIC static +#endif + void G_RestoreMapState(mapstate_t *save); void G_SaveMapState(mapstate_t *save); @@ -41,7 +49,7 @@ int32_t g_errorLineNum; int32_t g_tw; extern int32_t ticrandomseed; -static int32_t X_DoExecute(int32_t once); +GAMEEXEC_STATIC int32_t X_DoExecute(int32_t once); #include "gamestructures.c" @@ -151,7 +159,7 @@ static int32_t A_CheckSquished(int32_t i, int32_t p) return 0; } -static inline void P_ForceAngle(DukePlayer_t *p) +GAMEEXEC_STATIC GAMEEXEC_INLINE void P_ForceAngle(DukePlayer_t *p) { int32_t n = 128-(krand()&255); @@ -161,7 +169,7 @@ static inline void P_ForceAngle(DukePlayer_t *p) p->rotscrnang = n>>1; } -static int32_t A_Dodge(spritetype *s) +GAMEEXEC_STATIC int32_t A_Dodge(spritetype *s) { int32_t bx,by,bxvect,byvect,d,i; int32_t mx = s->x, my = s->y; @@ -382,7 +390,7 @@ int32_t G_GetAngleDelta(int32_t a,int32_t na) return (na-a); } -static inline void X_AlterAng(int32_t a) +GAMEEXEC_STATIC GAMEEXEC_INLINE void X_AlterAng(int32_t a) { intptr_t *moveptr = (intptr_t *)vm.g_t[1]; int32_t ticselapsed = (vm.g_t[0])&31; @@ -448,7 +456,7 @@ static inline void X_AlterAng(int32_t a) } } -static void X_Move(void) +GAMEEXEC_STATIC void X_Move(void) { int32_t l; intptr_t *moveptr; @@ -647,7 +655,7 @@ static void X_Move(void) } } -static inline void __fastcall X_DoConditional(register int32_t condition) +GAMEEXEC_STATIC GAMEEXEC_INLINE void __fastcall X_DoConditional(register int32_t condition) { if (condition) { @@ -666,7 +674,7 @@ static inline void __fastcall X_DoConditional(register int32_t condition) } } -static int32_t X_DoExecute(int32_t once) +GAMEEXEC_STATIC int32_t X_DoExecute(int32_t once) { register int32_t tw = *insptr; @@ -4776,6 +4784,23 @@ void A_Execute(int32_t iActor,int32_t iPlayer,int32_t lDist) changespritestat(vm.g_i,STAT_ZOMBIEACTOR); } +void G_FreeMapState2(mapstate_t *save) +{ + int i; + for (i=g_gameVarCount-1; i>=0; i--) + { + if (aGameVars[i].dwFlags & GAMEVAR_NORESET) continue; + if (aGameVars[i].dwFlags & (GAMEVAR_PERPLAYER|GAMEVAR_PERACTOR)) + { + if (save->vars[i]) + { + Bfree(save->vars[i]); + save->vars[i]=0; + } + } + } +} + void G_SaveMapState(mapstate_t *save) { if (save != NULL) diff --git a/polymer/eduke32/source/gamevars.c b/polymer/eduke32/source/gamevars.c index a46524e60..a937b740b 100644 --- a/polymer/eduke32/source/gamevars.c +++ b/polymer/eduke32/source/gamevars.c @@ -97,11 +97,18 @@ static void Gv_Clear(void) return; } -int32_t Gv_ReadSave(int32_t fil) +int32_t Gv_ReadSave(int32_t fil, int32_t newbehav) { int32_t i, j; intptr_t l; char savedstate[MAXVOLUMES*MAXLEVELS]; + char tbuf[12]; + + if (newbehav) + { + if (kread(fil, tbuf, 12)!=12) goto corrupt; + if (Bmemcmp(tbuf, "BEG: EDuke32", 12)) { OSD_Printf("BEG ERR\n"); return 2; } + } Bmemset(&savedstate,0,sizeof(savedstate)); @@ -197,10 +204,18 @@ int32_t Gv_ReadSave(int32_t fil) } } - if (kdfread(&l,sizeof(l),1,fil) != 1) goto corrupt; - if (kdfread(g_szBuf,l,1,fil) != 1) goto corrupt; - g_szBuf[l]=0; - OSD_Printf("%s\n",g_szBuf); + if (!newbehav) + { + if (kdfread(&l,sizeof(l),1,fil) != 1) goto corrupt; + if (kdfread(g_szBuf,l,1,fil) != 1) goto corrupt; + g_szBuf[l]=0; + OSD_Printf("%s\n",g_szBuf); + } + else + { + if (kread(fil, tbuf, 12)!=12) goto corrupt; + if (Bmemcmp(tbuf, "EOF: EDuke32", 12)) { OSD_Printf("EOF ERR\n"); return 2; } + } #if 0 { @@ -220,7 +235,7 @@ corrupt: return(1); } -void Gv_WriteSave(FILE *fil) +void Gv_WriteSave(FILE *fil, int32_t newbehav) { int32_t i, j; intptr_t l; @@ -229,6 +244,9 @@ void Gv_WriteSave(FILE *fil) Bmemset(&savedstate,0,sizeof(savedstate)); // AddLog("Saving Game Vars to File"); + if (newbehav) + fwrite("BEG: EDuke32", 12, 1, fil); + dfwrite(&g_gameVarCount,sizeof(g_gameVarCount),1,fil); for (i=0; i: adjusts gamma ramp",(void*)&vid_contrast, CVAR_DOUBLE|CVAR_FUNCPTR, 0, 0, 10 }, { "vid_brightness","vid_brightness : adjusts gamma ramp",(void*)&vid_brightness, CVAR_DOUBLE|CVAR_FUNCPTR, 0, 0, 10 }, + { "demorec_diffs","demorec_diffs: enable/disable diff recording in demos",(void*)&demorec_diffs_cvar, CVAR_BOOL, 0, 0, 1 }, + { "demorec_force","demorec_force: enable/disable forced demo recording",(void*)&demorec_force_cvar, CVAR_BOOL|CVAR_NOSAVE, 0, 0, 1 }, + { "demorec_difftics","demorec_difftics : sets game tic interval after which a diff is recorded",(void*)&demorec_difftics_cvar, CVAR_INT, 0, 2, 60*(TICRATE/TICSPERFRAME) }, + { "demorec_diffcompress","demorec_diffcompress : Compression method for diffs. (0: none, 1: KSLZW)",(void*)&demorec_diffcompress_cvar, CVAR_INT, 0, 0, 1 }, + { "demorec_synccompress","demorec_synccompress : Compression method for input. (0: none, 1: KSLZW)",(void*)&demorec_synccompress_cvar, CVAR_INT, 0, 0, 1 }, + { "demorec_seeds","demorec_seeds: enable/disable recording of random seed for later sync checking",(void*)&demorec_seeds_cvar, CVAR_BOOL, 0, 0, 1 }, + { "demoplay_diffs","demoplay_diffs: enable/disable application of diffs in demo playback",(void*)&demoplay_diffs, CVAR_BOOL, 0, 0, 1 }, + { "demoplay_showsync","demoplay_showsync: enable/disable display of sync status",(void*)&demoplay_showsync, CVAR_BOOL, 0, 0, 1 }, }; osdcmd_cheatsinfo_stat.cheatnum = -1; diff --git a/polymer/eduke32/source/premap.c b/polymer/eduke32/source/premap.c index 81b50e9ba..a57228a8f 100644 --- a/polymer/eduke32/source/premap.c +++ b/polymer/eduke32/source/premap.c @@ -1271,16 +1271,12 @@ static inline void prelevel(char g) } } -int32_t premap_quickenterlevel=0; void G_NewGame(int32_t vn,int32_t ln,int32_t sk) { DukePlayer_t *p = g_player[0].ps; int32_t i; - if (premap_quickenterlevel) - goto quick; - handleevents(); Net_GetPackets(); @@ -1321,7 +1317,6 @@ void G_NewGame(int32_t vn,int32_t ln,int32_t sk) FX_StopAllSounds(); } -quick: g_showShareware = GAMETICSPERSEC*34; ud.level_number = ln; @@ -1729,15 +1724,12 @@ int32_t G_EnterLevel(int32_t g) } } - if (!premap_quickenterlevel) - { - i = ud.screen_size; - ud.screen_size = 0; + i = ud.screen_size; + ud.screen_size = 0; - G_DoLoadScreen(NULL, -1); - G_UpdateScreenArea(); - ud.screen_size = i; - } + G_DoLoadScreen(NULL, -1); + G_UpdateScreenArea(); + ud.screen_size = i; if (boardfilename[0] != 0 && ud.m_level_number == 7 && ud.m_volume_number == 0) { @@ -1875,12 +1867,10 @@ int32_t G_EnterLevel(int32_t g) } } - if (!premap_quickenterlevel) - { - g_precacheCount = 0; - clearbufbyte(gotpic,sizeof(gotpic),0L); - clearbufbyte(precachehightile, sizeof(precachehightile), 0l); - } + g_precacheCount = 0; + clearbufbyte(gotpic,sizeof(gotpic),0L); + clearbufbyte(precachehightile, sizeof(precachehightile), 0l); + //clearbufbyte(ActorExtra,sizeof(ActorExtra),0l); // JBF 20040531: yes? no? prelevel(g); @@ -1891,8 +1881,7 @@ int32_t G_EnterLevel(int32_t g) cachedebug = 0; automapping = 0; - if (!premap_quickenterlevel) - G_CacheMapData(); + G_CacheMapData(); if (ud.recstat != 2) { @@ -1942,13 +1931,10 @@ int32_t G_EnterLevel(int32_t g) //g_player[myconnectindex].ps->palette = palette; //G_FadePalette(0,0,0,0); - if (!premap_quickenterlevel) - { - P_SetGamePalette(g_player[myconnectindex].ps, palette, 0); // JBF 20040308 + P_SetGamePalette(g_player[myconnectindex].ps, palette, 0); // JBF 20040308 - P_UpdateScreenPal(g_player[myconnectindex].ps); - flushperms(); - } + P_UpdateScreenPal(g_player[myconnectindex].ps); + flushperms(); everyothertime = 0; g_globalRandom = 0; @@ -1961,17 +1947,14 @@ int32_t G_EnterLevel(int32_t g) g_restorePalette = 1; - if (!premap_quickenterlevel) - { - Net_WaitForServer(); + Net_WaitForServer(); // mmulti_flushpackets(); - G_FadePalette(0,0,0,0); - G_UpdateScreenArea(); - clearview(0L); - G_DrawBackground(); - G_DrawRooms(myconnectindex,65536); - } + G_FadePalette(0,0,0,0); + G_UpdateScreenArea(); + clearview(0L); + G_DrawBackground(); + G_DrawRooms(myconnectindex,65536); g_player[myconnectindex].ps->over_shoulder_on = 0; @@ -1985,8 +1968,7 @@ int32_t G_EnterLevel(int32_t g) Bmemcpy(¤tboardfilename[0],&boardfilename[0],BMAX_PATH); X_OnEvent(EVENT_ENTERLEVEL, -1, -1, -1); - if (!premap_quickenterlevel) - OSD_Printf(OSDTEXT_YELLOW "E%dL%d: %s\n",ud.volume_number+1,ud.level_number+1,MapInfo[(ud.volume_number*MAXLEVELS)+ud.level_number].name); + OSD_Printf(OSDTEXT_YELLOW "E%dL%d: %s\n",ud.volume_number+1,ud.level_number+1,MapInfo[(ud.volume_number*MAXLEVELS)+ud.level_number].name); return 0; } diff --git a/polymer/eduke32/source/savegame.c b/polymer/eduke32/source/savegame.c index a4b5ed53e..7a6e58c45 100644 --- a/polymer/eduke32/source/savegame.c +++ b/polymer/eduke32/source/savegame.c @@ -414,7 +414,7 @@ int32_t G_LoadPlayer(int32_t spot) ud.m_noexits = ud.noexits; - if (Gv_ReadSave(fil)) goto corrupt; + if (Gv_ReadSave(fil, 0)) goto corrupt; kclose(fil); @@ -904,7 +904,7 @@ int32_t G_SavePlayer(int32_t spot) dfwrite(&ud.noexits,sizeof(ud.noexits),1,fil); - Gv_WriteSave(fil); + Gv_WriteSave(fil, 0); fclose(fil); @@ -922,3 +922,1357 @@ int32_t G_SavePlayer(int32_t spot) return(0); } + + +////////// GENERIC SAVING/LOADING SYSTEM ////////// + +typedef struct dataspec_ +{ + uint32_t flags; + void *ptr; + uint32_t size; + intptr_t cnt; +} dataspec_t; + +#define SV_MAJOR_VER 0 +#define SV_MINOR_VER 1 +#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 assumend constant throughout demo +#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, 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, &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; i65536) \ + 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)) + { + (*(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 const dataspec_t svgm_udnetw[]; +static const dataspec_t svgm_secwsp[]; +static const dataspec_t svgm_script[]; +static const dataspec_t svgm_anmisc[]; +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>3][3]; +static uint32_t savegame_bitptrsize; + +static void sv_calcbitptrsize() +{ + savegame_bitptrsize = (g_scriptSize+7)>>3; +} +static void sv_prescriptsave_once() +{ + int32_t i; + for (i=0; i>3]&(BITPTR_POINTER<<(i&7))) + script[i] = (intptr_t)((intptr_t *)script[i] - &script[0]); + for (i=0; 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= 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>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 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_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, &ActorExtra[0], sizeof(ActorData_t), MAXSPRITES }, + { DS_SAVEFN|DS_LOADFN, (void *)&sv_postactordata, 0, 1 }, + + { DS_END, 0, 0, 0 } +}; + + +static uint8_t savegame_quotedef[MAXQUOTES>>3]; +static char (*savegame_quotes)[MAXQUOTELEN]; +static char (*savegame_quoteredefs)[MAXQUOTELEN]; + +#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_restdata[SVARDATALEN]; + +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)§or[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)§or[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>3] |= 1<<(i&7); + Bmemcpy(savegame_quotes[i], ScriptQuotes[i], MAXQUOTELEN); + } +} +static void sv_quoteload() +{ + int32_t i; + for (i=0; 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; iplanelist = 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>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, ¶llaxyscale, 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 } +}; + + +#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", 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], 19, 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[19]; + const time_t t=time(NULL); + struct tm st; + Bsprintf(buf, "Eduke32 demo"); + if (t>=0 && localtime_r(&t, &st)==&st) + Bsprintf(buf, "Edemo32 %04d%02d%02d", st.tm_year+1900, st.tm_mon+1, st.tm_mday); + SAVEWRU(&buf, 19, 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[19]; + int32_t i; + + if (readspecdata(svgm_udnetw, fil, &mem)) + return -2; + PRINTSIZE(ud); + if (spot >= 0 && ud.multimode!=numplayers) + return 2; + + if (numplayers > 1) + { + if (LOADRDU(&tbuf, 19, 1)) return -3; + } + else + if (LOADRDU(&ud.savegame[spot][0], 19, 1)) return -3; + + 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= 0) + wall[animwall[i].wallnum].picnum = wall[animwall[i].wallnum].extra; + } + else + { + for (i=0; i= 0) + { + switch (sprite[i].lotag) + { + case 31: + G_SetInterpolation(§or[sprite[i].sectnum].floorz); + break; + case 32: + G_SetInterpolation(§or[sprite[i].sectnum].ceilingz); + break; + case 25: + G_SetInterpolation(§or[sprite[i].sectnum].floorz); + G_SetInterpolation(§or[sprite[i].sectnum].ceilingz); + break; + case 17: + G_SetInterpolation(§or[sprite[i].sectnum].floorz); + G_SetInterpolation(§or[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(i); + 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