1 /*
2  * GNUjump
3  * =======
4  *
5  * Copyright (C) 2005-2008, Juan Pedro Bolivar Puente
6  *
7  * GNUjump is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GNUjump is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * 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, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "gnujump.h"
22 #include "replay.h"
23 #include "game.h"
24 #include "tools.h"
25 #include "effects.h"
26 
27 extern L_gblOptions gblOps;
28 
29 const int REPFPS[NREPSPEEDS] = { 5, 10, 20, 40, 80, 160, 320, 640 };
30 
31 void
repPushUInt32(replay_t * rep,Uint32 data)32 repPushUInt32 (replay_t * rep, Uint32 data)
33 {
34   *(Uint32 *) (rep->buf) = data;
35   rep->buf += sizeof (Uint32);
36 }
37 
38 void
repPushUInt16(replay_t * rep,Uint16 data)39 repPushUInt16 (replay_t * rep, Uint16 data)
40 {
41   *(Uint16 *) (rep->buf) = data;
42   rep->buf += sizeof (Uint16);
43 }
44 
45 void
repPushUInt8(replay_t * rep,Uint8 data)46 repPushUInt8 (replay_t * rep, Uint8 data)
47 {
48   *(Uint8 *) (rep->buf) = data;
49   rep->buf += sizeof (Uint8);
50 }
51 
52 Uint32
repGetUInt32(replay_t * rep)53 repGetUInt32 (replay_t * rep)
54 {
55   rep->buf += sizeof (Uint32);
56   return *(Uint32 *) (rep->buf - sizeof (Uint32));
57 }
58 
59 Uint16
repGetUInt16(replay_t * rep)60 repGetUInt16 (replay_t * rep)
61 {
62   rep->buf += sizeof (Uint16);
63   return *(Uint16 *) (rep->buf - sizeof (Uint16));
64 }
65 
66 Uint8
repGetUInt8(replay_t * rep)67 repGetUInt8 (replay_t * rep)
68 {
69   rep->buf += sizeof (Uint8);
70   return *(Uint8 *) (rep->buf - sizeof (Uint8));
71 }
72 
73 void
endReplay(game_t * game,int totalms)74 endReplay (game_t * game, int totalms)
75 {
76   int i;
77 
78   updateReplay (game, 0);
79   game->replay.bodysize = game->replay.buf - game->replay.bst;
80   game->replay.totalms = totalms;
81   game->replay.record = 0;
82 
83   for (i = 0; i < game->numHeros; i++)
84     {
85       if (game->heros[i].floor > game->replay.record)
86 	game->replay.record = game->heros[i].floor;
87     }
88 
89   if (game->numHeros > 1)
90     game->heros[i].floor /= gblOps.mpLives;
91 }
92 
93 void
initReplay(game_t * game)94 initReplay (game_t * game)
95 {
96   int i;
97 
98   game->replay.timer = 0;
99   game->replay.scrolls = 0;
100   game->replay.sounds = 0;
101   game->replay.bufsize = REP_BUFFER_SIZE;
102   game->replay.bst = malloc (game->replay.bufsize);
103   game->replay.buf = game->replay.bst;
104   game->replay.nframes = 0;
105   game->replay.fps = gblOps.repFps;
106   game->replay.mspf = 1000.0 / gblOps.repFps;
107 
108   for (i = 0; i < game->numHeros; i++)
109     game->replay.deadHero[i] = 0;
110 
111   for (i = 0; i < GRIDHEIGHT; i++)
112     {
113       repPushUInt8 (&(game->replay), game->floor_l[i]);
114       repPushUInt8 (&(game->replay), game->floor_r[i]);
115     }
116   repPushUInt8 (&(game->replay), game->numHeros);
117   repPushHeros (game);
118 }
119 
120 void
updateReplay(game_t * game,float ms)121 updateReplay (game_t * game, float ms)
122 {
123   int d = game->replay.buf - game->replay.bst;
124 
125   if (game->replay.bufsize - d < REP_MIN_FREE)
126     {
127       game->replay.bufsize += REP_BUFFER_SIZE;
128       game->replay.bst = realloc (game->replay.bst, game->replay.bufsize);
129       game->replay.buf = game->replay.bst + d;
130     }
131 
132   game->replay.timer += ms;
133 
134   if (game->replay.timer >= game->replay.mspf)
135     {
136       game->replay.timer -= game->replay.mspf;
137       game->replay.nframes++;
138       repPushUInt8 (&(game->replay), game->replay.sounds);
139       game->replay.sounds = 0;
140       repPushGrid (game);
141       repPushHeros (game);
142     }
143 }
144 
145 void
repPushGrid(game_t * game)146 repPushGrid (game_t * game)
147 {
148   int i, j;
149 
150   repPushUInt8 (&(game->replay), game->scrollCount);
151   repPushUInt8 (&(game->replay), game->replay.scrolls);
152   for (i = 0, j = game->mapIndex;
153        i < game->replay.scrolls; i++, j = (j + 1) % GRIDHEIGHT)
154     {
155       repPushUInt8 (&(game->replay), game->floor_l[j]);
156       repPushUInt8 (&(game->replay), game->floor_r[j]);
157     }
158 
159   game->replay.scrolls = 0;
160 }
161 
162 void
repPushHeros(game_t * game)163 repPushHeros (game_t * game)
164 {
165   int i;
166   for (i = 0; i < game->numHeros; i++)
167     {
168       if (!game->replay.deadHero[i])
169 	{
170 	  repPushUInt16 (&(game->replay), game->heros[i].x);
171 	  repPushUInt16 (&(game->replay), game->heros[i].y);
172 	  repPushUInt8 (&(game->replay), game->heros[i].dir);
173 	  repPushUInt16 (&(game->replay), game->heros[i].angle);
174 	  repPushUInt8 (&(game->replay), game->heros[i].id);
175 	  repPushUInt8 (&(game->replay), game->heros[i].dead);
176 	  repPushUInt8 (&(game->replay), game->heros[i].lives);
177 	  repPushUInt16 (&(game->replay), game->heros[i].floor);
178 
179 	  game->replay.deadHero[i] = game->heros[i].dead;
180 	}
181     }
182 }
183 
184 void
freeReplay(replay_t * rep)185 freeReplay (replay_t * rep)
186 {
187   free (rep->bst);
188 }
189 
190 void
getPlayerReplay(hero_t * hero,replay_t * rep)191 getPlayerReplay (hero_t * hero, replay_t * rep)
192 {
193   int newid;
194 
195   hero->x = repGetUInt16 (rep);
196   hero->y = repGetUInt16 (rep);
197   hero->dir = repGetUInt8 (rep);
198   hero->angle = repGetUInt16 (rep);
199   newid = repGetUInt8 (rep);
200   hero->previd = hero->id;
201   if (newid != hero->id)
202     {
203       hero->sprite[newid].elpTime = hero->sprite[newid].frame = 0;
204     }
205   hero->id = newid;
206   hero->dead = repGetUInt8 (rep);
207   hero->prevLives = hero->lives;
208   hero->lives = repGetUInt8 (rep);
209   if (hero->lives > 4)
210     hero->lives = -1;
211   hero->prevFloor = hero->floor;
212   hero->floor = repGetUInt16 (rep);
213 
214   if (gblOps.trailMode != NOTRAIL)
215     updateTrails (hero, RECMS);
216   if (gblOps.useGL && gblOps.blur)
217     updateBlurs (hero, RECMS);
218 }
219 
220 void
initGameReplay(game_t * game,data_t * gfx,replay_t * rep)221 initGameReplay (game_t * game, data_t * gfx, replay_t * rep)
222 {
223   int i, j;
224 
225   rep->buf = rep->bst;
226   rep->speed = REP_1X;
227 
228   game->floorTop = GRIDHEIGHT - 4;
229   game->mapIndex = 0;
230   game->scrollCount = 0;
231   game->scrollTotal = 0;
232 
233   for (i = 0; i < GRIDHEIGHT; i++)
234     {
235       game->floor_l[i] = repGetUInt8 (rep);
236       game->floor_r[i] = repGetUInt8 (rep);
237     }
238   game->numHeros = repGetUInt8 (rep);
239   game->heros = malloc (sizeof (hero_t) * game->numHeros);
240   for (i = 0; i < game->numHeros; i++)
241     {
242       for (j = 0; j < HEROANIMS; j++)
243 	{
244 	  initializeSpriteCtlRot (&game->heros[i].sprite[j],
245 				  gfx->heroSprite[i][j]);
246 	}
247       game->heros[i].trail = NULL;
248       game->heros[i].blur = NULL;
249       getPlayerReplay (&(game->heros[i]), rep);
250       game->heros[i].prevLives = -1;
251     }
252 }
253 
254 void
scrollReplay(game_t * game,data_t * gfx,replay_t * rep)255 scrollReplay (game_t * game, data_t * gfx, replay_t * rep)
256 {
257   int scrolls;
258   int i, j;
259   int ds;
260 
261   ds = game->scrollCount;
262   game->scrollCount = repGetUInt8 (rep);
263   ds = game->scrollCount - ds;
264 
265   scrolls = repGetUInt8 (rep);
266   if (scrolls > 0)
267     ds += BLOCKSIZE * (scrolls);
268 
269   game->mapIndex -= scrolls;
270   game->floorTop += scrolls;
271 
272   game->scrollTotal += ds;
273 
274   if (game->mapIndex < 0)
275     game->mapIndex += GRIDHEIGHT;
276   for (i = game->mapIndex, j = 0; j < scrolls; j++, i = (i + 1) % GRIDHEIGHT)
277     {
278       game->floor_l[i] = repGetUInt8 (rep);
279       game->floor_r[i] = repGetUInt8 (rep);
280     }
281 
282   for (i = 0; i < game->numHeros; i++)
283     {
284       scrollTrails (&(game->heros[i]), ds);
285       scrollBlurs (&(game->heros[i]), ds);
286     }
287 }
288 
289 void
playRepSounds(data_t * gfx,replay_t * rep,int mute)290 playRepSounds (data_t * gfx, replay_t * rep, int mute)
291 {
292   int sounds = repGetUInt8 (rep);
293   if (!mute)
294     {
295       if ((sounds & S_JUMP) == S_JUMP && gfx->gjump)
296 	Mix_PlayChannel (-1, gfx->gjump, 0);
297       if ((sounds & S_FALL) == S_FALL && gfx->gfall)
298 	Mix_PlayChannel (-1, gfx->gfall, 0);
299       if ((sounds & S_DIE) == S_DIE   && gfx->gdie)
300 	Mix_PlayChannel (-1, gfx->gdie, 0);
301     }
302 }
303 
304 void
drawRepHud(data_t * gfx,replay_t * rep)305 drawRepHud (data_t * gfx, replay_t * rep)
306 {
307   char *str;
308   str = malloc (sizeof (char) * strlen (_("Speed: ")) + 8);
309 
310   strcpy (str, _(" Speed: "));
311   switch (rep->speed)
312     {
313     case REP_OX:
314       strcat (str, "1/8 X ");
315       break;
316     case REP_QX:
317       strcat (str, "1/4 X ");
318       break;
319     case REP_HX:
320       strcat (str, "1/2 X ");
321       break;
322     case REP_1X:
323       strcat (str, "  1 X ");
324       break;
325     case REP_2X:
326       strcat (str, "  2 X ");
327       break;
328     case REP_4X:
329       strcat (str, "  4 X ");
330       break;
331     case REP_8X:
332       strcat (str, "  8 X ");
333       break;
334     case REP_16X:
335       strcat (str, " 16 X ");
336       break;
337     default:
338       break;
339     }
340 
341   JPB_drawSquare (gfx->gcolor, gfx->galpha,
342 		  gfx->gameX + BLOCKSIZE,
343 		  gfx->gameY,
344 		  SFont_TextWidth (gfx->textfont, str),
345 		  SFont_TextHeight (gfx->textfont));
346 
347   SFont_Write (gfx->textfont, gfx->gameX + BLOCKSIZE, gfx->gameY, str);
348 
349   free (str);
350 }
351 
352 void
updateGameReplay(game_t * game,data_t * gfx,replay_t * rep,float ms,int mute)353 updateGameReplay (game_t * game, data_t * gfx, replay_t * rep, float ms,
354 		  int mute)
355 {
356   int i;
357 
358   playRepSounds (gfx, rep, mute);
359   scrollReplay (game, gfx, rep);
360   for (i = 0; i < game->numHeros; i++)
361     {
362       if (game->heros[i].dead == FALSE)
363 	{
364 	  getPlayerReplay (&(game->heros[i]), rep);
365 	  animateSpriteRot (&(game->heros[i].sprite[game->heros[i].id]), ms);
366 	}
367     }
368 }
369 
370 int
updateInputReplay(replay_t * rep,L_timer * time)371 updateInputReplay (replay_t * rep, L_timer * time)
372 {
373   int ret = 0;
374   SDL_Event event;
375 
376   while (SDL_PollEvent (&event))
377     {
378       switch (event.type)
379 	{
380 	  /* A key is pressed */
381 	case SDL_KEYDOWN:
382 	  if (event.key.keysym.sym == SDLK_RIGHT)
383 	    {
384 	      rep->speed++;
385 	      if (rep->speed >= NREPSPEEDS)
386 		rep->speed--;
387 	      setFpsTimer (time, REPFPS[rep->speed]);
388 	    }
389 	  if (event.key.keysym.sym == SDLK_LEFT)
390 	    {
391 	      rep->speed--;
392 	      if (rep->speed < 0)
393 		rep->speed++;
394 	      setFpsTimer (time, REPFPS[rep->speed]);
395 	    }
396 	  if (event.key.keysym.sym == KEY_QUIT)
397 	    {
398 	      ret = TRUE;
399 	    }
400 	  if (event.key.keysym.sym == SDLK_p
401 	      || event.key.keysym.sym == SDLK_PAUSE)
402 	    {
403 	      ret = PAUSED;
404 	    }
405 	  break;
406 	  /* Quit: */
407 	case SDL_QUIT:
408 	  ret = TRUE;
409 	  break;
410 	  /* Default */
411 	default:
412 	  break;
413 	}
414     }
415 
416   return ret;
417 }
418 
419 int
playReplay(data_t * gfx,replay_t * rep)420 playReplay (data_t * gfx, replay_t * rep)
421 {
422   L_timer timer;
423   game_t game;
424   int done = FALSE;
425   int r, i = 0, j;
426   /* int timegap; */
427   int skipf = 0;
428   int lskipf = 0;
429   /*int lskipf2 = 0; */
430   Uint32 mymscount = 0;
431   float tdelay, adelay, ladelay = 0;
432 
433   if (gfx->musgame)
434     Mix_PlayMusic (gfx->musgame, -1);
435 
436   drawBg (gfx->gameBg, 0, 0, gblOps.w, gblOps.h);
437   initGameReplay (&game, gfx, rep);
438   initTimer (&timer, rep->fps);
439   FlipScreen ();
440 
441   updateTimer (&timer);
442   while (!done)
443     {
444       if ((r = updateInputReplay (rep, &timer)))
445 	{
446 	  if (r == PAUSED)
447 	    {
448 	      done = pauseGame (gfx, &game, _("PAUSE"));
449 	    }
450 	  else
451 	    {
452 	      done =
453 		yesNoQuestion (gfx, &game,
454 			       _
455 			       ("Are you sure you want to stop playing this replay? (Y/n)"));
456 	    }
457 	  continueTimer (&timer);
458 	}
459       /*timegap = */
460       updateTimer (&timer);
461 
462       tdelay = (1000.0 / (float) REPFPS[rep->speed]);
463       adelay = (float) timer.ms / (skipf + 1);
464       if (lskipf <= skipf && ladelay < adelay)
465 	skipf--;
466       if (tdelay < adelay)
467 	skipf++;
468       else
469 	skipf--;
470 
471       if (skipf < 0)
472 	skipf = 0;
473       if (skipf > REPFPS[rep->speed] / REPFPS[REP_1X])
474 	skipf = REPFPS[rep->speed] / REPFPS[REP_1X];
475       ladelay = adelay;
476       lskipf = skipf;
477       /* Cutremode on */
478       //skipf = ceil((float)timegap/(1000.0/(float)REPFPS[rep->speed]));
479 
480       updateGameReplay (&game, gfx, rep, timer.ms, 0);
481 
482       /* Be careful when it takes longer than what we want to reach... */
483       /*if (lskipf < skipf && lskipf2 < lskipf) {
484          skipf = lskipf;//-1; if (skipf < 0) skipf = 0;
485          } else {
486          lskipf2 = lskipf;
487          lskipf = skipf;
488          } */
489 
490       //printf("skipping %d frames | %f, %f\n", skipf, adelay, tdelay);
491       mymscount += (skipf + 1) * (1000.0 / (float) REPFPS[REP_1X]);
492       for (j = 0; j < skipf && i < rep->nframes; i++, j++)
493 	{
494 	  updateGameReplay (&game, gfx, rep, 0, 1);
495 	}
496 
497       drawGame (gfx, &game);
498       drawRepHud (gfx, rep);
499       updateScore (gfx, &game, mymscount);
500       FlipScreen ();
501 
502       if (++i >= rep->nframes)
503 	done = ENDMATCH;
504     }
505   if (done == ENDMATCH)
506     {
507       r =
508 	yesNoQuestion (gfx, &game,
509 		       _("Do you want to watch this replay again? (Y/n)"));
510     }
511   else
512     {
513       r = FALSE;
514     }
515 
516   freeGameReplay (&game);
517 
518   return r;
519 }
520 
521 void
freeGameReplay(game_t * game)522 freeGameReplay (game_t * game)
523 {
524   int i;
525   for (i = 0; i < game->numHeros; i++)
526     free (game->heros[i].trail);
527   free (game->heros);
528 }
529 
530 void
fputUint32(Uint32 data,FILE * fh)531 fputUint32 (Uint32 data, FILE * fh)
532 {
533   fputc (data >> 24, fh);
534   fputc ((data << 8) >> 24, fh);
535   fputc ((data << 16) >> 24, fh);
536   fputc ((data << 24) >> 24, fh);
537 }
538 
539 int
saveReplay(replay_t * rep,char * fname,char * comment)540 saveReplay (replay_t * rep, char *fname, char *comment)
541 {
542   char *fullname = NULL;
543   char buf[64];
544   time_t timt;
545   struct tm *tims;
546   FILE *fh = NULL;
547   int i;
548 
549   fullname =
550     malloc (sizeof (char *) *
551 	    (strlen (fname) + strlen (gblOps.repDir) + strlen (REPEXT) + 2));
552   sprintf (fullname, "%s/%s%s", gblOps.repDir, fname, REPEXT);
553 
554   if ((fh = fopen (fullname, "wb")) == NULL)
555     {
556       fprintf (stderr, _("ERROR: Could not save replay to %s\n"), fullname);
557       free (fullname);
558       return FALSE;
559     }
560 
561   timt = time (0);
562   tims = localtime (&timt);
563 
564   /* Header */
565   fputUint32 (REP_VERS, fh);
566 
567   /* Comment */
568   fputs (comment, fh);
569   sprintf (buf, "Floor: %d", rep->record);
570   fputs (" | ", fh);
571   fputs (buf, fh);
572   sprintf (buf, "Time: %d", rep->totalms / 1000);
573   fputs (" | ", fh);
574   fputs (buf, fh);
575   strftime (buf, 64, "%H:%M %d/%m/%y", tims);
576   fputs (" | ", fh);
577   fputs (buf, fh);
578 
579   fputc ('\0', fh);
580 
581   /* The rest of the header */
582   fputUint32 (rep->record, fh);
583   fputUint32 (rep->totalms, fh);
584   fputUint32 (rep->fps, fh);
585   fputUint32 (rep->nframes, fh);
586 
587   /* Body */
588   for (i = 0; i < rep->bodysize; i++)
589     {
590       fputc (((char *) (rep->bst))[i], fh);
591     }
592 
593   free (fullname);
594 
595   fclose (fh);
596 
597   return TRUE;
598 }
599 
600 Uint32
fgetUint32(FILE * fh)601 fgetUint32 (FILE * fh)
602 {
603   Uint32 data = 0;
604   data |= fgetc (fh) << 24;
605   data |= fgetc (fh) << 16;
606   data |= fgetc (fh) << 8;
607   data |= fgetc (fh);
608   return data;
609 }
610 
611 int
loadReplay(data_t * gfx,char * file)612 loadReplay (data_t * gfx, char *file)
613 {
614   FILE *fh = NULL;
615   int version;
616   int size;
617   replay_t rep;
618   char *comment;
619   int i;
620 
621   size = getFileSize (file);
622 
623   if ((fh = fopen (file, "rb")) == NULL)
624     {
625       fprintf (stderr, _("ERROR: Could not open replay %s\n"), file);
626       return FALSE;
627     }
628 
629   version = fgetUint32 (fh);
630   if (version != REP_VERS)
631     {
632       fprintf (stderr, _("ERROR: Replay %s is of a different version.\n"),
633 	       file);
634       return FALSE;
635     }
636 
637   i = 0;
638   comment = malloc (sizeof (char) * MAX_CHAR);
639   while ((comment[i++] = fgetc (fh)) != '\0');
640 
641   rep.record = fgetUint32 (fh);
642   rep.totalms = fgetUint32 (fh);
643   rep.fps = fgetUint32 (fh);
644   rep.nframes = fgetUint32 (fh);
645   rep.buf = rep.bst = malloc (size);
646   rep.bufsize = size;
647 
648   for (rep.bodysize = 0; !feof (fh); rep.bodysize++)
649     {
650       ((char *) (rep.buf))[rep.bodysize] = fgetc (fh);
651     }
652 
653   fclose (fh);
654 
655   printf (_
656 	  ("Replay loaded: Size: %d FPS: %d Frames: %d Record: %d Time: %d Comment: %s\n"),
657 	  size, rep.fps, rep.nframes, rep.record, rep.totalms / 1000,
658 	  comment);
659   while (playReplay (gfx, &rep));
660 
661   freeReplay (&rep);
662   free (comment);
663 
664   return TRUE;
665 }
666 
667 char *
getReplayComment(char * file)668 getReplayComment (char *file)
669 {
670   FILE *fh = NULL;
671   int version;
672   char *comment = NULL;
673   int i;
674 
675   if ((fh = fopen (file, "rb")) == NULL)
676     {
677       fprintf (stderr, _("WARNING: Could not open replay %s\n"), file);
678       return NULL;
679     }
680 
681   version = fgetUint32 (fh);
682   if (version != REP_VERS)
683     {
684       fprintf (stderr, _("WARNING: Replay %s is of a different version.\n"),
685 	       file);
686       return NULL;
687     }
688 
689   i = 0;
690   comment = malloc (sizeof (char) * MAX_CHAR);
691   while ((comment[i++] = fgetc (fh)) != '\0');
692 
693   return comment;
694 }
695