Subversion Repositories eduke32

Rev

Rev 4815 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1677 terminx 1
//-------------------------------------------------------------------------
2
/*
3
Copyright (C) 2010 EDuke32 developers and contributors
4
 
5
This file is part of EDuke32.
6
 
7
EDuke32 is free software; you can redistribute it and/or
8
modify it under the terms of the GNU General Public License version 2
9
as published by the Free Software Foundation.
10
 
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
 
15
See the GNU General Public License for more details.
16
 
17
You should have received a copy of the GNU General Public License
18
along with this program; if not, write to the Free Software
4541 hendricks2 19
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
1677 terminx 20
*/
21
//-------------------------------------------------------------------------
22
 
23
#include "duke3d.h"
24
#include "demo.h"
2326 helixhorne 25
//#include "premap.h"  // G_UpdateScreenArea()
1678 terminx 26
#include "menus.h"
2198 helixhorne 27
#include "savegame.h"
2728 hendricks2 28
#include "input.h"
1677 terminx 29
 
2994 helixhorne 30
char g_firstDemoFile[BMAX_PATH];
1677 terminx 31
 
3001 helixhorne 32
FILE *g_demo_filePtr = (FILE *)NULL;  // write
33
int32_t g_demo_recFilePtr = -1;  // read
34
 
1677 terminx 35
int32_t g_demo_cnt;
36
int32_t g_demo_goalCnt=0;
37
int32_t g_demo_totalCnt;
38
int32_t g_demo_paused=0;
39
int32_t g_demo_rewind=0;
40
int32_t g_demo_showStats=1;
3001 helixhorne 41
static int32_t g_demo_soundToggle;
1677 terminx 42
 
3007 helixhorne 43
static int32_t demo_hasdiffs, demorec_diffs=1, demorec_difftics = 2*REALGAMETICSPERSEC;
1677 terminx 44
int32_t demoplay_diffs=1;
45
int32_t demorec_diffs_cvar=1;
46
int32_t demorec_force_cvar=0;
3007 helixhorne 47
int32_t demorec_difftics_cvar = 2*REALGAMETICSPERSEC;
1677 terminx 48
int32_t demorec_diffcompress_cvar=1;
49
int32_t demorec_synccompress_cvar=1;
50
int32_t demorec_seeds_cvar=1;
51
int32_t demoplay_showsync=1;
52
 
53
static int32_t demo_synccompress=1, demorec_seeds=1, demo_hasseeds;
54
 
2994 helixhorne 55
static void Demo_RestoreModes(int32_t menu)
1677 terminx 56
{
2994 helixhorne 57
    if (menu)
4738 hendricks2 58
        M_OpenMenu(myconnectindex);
2994 helixhorne 59
    else
4738 hendricks2 60
        M_CloseMenu(myconnectindex);
2994 helixhorne 61
 
1677 terminx 62
    g_player[myconnectindex].ps->gm &= ~MODE_GAME;
63
    g_player[myconnectindex].ps->gm |= MODE_DEMO;
64
}
65
 
2994 helixhorne 66
void Demo_PrepareWarp(void)
1677 terminx 67
{
68
    if (!g_demo_paused)
69
    {
70
        g_demo_soundToggle = ud.config.SoundToggle;
71
        ud.config.SoundToggle = 0;
72
    }
3001 helixhorne 73
 
1677 terminx 74
    FX_StopAllSounds();
75
    S_ClearSoundLocks();
76
}
77
 
78
 
2207 helixhorne 79
static int32_t G_OpenDemoRead(int32_t g_whichDemo) // 0 = mine
1677 terminx 80
{
81
    int32_t i;
2207 helixhorne 82
    savehead_t saveh;
83
 
3003 helixhorne 84
    char demofn[14];
85
    const char *demofnptr;
1677 terminx 86
 
3003 helixhorne 87
    if (g_whichDemo == 1 && g_firstDemoFile[0])
88
    {
89
        demofnptr = g_firstDemoFile;
90
    }
1677 terminx 91
    else
92
    {
3045 helixhorne 93
        Bsprintf(demofn, DEMOFN_FMT, g_whichDemo);
3003 helixhorne 94
        demofnptr = demofn;
1677 terminx 95
    }
96
 
3003 helixhorne 97
    g_demo_recFilePtr = kopen4loadfrommod(demofnptr, g_loadFromGroupOnly);
98
    if (g_demo_recFilePtr == -1)
99
        return 0;
100
 
2547 helixhorne 101
    Bassert(g_whichDemo >= 1);
2207 helixhorne 102
    i = sv_loadsnapshot(g_demo_recFilePtr, -g_whichDemo, &saveh);
103
    if (i)
1677 terminx 104
    {
2207 helixhorne 105
        OSD_Printf(OSD_ERROR "There were errors opening demo %d (code: %d).\n", g_whichDemo, i);
106
        kclose(g_demo_recFilePtr); g_demo_recFilePtr = -1;
107
        return 0;
108
    }
1677 terminx 109
 
2207 helixhorne 110
    demo_hasdiffs = saveh.recdiffsp;
111
    g_demo_totalCnt = saveh.reccnt;
112
    demo_synccompress = saveh.synccompress;
1677 terminx 113
 
2207 helixhorne 114
    demo_hasseeds = demo_synccompress&2;
115
    demo_synccompress &= 1;
1677 terminx 116
 
3007 helixhorne 117
    i = g_demo_totalCnt/REALGAMETICSPERSEC;
2961 helixhorne 118
    OSD_Printf("demo %d duration: %d min %d sec\n", g_whichDemo, i/60, i%60);
1677 terminx 119
 
3001 helixhorne 120
    g_demo_cnt = 1;
2207 helixhorne 121
    ud.reccnt = 0;
1677 terminx 122
 
2207 helixhorne 123
    ud.god = ud.cashman = ud.eog = ud.showallmap = 0;
2454 helixhorne 124
    ud.noclip = ud.scrollmode = ud.overhead_on = 0; //= ud.pause_on = 0;
2207 helixhorne 125
 
126
    totalclock = ototalclock = lockclock = 0;
127
 
128
    return 1;
1677 terminx 129
}
130
 
131
#if KRANDDEBUG
132
extern void krd_enable(int32_t which);
133
extern int32_t krd_print(const char *filename);
134
#endif
135
 
136
void G_OpenDemoWrite(void)
137
{
4211 helixhorne 138
#ifdef LUNATIC
139
    // TODO: Currently, we can't diff gamevars in Lunatic...
140
    Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "DEMOS UNSUPPORTED IN LUNATIC BUILD");
141
    P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
142
    ud.recstat = ud.m_recstat = 0;
143
    return;
144
#else
2961 helixhorne 145
    char demofn[BMAX_PATH];
1677 terminx 146
    int32_t i, demonum=1;
147
 
2207 helixhorne 148
    if (ud.recstat == 2)
149
    {
150
        kclose(g_demo_recFilePtr);
151
        g_demo_recFilePtr = -1;
152
    }
1677 terminx 153
 
154
    if ((g_player[myconnectindex].ps->gm&MODE_GAME) && g_player[myconnectindex].ps->dead_flag)
155
    {
1802 terminx 156
        Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "CANNOT START DEMO RECORDING WHEN DEAD!");
157
        P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
1677 terminx 158
        ud.recstat = ud.m_recstat = 0;
159
        return;
160
    }
4211 helixhorne 161
# if !defined LUNATIC
1677 terminx 162
    if (demorec_diffs_cvar && !demorec_force_cvar)
163
        for (i=1; i<g_scriptSize-2; i++)
164
        {
165
            intptr_t w=script[i];
166
            if ((w&0x0fff)==CON_RESIZEARRAY && (w>>12) && script[i+1]>=0 && script[i+1]<g_gameArrayCount)
167
            {
168
                OSD_Printf("\nThe CON code possibly contains a RESIZEARRAY command.\n");
169
                OSD_Printf("Gamearrays that change their size during the game are unsupported by\n");
170
                OSD_Printf("the demo recording system. If you are sure that the code doesn't\n");
171
                OSD_Printf("contain a RESIZEARRAY command, you can force recording with the\n");
172
                OSD_Printf("`demorec_force' cvar. Alternatively, you can disable diff recording\n");
173
                OSD_Printf("with the `demorec_diffs' cvar.\n\n");
4133 helixhorne 174
                Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "FAILED STARTING DEMO RECORDING. SEE OSD.");
1802 terminx 175
                P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
1677 terminx 176
                ud.recstat = ud.m_recstat = 0;
177
                return;
178
            }
179
        }
4211 helixhorne 180
# endif
3412 helixhorne 181
    do
182
    {
183
        if (demonum == MAXDEMOS)
184
            return;
185
 
186
        if (G_ModDirSnprintf(demofn, sizeof(demofn), DEMOFN_FMT, demonum))
1677 terminx 187
        {
3412 helixhorne 188
            initprintf("Couldn't start demo writing: INTERNAL ERROR: file name too long\n");
189
            goto error_wopen_demo;
190
        }
1840 terminx 191
 
3412 helixhorne 192
        demonum++;
2960 helixhorne 193
 
3412 helixhorne 194
        g_demo_filePtr = Bfopen(demofn, "rb");
195
        if (g_demo_filePtr == NULL)
196
            break;
2997 helixhorne 197
 
3412 helixhorne 198
        MAYBE_FCLOSE_AND_NULL(g_demo_filePtr);
199
    }
200
    while (1);
2994 helixhorne 201
 
3412 helixhorne 202
    g_demo_filePtr = Bfopen(demofn,"wb");
203
    if (g_demo_filePtr == NULL)
204
        return;
1677 terminx 205
 
3412 helixhorne 206
    i=sv_saveandmakesnapshot(g_demo_filePtr, -1, demorec_diffs_cvar, demorec_diffcompress_cvar,
207
                             demorec_synccompress_cvar|(demorec_seeds_cvar<<1));
208
    if (i)
209
    {
210
        MAYBE_FCLOSE_AND_NULL(g_demo_filePtr);
2960 helixhorne 211
error_wopen_demo:
3412 helixhorne 212
        Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "FAILED STARTING DEMO RECORDING. SEE OSD FOR DETAILS.");
213
        P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
214
        ud.recstat = ud.m_recstat = 0;
215
        return;
216
    }
2994 helixhorne 217
 
3412 helixhorne 218
    demorec_seeds = demorec_seeds_cvar;
219
    demorec_diffs = demorec_diffs_cvar;
220
    demo_synccompress = demorec_synccompress_cvar;
221
    demorec_difftics = demorec_difftics_cvar;
1677 terminx 222
 
3412 helixhorne 223
    Bsprintf(ScriptQuotes[QUOTE_RESERVED4], "DEMO %d RECORDING STARTED", demonum-1);
224
    P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
1677 terminx 225
 
3412 helixhorne 226
    ud.reccnt = 0;
227
    ud.recstat = ud.m_recstat = 1;  //
1677 terminx 228
 
4211 helixhorne 229
# if KRANDDEBUG
3412 helixhorne 230
    krd_enable(1);
4211 helixhorne 231
# endif
232
    g_demo_cnt = 1;
1677 terminx 233
#endif
234
}
235
 
3021 helixhorne 236
// demo_profile: < 0: prepare
237
static int32_t g_demo_playFirstFlag, g_demo_profile, g_demo_stopProfile;
3024 helixhorne 238
static int32_t g_demo_exitAfter;
239
void Demo_PlayFirst(int32_t prof, int32_t exitafter)
3006 helixhorne 240
{
241
    g_demo_playFirstFlag = 1;
3024 helixhorne 242
    g_demo_exitAfter = exitafter;
3021 helixhorne 243
    Bassert(prof >= 0);
244
    g_demo_profile = -prof;  // prepare
3006 helixhorne 245
}
1677 terminx 246
 
3045 helixhorne 247
void Demo_SetFirst(const char *demostr)
248
{
249
    char *tailptr;
250
    int32_t i = Bstrtol(demostr, &tailptr, 10);
3006 helixhorne 251
 
3045 helixhorne 252
    if (tailptr==demostr+Bstrlen(demostr) && (unsigned)i < MAXDEMOS)  // demo number passed
253
        Bsprintf(g_firstDemoFile, DEMOFN_FMT, i);
254
    else  // demo file name passed
255
        maybe_append_ext(g_firstDemoFile, sizeof(g_firstDemoFile), demostr, ".edm");
256
}
257
 
258
 
1677 terminx 259
static uint8_t g_demo_seedbuf[RECSYNCBUFSIZ];
260
 
2994 helixhorne 261
static void Demo_WriteSync()
1677 terminx 262
{
263
    int16_t tmpreccnt;
264
 
265
    fwrite("sYnC", 4, 1, g_demo_filePtr);
266
    tmpreccnt = (int16_t)ud.reccnt;
267
    fwrite(&tmpreccnt, sizeof(int16_t), 1, g_demo_filePtr);
268
    if (demorec_seeds)
269
        fwrite(g_demo_seedbuf, 1, ud.reccnt, g_demo_filePtr);
270
 
271
    if (demo_synccompress)
272
        dfwrite(recsync, sizeof(input_t), ud.reccnt, g_demo_filePtr);
273
    else //if (demo_synccompress==0)
274
        fwrite(recsync, sizeof(input_t), ud.reccnt, g_demo_filePtr);
2994 helixhorne 275
 
1677 terminx 276
    ud.reccnt = 0;
277
}
278
 
279
void G_DemoRecord(void)
280
{
281
    int16_t i;
282
 
283
    g_demo_cnt++;
284
 
285
    if (demorec_diffs && (g_demo_cnt%demorec_difftics == 1))
286
    {
287
        sv_writediff(g_demo_filePtr);
288
        demorec_difftics = demorec_difftics_cvar;
289
    }
290
 
291
    if (demorec_seeds)
292
        g_demo_seedbuf[ud.reccnt] = (uint8_t)(randomseed>>24);
293
 
2379 helixhorne 294
    for (TRAVERSE_CONNECT(i))
1677 terminx 295
    {
296
        Bmemcpy(&recsync[ud.reccnt], g_player[i].sync, sizeof(input_t));
297
        ud.reccnt++;
298
    }
299
 
300
    if (ud.reccnt > RECSYNCBUFSIZ-MAXPLAYERS || (demorec_diffs && (g_demo_cnt%demorec_difftics == 0)))
2994 helixhorne 301
        Demo_WriteSync();
1677 terminx 302
}
303
 
304
void G_CloseDemoWrite(void)
305
{
306
    if (ud.recstat == 1)
307
    {
308
        if (ud.reccnt > 0)
2994 helixhorne 309
            Demo_WriteSync();
1677 terminx 310
 
311
        fwrite("EnD!", 4, 1, g_demo_filePtr);
312
 
2207 helixhorne 313
        // lastly, we need to write the number of written recsyncs to the demo file
314
        if (fseek(g_demo_filePtr, offsetof(savehead_t, reccnt), SEEK_SET))
315
            perror("G_CloseDemoWrite: final fseek");
1677 terminx 316
        else
317
            fwrite(&g_demo_cnt, sizeof(g_demo_cnt), 1, g_demo_filePtr);
318
 
319
        ud.recstat = ud.m_recstat = 0;
2995 helixhorne 320
        MAYBE_FCLOSE_AND_NULL(g_demo_filePtr);
1677 terminx 321
 
322
        sv_freemem();
323
 
1802 terminx 324
        Bstrcpy(ScriptQuotes[QUOTE_RESERVED4], "DEMO RECORDING STOPPED");
325
        P_DoQuote(QUOTE_RESERVED4, g_player[myconnectindex].ps);
1677 terminx 326
    }
327
#if KRANDDEBUG
328
    krd_print("krandrec.log");
329
#endif
330
}
331
 
332
static int32_t g_whichDemo = 1;
333
 
2994 helixhorne 334
static int32_t Demo_UpdateState(int32_t frominit)
1677 terminx 335
{
2994 helixhorne 336
    int32_t j = g_player[myconnectindex].ps->gm&MODE_MENU;
337
    int32_t k = sv_updatestate(frominit);
1677 terminx 338
    //                            tmpdifftime = g_demo_cnt+12;
2994 helixhorne 339
    Demo_RestoreModes(j);
340
 
341
    if (k)
342
        OSD_Printf("sv_updatestate() returned %d.\n", k);
1677 terminx 343
    return k;
344
}
345
 
346
#define CORRUPT(code) do { corruptcode=code; goto corrupt; } while(0)
347
 
2994 helixhorne 348
static int32_t Demo_ReadSync(int32_t errcode)
349
{
350
    uint16_t si;
351
    int32_t i;
1677 terminx 352
 
2994 helixhorne 353
    if (kread(g_demo_recFilePtr, &si, sizeof(uint16_t)) != sizeof(uint16_t))
354
        return errcode;
1677 terminx 355
 
2994 helixhorne 356
    i = si;
357
    if (demo_hasseeds)
358
    {
359
        if (kread(g_demo_recFilePtr, g_demo_seedbuf, i) != i)
360
            return errcode;
361
    }
362
 
363
    if (demo_synccompress)
364
    {
365
        if (kdfread(recsync, sizeof(input_t), i, g_demo_recFilePtr) != i)
366
            return errcode+1;
367
    }
368
    else
369
    {
370
        int32_t bytes = sizeof(input_t)*i;
371
 
372
        if (kread(g_demo_recFilePtr, recsync, bytes) != bytes)
373
            return errcode+2;
374
    }
375
 
376
    ud.reccnt = i;
377
    return 0;
378
}
379
 
3021 helixhorne 380
////////// DEMO PROFILING (TIMEDEMO MODE) //////////
381
static struct {
382
    int32_t numtics, numframes;
383
    double totalgamems;
384
    double totalroomsdrawms, totalrestdrawms;
3935 helixhorne 385
    double starthiticks;
3021 helixhorne 386
} g_prof;
2994 helixhorne 387
 
3021 helixhorne 388
int32_t Demo_IsProfiling(void)
389
{
390
    return (g_demo_profile > 0);
391
}
392
 
3022 helixhorne 393
static void Demo_StopProfiling(void)
3021 helixhorne 394
{
395
    g_demo_stopProfile = 1;
396
}
397
 
398
static void Demo_GToc(double t)
399
{
400
    g_prof.numtics++;
3935 helixhorne 401
    g_prof.totalgamems += gethiticks()-t;
3021 helixhorne 402
}
403
 
404
static void Demo_RToc(double t1, double t2)
405
{
406
    g_prof.numframes++;
407
    g_prof.totalroomsdrawms += t2-t1;
3935 helixhorne 408
    g_prof.totalrestdrawms += gethiticks()-t2;
3021 helixhorne 409
}
410
 
411
static void Demo_DisplayProfStatus(void)
412
{
413
    char buf[64];
414
 
415
    static int32_t lastpercent=-1;
416
    int32_t percent = (100*g_demo_cnt)/g_demo_totalCnt;
417
 
418
    if (lastpercent == percent)
419
        return;
420
    lastpercent = percent;
421
 
422
    clearallviews(0);
423
    Bsnprintf(buf, sizeof(buf), "timing... %d/%d game tics (%d %%)",
424
              g_demo_cnt, g_demo_totalCnt, percent);
425
    gametext(160,60, buf, 0, 2+8+16);
426
    nextpage();
427
}
428
 
429
static void Demo_SetupProfile(void)
430
{
431
    g_demo_profile *= -1;  // now >0: profile for real
432
 
433
    g_demo_soundToggle = ud.config.SoundToggle;
434
    ud.config.SoundToggle = 0;  // restored by Demo_FinishProfile()
435
 
436
    Bmemset(&g_prof, 0, sizeof(g_prof));
437
 
3935 helixhorne 438
    g_prof.starthiticks = gethiticks();
3021 helixhorne 439
}
440
 
441
static void Demo_FinishProfile(void)
442
{
443
    if (Demo_IsProfiling())
444
    {
445
        int32_t dn=g_whichDemo-1;
446
        int32_t nt=g_prof.numtics, nf=g_prof.numframes;
447
        double gms=g_prof.totalgamems;
448
        double dms1=g_prof.totalroomsdrawms, dms2=g_prof.totalrestdrawms;
449
 
450
        ud.config.SoundToggle = g_demo_soundToggle;
451
 
452
        if (nt > 0)
453
        {
454
            OSD_Printf("== demo %d: %d gametics\n", dn, nt);
455
            OSD_Printf("== demo %d game times: %.03f ms (%.03f us/gametic)\n",
456
                       dn, gms, (gms*1000.0)/nt);
457
        }
458
 
459
        if (nf > 0)
460
        {
461
            OSD_Printf("== demo %d: %d frames (%d frames/gametic)\n", dn, nf, g_demo_profile-1);
462
            OSD_Printf("== demo %d drawrooms times: %.03f s (%.03f ms/frame)\n",
463
                       dn, dms1/1000.0, dms1/nf);
464
            OSD_Printf("== demo %d drawrest times: %.03f s (%.03f ms/frame)\n",
465
                       dn, dms2/1000.0, dms2/nf);
466
        }
467
 
468
        {
469
            double totalprofms = gms+dms1+dms2;
3935 helixhorne 470
            double totalms = gethiticks()-g_prof.starthiticks;
3024 helixhorne 471
            if (totalprofms != 0)
472
                OSD_Printf("== demo %d: non-profiled time overhead: %.02f %%\n",
473
                           dn, 100.0*totalms/totalprofms - 100.0);
3021 helixhorne 474
        }
475
    }
476
 
477
    g_demo_profile = 0;
478
    g_demo_stopProfile = 0;
479
}
480
////////////////////
481
 
1677 terminx 482
int32_t G_PlaybackDemo(void)
483
{
3001 helixhorne 484
    int32_t bigi, j, initsyncofs = 0, lastsyncofs = 0, lastsynctic = 0, lastsyncclock = 0;
1677 terminx 485
    int32_t foundemo = 0, corruptcode, outofsync=0;
486
    static int32_t in_menu = 0;
487
    //    static int32_t tmpdifftime=0;
488
 
2994 helixhorne 489
    if (ready2send)
490
        return 0;
1677 terminx 491
 
3024 helixhorne 492
    if (!g_demo_playFirstFlag)
493
        g_demo_profile = 0;
3021 helixhorne 494
 
1677 terminx 495
RECHECK:
3024 helixhorne 496
    if (g_demo_playFirstFlag)
497
        g_demo_playFirstFlag = 0;
498
    else if (g_demo_exitAfter)
499
        G_GameExit(" ");
1677 terminx 500
 
501
#if KRANDDEBUG
502
    if (foundemo)
503
        krd_print("krandplay.log");
504
#endif
505
 
506
    in_menu = g_player[myconnectindex].ps->gm&MODE_MENU;
507
 
508
    pub = NUMPAGES;
509
    pus = NUMPAGES;
510
 
511
    flushperms();
2994 helixhorne 512
 
513
    if (!g_netServer && ud.multimode < 2)
514
        foundemo = G_OpenDemoRead(g_whichDemo);
515
 
1677 terminx 516
    if (foundemo == 0)
517
    {
3006 helixhorne 518
        ud.recstat = 0;
519
 
1677 terminx 520
        if (g_whichDemo > 1)
521
        {
522
            g_whichDemo = 1;
523
            goto RECHECK;
524
        }
2994 helixhorne 525
 
1677 terminx 526
        fadepal(0,0,0, 0,63,7);
3025 helixhorne 527
        P_SetGamePalette(g_player[myconnectindex].ps, BASEPAL, 1);    // JBF 20040308
1677 terminx 528
        G_DrawBackground();
529
        M_DisplayMenus();
530
        //g_player[myconnectindex].ps->palette = palette;
531
        nextpage();
532
        fadepal(0,0,0, 63,0,-7);
533
        ud.reccnt = 0;
534
    }
535
    else
536
    {
537
        ud.recstat = 2;
3006 helixhorne 538
 
1677 terminx 539
        g_whichDemo++;
3045 helixhorne 540
        if (g_whichDemo == MAXDEMOS)
2994 helixhorne 541
            g_whichDemo = 1;
1677 terminx 542
 
543
        g_player[myconnectindex].ps->gm &= ~MODE_GAME;
544
        g_player[myconnectindex].ps->gm |= MODE_DEMO;
545
 
546
        lastsyncofs = ktell(g_demo_recFilePtr);
547
        initsyncofs = lastsyncofs;
548
        lastsynctic = g_demo_cnt;
549
        lastsyncclock = totalclock;
550
        outofsync = 0;
551
#if KRANDDEBUG
552
        krd_enable(2);
553
#endif
3021 helixhorne 554
        if (g_demo_profile < 0)
555
        {
556
            Demo_SetupProfile();
557
        }
1677 terminx 558
    }
559
 
2728 hendricks2 560
    if (foundemo == 0 || in_menu || I_CheckAllInput() || numplayers > 1)
1677 terminx 561
    {
562
        FX_StopAllSounds();
563
        S_ClearSoundLocks();
4738 hendricks2 564
        M_OpenMenu(myconnectindex);
1677 terminx 565
    }
566
 
567
    ready2send = 0;
568
    bigi = 0;
569
 
2728 hendricks2 570
    I_ClearAllInput();
1677 terminx 571
 
572
    //    OSD_Printf("ticcnt=%d, total=%d\n", g_demo_cnt, g_demo_totalCnt);
573
    while (g_demo_cnt < g_demo_totalCnt || foundemo==0)
574
    {
3001 helixhorne 575
        // Main loop here. It also runs when there's no demo to show,
576
        // so maybe a better name for this function would be
577
        // G_MainLoopWhenNotInGame()?
578
 
3006 helixhorne 579
        // Demo requested from the OSD, its name is in g_firstDemoFile[]
580
        if (g_demo_playFirstFlag)
581
        {
582
            g_demo_playFirstFlag = 0;
583
            g_whichDemo = 1;  // force g_firstDemoFile[]
3021 helixhorne 584
            g_demo_paused = 0;
3006 helixhorne 585
            goto nextdemo_nomenu;
586
        }
587
 
1677 terminx 588
        if (foundemo && (!g_demo_paused || g_demo_goalCnt))
589
        {
3001 helixhorne 590
            if (g_demo_goalCnt>0 && g_demo_goalCnt < g_demo_cnt)
1677 terminx 591
            {
3006 helixhorne 592
                // initialize rewind
3001 helixhorne 593
 
594
                int32_t menu = g_player[myconnectindex].ps->gm&MODE_MENU;
595
 
1677 terminx 596
                if (g_demo_goalCnt > lastsynctic)
597
                {
3006 helixhorne 598
                    // we can use a previous diff
2994 helixhorne 599
                    if (Demo_UpdateState(0)==0)
1677 terminx 600
                    {
601
                        g_demo_cnt = lastsynctic;
602
                        klseek(g_demo_recFilePtr, lastsyncofs, SEEK_SET);
603
                        ud.reccnt = 0;
604
 
605
                        totalclock = ototalclock = lockclock = lastsyncclock;
606
                    }
607
                    else CORRUPT(-1);
608
                }
609
                else
610
                {
3001 helixhorne 611
                    // update to initial state
612
                    if (Demo_UpdateState(1) == 0)
1677 terminx 613
                    {
614
                        klseek(g_demo_recFilePtr, initsyncofs, SEEK_SET);
615
                        g_levelTextTime = 0;
616
 
617
                        g_demo_cnt = 1;
618
                        ud.reccnt = 0;
619
 
620
                        //                        ud.god = ud.cashman = ud.eog = ud.showallmap = 0;
2454 helixhorne 621
                        //                        ud.noclip = ud.scrollmode = ud.overhead_on = ud.pause_on = 0;
1677 terminx 622
 
623
                        totalclock = ototalclock = lockclock = 0;
624
                    }
625
                    else CORRUPT(0);
626
                }
2994 helixhorne 627
 
3001 helixhorne 628
                Demo_RestoreModes(menu);
1677 terminx 629
            }
630
 
3021 helixhorne 631
            if (g_demo_stopProfile)
632
                Demo_FinishProfile();
633
 
1677 terminx 634
            while (totalclock >= (lockclock+TICSPERFRAME)
3007 helixhorne 635
                //                   || (ud.reccnt > REALGAMETICSPERSEC*2 && ud.pause_on)
1677 terminx 636
                || (g_demo_goalCnt>0 && g_demo_cnt<g_demo_goalCnt))
637
            {
638
                if (ud.reccnt<=0)
639
                {
3001 helixhorne 640
                    // Record count reached zero (or <0, corrupted), need
641
                    // reading another chunk.
642
 
1677 terminx 643
                    char tmpbuf[4];
644
 
645
                    if (ud.reccnt<0)
646
                    {
647
                        OSD_Printf("G_PlaybackDemo: ud.reccnt<0!\n");
648
                        CORRUPT(1);
649
                    }
650
 
651
                    bigi = 0;
652
                    //reread:
2994 helixhorne 653
                    if (kread(g_demo_recFilePtr, tmpbuf, 4) != 4)
654
                        CORRUPT(2);
1677 terminx 655
 
656
                    if (Bmemcmp(tmpbuf, "sYnC", 4)==0)
2994 helixhorne 657
                    {
658
                        int32_t err = Demo_ReadSync(3);
659
                        if (err)
660
                            CORRUPT(err);
661
                    }
662
 
1677 terminx 663
                    else if (demo_hasdiffs && Bmemcmp(tmpbuf, "dIfF", 4)==0)
664
                    {
3001 helixhorne 665
                        int32_t k = sv_readdiff(g_demo_recFilePtr);
666
 
1677 terminx 667
                        if (k)
668
                        {
669
                            OSD_Printf("sv_readdiff() returned %d.\n", k);
670
                            CORRUPT(6);
671
                        }
672
                        else
673
                        {
674
                            lastsyncofs = ktell(g_demo_recFilePtr);
675
                            lastsynctic = g_demo_cnt;
676
                            lastsyncclock = totalclock;
677
 
2994 helixhorne 678
                            if (kread(g_demo_recFilePtr, tmpbuf, 4) != 4)
679
                                CORRUPT(7);
680
                            if (Bmemcmp(tmpbuf, "sYnC", 4))
681
                                CORRUPT(8);
682
 
683
                            {
684
                                int32_t err = Demo_ReadSync(9);
685
                                if (err)
686
                                    CORRUPT(err);
687
                            }
688
 
1677 terminx 689
                            if ((g_demo_goalCnt==0 && demoplay_diffs) ||
690
                                (g_demo_goalCnt>0 && ud.reccnt/ud.multimode >= g_demo_goalCnt-g_demo_cnt))
691
                            {
2994 helixhorne 692
                                Demo_UpdateState(0);
1677 terminx 693
                            }
694
                        }
695
                    }
696
                    else if (Bmemcmp(tmpbuf, "EnD!", 4)==0)
697
                        goto nextdemo;
698
                    else CORRUPT(12);
699
 
700
                    if (0)
701
                    {
702
corrupt:
703
                        OSD_Printf(OSD_ERROR "Demo %d is corrupt (code %d).\n", g_whichDemo-1, corruptcode);
704
nextdemo:
4738 hendricks2 705
                        M_OpenMenu(myconnectindex);
3006 helixhorne 706
nextdemo_nomenu:
1677 terminx 707
                        foundemo = 0;
708
                        ud.reccnt = 0;
2207 helixhorne 709
                        kclose(g_demo_recFilePtr); g_demo_recFilePtr = -1;
3001 helixhorne 710
 
1677 terminx 711
                        if (g_demo_goalCnt>0)
712
                        {
713
                            g_demo_goalCnt=0;
714
                            ud.config.SoundToggle = g_demo_soundToggle;
715
                        }
3001 helixhorne 716
 
3021 helixhorne 717
                        if (Demo_IsProfiling())  // don't reset g_demo_profile if it's < 0
718
                            Demo_FinishProfile();
1677 terminx 719
                        goto RECHECK;
720
                    }
721
                }
722
 
723
                if (demo_hasseeds)
2994 helixhorne 724
                    outofsync = ((uint8_t)(randomseed>>24) != g_demo_seedbuf[bigi]);
1677 terminx 725
 
2379 helixhorne 726
                for (TRAVERSE_CONNECT(j))
1677 terminx 727
                {
2207 helixhorne 728
                    Bmemcpy(&inputfifo[0][j], &recsync[bigi], sizeof(input_t));
1677 terminx 729
                    bigi++;
730
                    ud.reccnt--;
731
                }
3001 helixhorne 732
 
1677 terminx 733
                g_demo_cnt++;
734
 
3021 helixhorne 735
                if (Demo_IsProfiling())
1677 terminx 736
                {
3935 helixhorne 737
                    double t = gethiticks();
3021 helixhorne 738
                    G_DoMoveThings();
739
                    Demo_GToc(t);
740
                }
741
                else if (!g_demo_paused)
742
                {
1677 terminx 743
                    // assumption that ud.multimode doesn't change in a demo may not be true
744
                    // sometime in the future                    v v v v v v v v v
745
                    if (g_demo_goalCnt==0 || !demo_hasdiffs || ud.reccnt/ud.multimode>=g_demo_goalCnt-g_demo_cnt)
3021 helixhorne 746
                    {
1677 terminx 747
                        G_DoMoveThings();  // increases lockclock by TICSPERFRAME
3021 helixhorne 748
                    }
1677 terminx 749
                    else
3021 helixhorne 750
                    {
1677 terminx 751
                        lockclock += TICSPERFRAME;
3021 helixhorne 752
                    }
1677 terminx 753
                }
754
                else
755
                {
3001 helixhorne 756
                    int32_t k = ud.config.SoundToggle;
1677 terminx 757
                    ud.config.SoundToggle = 0;
758
                    G_DoMoveThings();
759
                    ud.config.SoundToggle = k;
760
                }
761
 
762
                ototalclock += TICSPERFRAME;
763
 
764
                if (g_demo_goalCnt > 0)
765
                {
3001 helixhorne 766
                    // if fast-forwarding, we must update totalclock
1677 terminx 767
                    totalclock += TICSPERFRAME;
768
 
3001 helixhorne 769
//                    OSD_Printf("t:%d, l+T:%d; cnt:%d, goal:%d%s", totalclock, (lockclock+TICSPERFRAME),
770
//                               g_demo_cnt, g_demo_goalCnt, g_demo_cnt>=g_demo_goalCnt?" ":"\n");
1677 terminx 771
                    if (g_demo_cnt>=g_demo_goalCnt)
772
                    {
773
                        g_demo_goalCnt = 0;
774
                        ud.config.SoundToggle = g_demo_soundToggle;
775
                    }
776
                }
777
            }
778
        }
779
        else if (foundemo && g_demo_paused)
780
        {
781
            totalclock = lockclock;
782
        }
783
 
3021 helixhorne 784
        if (Demo_IsProfiling())
785
            totalclock += TICSPERFRAME;
786
 
3001 helixhorne 787
        if (foundemo == 0)
2592 helixhorne 788
        {
1677 terminx 789
            G_DrawBackground();
4815 helixhorne 790
#ifdef LUNATIC
791
            El_DisplayErrors();
792
#endif
2592 helixhorne 793
        }
1677 terminx 794
        else
795
        {
2326 helixhorne 796
            static uint32_t nextrender = 0, framewaiting = 0;
797
            uint32_t tt;
798
 
3021 helixhorne 799
            // NOTE: currently, no key/mouse events will be seen while
800
            // demo-profiling because we need 'totalclock' for ourselves.
801
            // And handleevents() -> sampletimer() would mess that up.
1677 terminx 802
            G_HandleLocalKeys();
803
 
2326 helixhorne 804
            if (framewaiting)
805
            {
806
                framewaiting--;
807
                nextpage();
808
            }
1677 terminx 809
 
2326 helixhorne 810
            tt = getticks();
1677 terminx 811
 
3021 helixhorne 812
            // Render one frame (potentially many if profiling)
813
            if (Demo_IsProfiling())
2326 helixhorne 814
            {
3021 helixhorne 815
                int32_t i, num = g_demo_profile-1;
816
 
817
                Bassert(totalclock-ototalclock==4);
818
 
819
                for (i=0; i<num; i++)
820
                {
3935 helixhorne 821
                    double t1 = gethiticks(), t2;
3021 helixhorne 822
 
823
//                    initprintf("t=%d, o=%d, t-o = %d\n", totalclock,
824
//                               ototalclock, totalclock-ototalclock);
825
 
826
                    // NOTE: G_DrawRooms() calculates smoothratio inside and
827
                    // ignores the function argument, so we set totalclock
828
                    // accordingly.
829
                    j = (i<<16)/num;
830
                    totalclock = ototalclock + (j>>16);
831
 
832
                    G_DrawRooms(screenpeek,j);
833
 
3935 helixhorne 834
                    t2 = gethiticks();
3021 helixhorne 835
 
836
                    G_DisplayRest(j);
837
 
838
                    Demo_RToc(t1, t2);
839
                }
840
 
841
                totalclock = ototalclock+4;
842
 
843
                // draw status
844
                Demo_DisplayProfStatus();
3022 helixhorne 845
 
846
                if (handleevents_peekkeys())
847
                    Demo_StopProfiling();
3021 helixhorne 848
            }
849
            else if (r_maxfps == 0 || tt >= nextrender)
850
            {
2326 helixhorne 851
                if (tt > nextrender+g_frameDelay)
852
                    nextrender = tt;
853
 
854
                nextrender += g_frameDelay;
855
 
3007 helixhorne 856
                j = calc_smoothratio(totalclock, ototalclock);
2326 helixhorne 857
                if (g_demo_paused && g_demo_rewind)
858
                    j = 65536-j;
859
 
860
                G_DrawRooms(screenpeek,j);
861
                G_DisplayRest(j);
862
 
863
                framewaiting++;
864
            }
865
 
3021 helixhorne 866
            if (!Demo_IsProfiling() && (g_player[myconnectindex].ps->gm&MODE_MENU) == 0)
1677 terminx 867
            {
868
                if (demoplay_showsync && outofsync)
869
                    gametext(160,100,"OUT OF SYNC",0,2+8+16);
870
 
871
                if (g_demo_showStats)
872
                {
3001 helixhorne 873
#if 0
874
                    if (g_demo_cnt<tmpdifftime)
875
                        gametext(160,100,"DIFF",0,2+8+16);
1677 terminx 876
 
3001 helixhorne 877
                    {
878
                        char buf[32];
879
                        Bsprintf(buf, "RC:%4d  TC:%5d", ud.reccnt, g_demo_cnt);
880
                        gametext(160,100,buf,0,2+8+16);
881
                    }
882
#endif
3007 helixhorne 883
                    j=g_demo_cnt/REALGAMETICSPERSEC;
1677 terminx 884
                    Bsprintf(buf, "%02d:%02d", j/60, j%60);
2304 helixhorne 885
                    gametext(18,16,buf,0,2+8+16+1024);
1677 terminx 886
 
2304 helixhorne 887
                    rotatesprite(60<<16,16<<16,32768,0,SLIDEBAR,0,0,2+8+16+1024,0,0,(xdim*95)/320,ydim-1);
888
                    rotatesprite(90<<16,16<<16,32768,0,SLIDEBAR,0,0,2+8+16+1024,(xdim*95)/320,0,(xdim*125)/320,ydim-1);
889
                    rotatesprite(120<<16,16<<16,32768,0,SLIDEBAR,0,0,2+8+16+1024,(xdim*125)/320,0,(xdim*155)/320,ydim-1);
890
                    rotatesprite(150<<16,16<<16,32768,0,SLIDEBAR,0,0,2+8+16+1024,(xdim*155)/320,0,xdim-1,ydim-1);
1677 terminx 891
 
4658 terminx 892
                    j = (182<<16) - (tabledivide32_noinline((120*(g_demo_totalCnt-g_demo_cnt))<<4, g_demo_totalCnt)<<12);
2308 helixhorne 893
                    rotatesprite_fs(j,(16<<16)+(1<<15),32768,0,SLIDEBAR+1,0,0,2+8+16+1024);
1677 terminx 894
 
3007 helixhorne 895
                    j=(g_demo_totalCnt-g_demo_cnt)/REALGAMETICSPERSEC;
1677 terminx 896
                    Bsprintf(buf, "-%02d:%02d%s", j/60, j%60, g_demo_paused?"   ^15PAUSED":"");
2304 helixhorne 897
                    gametext(194,16,buf,0,2+8+16+1024);
1677 terminx 898
                }
899
            }
900
 
901
            if ((g_netServer || ud.multimode > 1) && g_player[myconnectindex].ps->gm)
902
                Net_GetPackets();
903
 
904
            if (g_player[myconnectindex].gotvote == 0 && voting != -1 && voting != myconnectindex)
2540 hendricks2 905
                gametext(160,60,"Press F1 to Accept, F2 to Decline",0,2+8+16);
1677 terminx 906
        }
907
 
908
        if ((g_player[myconnectindex].ps->gm&MODE_MENU) && (g_player[myconnectindex].ps->gm&MODE_EOL))
3021 helixhorne 909
        {
910
            Demo_FinishProfile();
1677 terminx 911
            goto RECHECK;
3021 helixhorne 912
        }
1677 terminx 913
 
2728 hendricks2 914
        if (I_EscapeTrigger() && (g_player[myconnectindex].ps->gm&MODE_MENU) == 0 && (g_player[myconnectindex].ps->gm&MODE_TYPE) == 0)
1677 terminx 915
        {
2728 hendricks2 916
            I_EscapeTriggerClear();
1677 terminx 917
            FX_StopAllSounds();
918
            S_ClearSoundLocks();
4738 hendricks2 919
            M_OpenMenu(myconnectindex);
3084 terminx 920
            M_ChangeMenu(MENU_MAIN);
1677 terminx 921
            S_MenuSound();
922
        }
923
 
3021 helixhorne 924
        if (Demo_IsProfiling())
1677 terminx 925
        {
3021 helixhorne 926
            // Do nothing: sampletimer() is reached from M_DisplayMenus() ->
927
            // Net_GetPackets() else.
928
        }
929
        else if (g_player[myconnectindex].ps->gm&MODE_TYPE)
930
        {
3095 terminx 931
            Net_SendMessage();
3006 helixhorne 932
 
1677 terminx 933
            if ((g_player[myconnectindex].ps->gm&MODE_TYPE) != MODE_TYPE)
4738 hendricks2 934
            {
935
                g_player[myconnectindex].ps->gm = 0;
936
                M_OpenMenu(myconnectindex);
937
            }
1677 terminx 938
        }
939
        else
940
        {
941
            if (ud.recstat != 2)
942
                M_DisplayMenus();
3006 helixhorne 943
 
4496 hendricks2 944
            if ((g_netServer || ud.multimode > 1)  && !M_IsTextInput(m_currentMenu))
1677 terminx 945
            {
946
                ControlInfo noshareinfo;
947
                CONTROL_GetInput(&noshareinfo);
948
                if (BUTTON(gamefunc_SendMessage))
949
                {
950
                    KB_FlushKeyboardQueue();
951
                    CONTROL_ClearButton(gamefunc_SendMessage);
952
                    g_player[myconnectindex].ps->gm = MODE_TYPE;
953
                    typebuf[0] = 0;
954
                }
955
            }
956
        }
957
 
3021 helixhorne 958
        if (!Demo_IsProfiling())
959
            G_PrintGameQuotes(screenpeek);
1677 terminx 960
 
961
        if (ud.last_camsprite != ud.camerasprite)
962
            ud.last_camsprite = ud.camerasprite;
963
 
964
        if (VOLUMEONE)
965
        {
966
            if (ud.show_help == 0 && (g_player[myconnectindex].ps->gm&MODE_MENU) == 0)
2308 helixhorne 967
                rotatesprite_fs((320-50)<<16,9<<16,65536L,0,BETAVERSION,0,0,2+8+16+128);
1677 terminx 968
        }
2994 helixhorne 969
 
3021 helixhorne 970
        // NOTE: We must prevent handleevents() and Net_GetPackets() from
971
        // updating totalclock when profiling (both via sampletimer()):
972
        if (!Demo_IsProfiling())
973
            G_HandleAsync();
2994 helixhorne 974
 
3006 helixhorne 975
        if (ud.recstat==0)
2332 helixhorne 976
            nextpage();
1677 terminx 977
 
978
        if (g_player[myconnectindex].ps->gm == MODE_GAME)
979
        {
3001 helixhorne 980
            // user wants to play a game, quit showing demo!
981
 
1677 terminx 982
            if (foundemo)
983
            {
984
#if KRANDDEBUG
985
                krd_print("krandplay.log");
986
#endif
2207 helixhorne 987
                kclose(g_demo_recFilePtr); g_demo_recFilePtr = -1;
1677 terminx 988
            }
3001 helixhorne 989
 
1677 terminx 990
            return 0;
991
        }
992
    }
2994 helixhorne 993
 
1677 terminx 994
    ud.multimode = numplayers;  // fixes 2 infinite loops after watching demo
2207 helixhorne 995
    kclose(g_demo_recFilePtr); g_demo_recFilePtr = -1;
1677 terminx 996
 
3021 helixhorne 997
    Demo_FinishProfile();
998
 
3001 helixhorne 999
    // if we're in the menu, try next demo immediately
1000
    if (g_player[myconnectindex].ps->gm&MODE_MENU)
1001
        goto RECHECK;
1002
 
1677 terminx 1003
#if KRANDDEBUG
1004
    if (foundemo)
1005
        krd_print("krandplay.log");
1006
#endif
3001 helixhorne 1007
 
1008
    // finished playing a demo and not in menu:
1009
    // return so that e.g. the title can be shown
1677 terminx 1010
    return 1;
1011
}