1 /* PlayBack.c
2  *
3  * Kevin O'Connor 9/22/97
4  *
5  * Routines neccessary to playback a game recording.
6  */
7 #include "config.h"
8 
9 #include INC_MACHINE_ENDIAN
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 #include <time.h>
16 #include <setjmp.h>
17 #include INC_SYS_TIME
18 #include INC_SYS_WAIT
19 #include INC_SYS_RESOURCE
20 #include INC_SYS_SELECT
21 #include <signal.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <pwd.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <arpa/inet.h>
28 
29 #include "Wlib.h"
30 #include "defs.h"
31 #include "struct.h"
32 #include "data.h"
33 #include "packets.h"
34 
35 #include "version.h"
36 #include "patchlevel.h"
37 
38 #include "censor.h"
39 #include "cowapi.h"
40 #include "defaults.h"
41 #include "enter.h"
42 #include "getship.h"
43 #include "inform.h"
44 #include "input.h"
45 #include "lagmeter.h"
46 #include "map.h"
47 #include "newwin.h"
48 #include "pingstats.h"
49 #include "redraw.h"
50 #include "socket.h"
51 #include "spopt.h"
52 #include "stats.h"
53 #include "defs.h"
54 #include "playerlist.h"
55 
56 #include "playback.h"
57 
58 #ifdef RECORDGAME
59 extern int opened_info;				 /* counter for infowin *
60 
61 						  *
62 						  * * popup, 6/1/93 LAB */
63 #define RETURNBASE 10
64 
65 extern jmp_buf env;
66 
67 int     pbdelay = 200000;
68 
69 int *pb_index;
70 int pb_num_index = 0;
71 int pb_goto = 0;
72 int pb_create_index;
73 int pb_index_exists;
74 int pb_num_context = 0;
75 int pb_num_fast_forward = 0;
76 int pb_sequence_count = 0;
77 int pb_stepping = 0;			/* non-zero if doing a step	*/
78 int pb_snapping = 0;			/* non-zero if taking camera snapshots	*/
79 
80 const char *INDEX_FORMAT = "%d,%d,%d";
81 const int INDEX_GRANULARITY = 100;
82 
83 static int packet_size = 0;
84 
85 struct player dummyme;
86 
87 struct player *packetsme;
88 struct player *displayme;
89 
90 /* We want reverse-playback!!! */
91 #define REVERSE_PLAYBACK
92 
93 void pb_read_index();
94 int pb_get_index(int sequence_num, int *jump_actual, int *offset, int *num_context);
95 int pb_index_compare(const void *a, const void *b);
96 
97 /* Forward declarations for reverse playback */
98 void rpb_init(void);
99 void rpb_analyze(int diskpos, void *packet);
100 void rpb_dorev(char *buf);
101 static int readFromFile0();
102 
103 struct player *packetsme;
104 struct player *displayme;
105 
106 int
pbmain(char * name)107         pbmain(char *name)
108 {
109   int     i;
110 
111 #if 0 /* unused? */
112   int     s_type;
113 #endif
114 
115   char index_filename[FILENAME_MAX+1];
116   char context_filename[FILENAME_MAX+1];
117 
118   strncpy(index_filename, recordFileName, FILENAME_MAX-4);
119   strncpy(context_filename, recordFileName, FILENAME_MAX-4);
120   index_filename[FILENAME_MAX-4] = '\0';
121   context_filename[FILENAME_MAX-4] = '\0';
122   strcat(index_filename, ".idx");
123   strcat(context_filename, ".cxt");
124 
125 #ifdef REVERSE_PLAYBACK
126   rpb_init();
127 #endif
128 
129   playback = PL_FORWARD;
130   pseudo[0] = defpasswd[0] = '\0';
131 
132   i = setjmp(env);				 /* Error while initializing */
133   if (i >= RETURNBASE)
134     return (i - RETURNBASE);			 /* Terminate with retcode */
135 
136   if (logFileName != NULL)
137     {
138       logFile = fopen(logFileName, "a");
139       if (logFile == NULL)
140         {
141           perror(logFileName);
142           return (1);
143         }
144     }
145 
146   for (i = 0; i < 80; i++)
147     {
148       outmessage[i] = '\0';
149     }
150 
151   SRANDOM(time(NULL));
152 
153   initDefaults(deffile);
154 
155   SRANDOM(getpid() * time(NULL));
156 
157   newwin(display_host, name);
158 
159   resetdefaults();
160   if (censorMessages)
161     initCensoring();
162 
163   /* open memory...? */
164   openmem();
165 
166   /* Open record file. */
167   recordFile = fopen(recordFileName, "rb");
168   if (recordFile == NULL)
169     {
170       perror(recordFileName);
171       return (1);
172 
173     }
174 
175   /* Open index files of recording. */
176   pb_index_exists = 1;
177   if (!pb_create_index) {
178       recordIndexFile = fopen(index_filename, "r");
179       if (recordIndexFile == NULL)
180           {
181               perror(index_filename);
182               pb_index_exists = 0;
183           }
184 
185       recordContextFile = fopen(context_filename, "rb");
186       if (recordContextFile == NULL)
187       {
188           perror(context_filename);
189           pb_index_exists = 0;
190       }
191   }
192   else
193       pb_index_exists = 0;
194 
195 
196   /* Create index files if we were told to index the recording. */
197   if (pb_create_index  && (recordIndexFile =
198        fopen(index_filename, "wb"))==NULL)
199   {
200       perror("Could not create index file.");
201       exit(1);
202   }
203   if (pb_create_index && (recordContextFile =
204        fopen(context_filename, "wb"))==NULL)
205   {
206       perror("Could not create context file.");
207       exit(1);
208   }
209   if (pb_create_index) {
210       pbdelay = 0;
211       playback = PL_FORWARD;
212       printf("Creating index.\n");
213   }
214 
215   me = &dummyme;
216   myship = &(me->p_ship);
217   mystats = &(me->p_stats);
218 
219   me->p_x = me->p_y = 50000;
220   getship(myship, CRUISER);
221   shipchange(CRUISER);
222   displayme = me;
223   packetsme = me;
224 
225   /* Read the index file.  We do this only once. */
226   pb_read_index();
227 
228   /* Get first packet from file */
229   readFromFile();
230 
231   displayme = packetsme;
232 
233   lastm = mctl->mc_current;
234 
235   mapAll();
236 
237   /* Instructions from getname() */
238   MZERO(mystats, sizeof(struct stats));
239 
240   mystats->st_tticks = 1;
241   for (i = 0; i < 95; i++)
242     {
243       mystats->st_keymap[i] = i + 32;
244       mystats->st_keymap[i + 96] = i + 32 + 96;
245 
246 #ifdef MOUSE_AS_SHIFT
247       mystats->st_keymap[i + 192] = i + 32;
248       mystats->st_keymap[i + 288] = i + 32;
249       mystats->st_keymap[i + 384] = i + 32;
250 #endif
251     }
252   mystats->st_keymap[95] = 0;
253   mystats->st_flags = ST_MAPMODE + ST_NAMEMODE + ST_SHOWSHIELDS +
254       ST_KEEPPEACE + ST_SHOWLOCAL * 2 + ST_SHOWGLOBAL * 2;
255 
256   /* End getname() */
257 
258   phaserWindow = booleanDefault("phaserWindow", phaserWindow);
259 
260 
261 #ifdef AUTOKEY
262   /* autokey.c */
263   autoKeyDefaults();
264 #endif /* AUTOKEY */
265 
266   initkeymap();
267 
268   /* Set p_hostile to hostile, so if keeppeace is on, the guy starts off * *
269    * hating everyone (like a good fighter should) */
270   me->p_hostile = (FED | ROM | KLI | ORI);
271 
272   if (!newDashboard)
273     {
274       char    buf[128];
275 
276       sprintf(buf,
277 	   "Maximum:      %2d  %3d %3d               %3d   %6d   %3d   %3d",
278 	      0, 0, 0, 0, 0, 0, 0);
279       W_WriteText(tstatw, 50, 27, textColor, buf, strlen(buf), W_RegularFont);
280     }
281 
282 #ifdef AUTOKEY
283   if (autoKey)
284     {
285       /* XX: changes entire state of display */
286       W_AutoRepeatOff();
287     }
288 #endif
289 
290 #ifdef SOUND
291   Init_Sound();
292 #endif
293 
294   i = setjmp(env);				 /* Reentry point of game */
295   if (i >= RETURNBASE)
296     return (i - RETURNBASE);			 /* Terminate with retcode */
297 
298 #if defined(SOUND)
299 #if defined(sgi)
300 	Engine_Sound(ENG_OFF);			/* Stop engine sound */
301 #else
302 	/* text in sound.c:soundrefresh() says engine sound is not supported
303 	Abort_Sound(ENGINE_SOUND); */
304 #endif
305 #endif
306 
307 #ifdef nodef
308   /* Code from entrywindow() */
309   {
310     run_clock(time(0));
311 
312     if (remap[me->p_team] == NOBODY)
313       RedrawPlayerList();			 /* When you first login */
314     else
315       UpdatePlayerList();			 /* Otherwise */
316 
317   }
318   /* End entrywindow() */
319 #endif
320 
321   redrawall = 2;
322 
323   getship(myship, myship->s_type);
324   shipchange(myship->s_type);
325   enter();
326   calibrate_stats();
327   W_ClearWindow(w);
328 
329   me->p_status = PALIVE;			 /* Put player in game */
330   PlistNoteUpdate(me->p_no);
331 
332   if (showStats)				 /* Default showstats are on.
333 						  *
334 						  */
335     W_MapWindow(statwin);
336 
337   if (W_IsMapped(lMeter))
338     redrawLMeter();
339 
340   if (W_IsMapped(pStats))			 /* support ping stuff */
341     redrawPStats();
342 
343 #ifdef SOUND
344 #if defined(sgi)
345 	Engine_Sound(ENG_ON);
346 #else
347 	/* text in sound.c:soundrefresh() says engine sound is not supported
348 	Play_Sound(ENGINE_SOUND);
349 	*/
350 #endif
351 	Play_Sound(ENTER_SHIP_SOUND);
352 #endif
353 
354 #ifdef HOCKEY_LINES
355   init_hockey_lines();
356 #endif
357 
358   while (1)
359     {
360 
361 #ifdef nodef
362       fd_set  readfds;
363       struct timeval timeout;
364       int     xsock = W_Socket();
365 
366       timeout.tv_sec = 0;
367       timeout.tv_usec = 0;
368 #endif
369 
370       if (keepInfo > 0 && opened_info != -2 &&	 /* 6/1/93 LAB */
371 	  opened_info < 0 && infomapped)
372 	destroyInfo();
373 
374       if (keepInfo > 0 && opened_info != -2)
375 	opened_info--;
376 
377       while (W_EventsQueuedCk())
378 	{
379 	  process_event();
380 	  /* W_Flush(); */
381 	}
382 
383       intrupt(NULL);
384       W_Flush();
385       if (!pb_stepping) usleep(pbdelay);
386       if (pb_snapping) W_CameraSnap(w);
387     }
388 }
389 
390 void
pbsetspeed(char key)391         pbsetspeed(char key)
392 {
393 #define JUMP_MAX 7
394   static char jump_str[JUMP_MAX] = "";
395   static int jump_idx = 0;
396   static int jump_on = 0;
397   static int tmp_playback;
398   static int tmp_pbdelay = -1;
399 
400   int old_playback;
401 
402   /* Used at end of function. */
403   old_playback = playback;
404 
405   /* Read in sequence number to jump to. */
406   if (jump_on) {
407       switch (key) {
408       case '0': case '1': case '2': case '3': case '4': case '5':
409       case '6': case '7': case '8': case '9':
410 
411           /* Ignore further input if we run out of room. */
412           if (jump_idx > JUMP_MAX - 2)
413               break;
414 
415           /* Get the next number. */
416           jump_str[jump_idx] = key;
417           jump_idx++;
418           jump_str[jump_idx] = '\0';
419           printf("Jump input: %s\n", jump_str);
420 
421           break;
422       case 'J': /* abort jump */
423           jump_on = 0;
424           playback = tmp_playback;
425           printf("Aborting jump.\n");
426           jump_str[0] = '\0';
427           break;
428       default: /* Done entering jump data when non-digit key hit. */
429           jump_str[jump_idx] = '\0';
430 
431           /* Convert string to number. */
432           pb_goto = strtol(jump_str, NULL, 10);
433           if (pb_goto < 1)
434               pb_goto = 1;
435 
436           jump_on = 0;
437           jump_str[0] = '\0';
438           break;
439       }
440       goto end;
441   }
442 
443   if (playback == PL_PAUSE)
444     playback = PL_FORWARD;
445   switch (key)
446     {
447     case 0x8:	/* step backward one frame	*/
448       pb_stepping++;
449       playback = PL_REVERSE;
450       break;
451     case 0xd:	/* step forward one frame	*/
452       pb_stepping++;
453       break;
454     case ' ':	/* turn on or off single step	*/
455       if (old_playback == PL_PAUSE)
456 	playback = PL_FORWARD;
457       else
458 	pb_stepping++;
459       break;
460     case '0':
461       playback = PL_PAUSE;
462       break;
463     case '1':
464       tmp_pbdelay = pbdelay = 800000;
465       break;
466     case '2':
467       tmp_pbdelay = pbdelay = 400000;
468       break;
469     case '3':
470       tmp_pbdelay = pbdelay = 200000;
471       break;
472     case '4':
473       tmp_pbdelay = pbdelay = 100000;
474       break;
475     case '5':
476       tmp_pbdelay = pbdelay = 50000;
477       break;
478     case '6':
479       tmp_pbdelay = pbdelay = 25000;
480       break;
481     case '7':
482       tmp_pbdelay = pbdelay = 12500;
483       break;
484     case '8':
485       tmp_pbdelay = pbdelay = 6250;
486       break;
487     case '9':
488     case '#':
489     case '!':
490     case '@':
491     case '%':
492       tmp_pbdelay = pbdelay = 0;
493       break;
494     case '<':
495       if (tmp_pbdelay == -1)
496           tmp_pbdelay = pbdelay /= 2;
497       else
498           pbdelay = tmp_pbdelay /= 2;
499       break;
500     case '>':
501       if (tmp_pbdelay == -1)
502           tmp_pbdelay = pbdelay *= 2;
503       else
504           pbdelay = tmp_pbdelay *= 2;
505       break;
506     case 'R':
507       {
508 #ifdef REVERSE_PLAYBACK
509 	rpb_init();
510 #endif
511   	fseek (recordFile, 0, SEEK_SET);
512 	playback = PL_FORWARD;
513         pb_sequence_count = 0;
514 	break;
515       }
516     case '(':
517       playback = PL_REVERSE;
518       break;
519     case ')':
520       playback = PL_FORWARD;
521       break;
522     case 'j':
523       printf("Jumping..\n");
524       tmp_playback = playback;
525       playback = PL_PAUSE;
526       jump_on = 1;
527       jump_idx = 0;
528       jump_str[0] = '\0';
529       break;
530     case 's':
531       fprintf(stderr, "toggle-snap\n");
532       pb_snapping = ~pb_snapping;
533       break;
534     }
535 
536  end:
537     /* If we are paused, set the delay to something reasonable. */
538     if (playback == PL_PAUSE && old_playback != PL_PAUSE) {
539         tmp_pbdelay = pbdelay;
540         pbdelay = 100000;
541     }
542 
543     /* If we were paused, but now are not, reset the original delay. */
544     if (old_playback == PL_PAUSE && playback != PL_PAUSE && tmp_pbdelay != -1) {
545         pbdelay = tmp_pbdelay;
546         tmp_pbdelay = -1;
547     }
548 }
549 
550 void
pblockplayer(int who)551         pblockplayer(int who)
552 {
553    me = displayme = &players[who];
554 }
555 
556 void
pblockplanet(int pl)557         pblockplanet(int pl)
558 {
559    me = &dummyme;
560    displayme->p_x = planets[pl].pl_x;
561    displayme->p_y = planets[pl].pl_y;
562    displayme = me;
563 }
564 
565 int
ckRecordPacket(char packet)566         ckRecordPacket(char packet)
567 {
568   return 1;
569 #if 0
570   switch (packet)
571     {
572     case SP_MESSAGE:
573     case SP_S_MESSAGE:
574     case SP_S_WARNING:
575     case SP_WARNING:
576       /* SP_MOTD */
577 
578     case SP_PLAYER_INFO:
579     case SP_PLAYER:
580     case SP_S_PLAYER:
581     case SP_KILLS:
582     case SP_S_KILLS:
583 
584     case SP_TORP_INFO:
585     case SP_TORP:
586     case SP_S_TORP:
587     case SP_S_TORP_INFO:
588     case SP_S_8_TORP:
589     case SP_PLASMA_INFO:
590     case SP_PLASMA:
591 
592     case SP_PHASER:
593     case SP_S_PHASER:
594 
595     case SP_YOU:
596     case SP_S_YOU:
597     case SP_S_YOU_SS:
598 
599     case SP_STATUS:
600 
601     case SP_PLANET:
602     case SP_S_PLANET:
603     case SP_PLANET_LOC:
604 
605     case SP_FLAGS:
606       /*    case SP_MASK: */
607     case SP_PSTATUS:
608     case SP_HOSTILE:
609     case SP_STATS:
610     case SP_S_STATS:
611     case SP_PL_LOGIN:
612     case SP_SHIP_CAP:
613 
614 
615     case SP_SEQUENCE:
616     case SP_SC_SEQUENCE:
617     case SP_S_SEQUENCE:
618 
619       return 1;
620     }
621   return 0;
622 #endif
623 }
624 
625 extern struct packet_handler handlers[];
626 
627 /**
628  * Read the next packet from the record file and call the appropiate handler.
629  *
630  * @param buf Stores the next packet.
631  *
632  * @return 1 if EOF or error, or 0 for success.
633  */
634 int
pb_dopacket(char * buf)635 pb_dopacket(char *buf)
636 {
637   int size, count;
638 
639   count = fread(buf, 1, 4, recordFile);
640   if (count < 4)
641     {
642       return 1;
643     }
644 
645 
646   /* Determine how many more bytes we need to read. */
647   size = handlers[(unsigned char) buf[0]].size;
648   if (size == -1)
649     {
650       if (buf[0] == SP_S_MESSAGE)
651 	{
652 	  /* UGH.  SP_S_MESSAGE needs next word to calculate size */
653 	  count += fread(buf+count, 1, 4, recordFile);
654 	  if (count < 8)
655 	    {
656 	      return 1;
657 	    }
658 	}
659       size = getvpsize(buf);
660     }
661 
662   packet_size = size;
663 
664   /* Read the rest of the packet. */
665   if (size > count)
666     count += fread(buf+count, 1, size-count, recordFile);
667   if (debug)
668     printf("Reading packet %d\n", buf[0]);
669 
670   /* If we couldn't read enough */
671   if (count < size)
672     {
673       return 1;
674     }
675 
676   /* Call the packet handler and return success (zero). */
677   (*(handlers[(unsigned char) buf[0]].handler)) (buf
678 #ifdef CORRUPTED_PACKETS
679 				 ,recordFile
680 #endif
681 				 );
682   return 0;
683 }
684 
685 
readFromFile()686 int readFromFile() {
687     int offset = ftell(recordFile);
688     int jump_actual; /* The sequence number we actually jump to. */
689     FILE *tmp_file;
690     int result;
691     int num_fast_forward;
692 
693     /* If jumping to a spot in the recording, read the required number of
694        context packets first. */
695     if (pb_goto) {
696         playback = PL_FORWARD;
697         num_fast_forward = pb_get_index(pb_goto, &jump_actual,
698                                         &offset, &pb_num_context);
699 
700         if (pb_num_context > 0) {
701             printf("Reading in %d context packets..\n", pb_num_context);
702             rewind(recordContextFile);
703             tmp_file = recordFile;
704             recordFile = recordContextFile;
705             pb_sequence_count = 0;
706 
707             readFromFile0();
708 
709             recordFile = tmp_file;
710         }
711 
712         pb_sequence_count = jump_actual - 1;
713         pb_num_fast_forward = num_fast_forward;
714 
715 #ifdef REVERSE_PLAYBACK
716         rpb_init();
717 #endif
718     }
719 
720     /* Get up to the next sequence packet. */
721     fseek(recordFile, offset, SEEK_SET);
722     result = readFromFile0();
723 
724     /* If we jumped pause playback and reset state back to normal. */
725     if (pb_goto) {
726         playback = PL_PAUSE;
727         pb_goto = 0;
728 
729         printf("Done jumping.\n");
730     }
731 
732     return result;
733 }
734 
735 
readFromFile0()736 static int readFromFile0()
737 {
738 #define MAXPACKETSIZE 128
739   static uint aligned_buf[MAXPACKETSIZE/sizeof(uint)];
740   static int num_context_written = 0;
741 
742   char *buf = (char *) &aligned_buf;
743   int diskpos;
744   int sequence_start_pos = -1;
745   int sequence_num_context = 0;
746 
747 #if 0 /* unused? */
748   int read_context = 0;
749 #endif
750 
751 
752   if (playback == PL_PAUSE)
753     return 1;
754 #ifdef REVERSE_PLAYBACK
755     else if (playback == PL_REVERSE) {
756         diskpos = ftell(recordFile);
757         me = packetsme;
758 
759         rpb_dorev(buf);
760 
761         packetsme = me;
762         me = displayme;
763 
764         if (pb_stepping) {
765 	  playback = PL_PAUSE;
766 	  pb_stepping = 0;
767         }
768 
769         return 1;
770     }
771 #endif
772 
773   if (pb_stepping) {
774     playback = PL_PAUSE;
775     pb_stepping = 0;
776   }
777 
778   /* Read packets. */
779   while (1) {
780       diskpos = ftell(recordFile);
781       me = packetsme;
782 
783       if (sequence_start_pos == -1)
784           sequence_start_pos = diskpos;
785 
786       /* Read a packet and call it's handler. */
787       if (pb_dopacket(buf)) {
788           /* End of file reached! */
789           playback = PL_PAUSE;
790           pb_num_fast_forward = 0;
791           packetsme = me;
792           me = displayme;
793           printf("End of file.\n");
794 
795           if (pb_create_index)
796               exit(0);
797 
798           return 1;
799       }
800 
801 #ifdef REVERSE_PLAYBACK
802       if (!pb_create_index && !pb_num_context)
803           rpb_analyze(diskpos, buf);
804 #endif
805 
806       /* If we are reading in context packts.. */
807       if (pb_num_context) {
808           pb_num_context--;
809 
810           if (pb_num_context < 1) {
811               printf("Done reading context packets.\n");
812               return 1;
813           }
814       }
815 
816       /* If we are creating an index of the recording, write packet if necessary */
817       if (pb_create_index && PB_CONTEXT(buf[0]) ) {
818           if (!fwrite(buf, 1, packet_size, recordContextFile)) {
819               perror("Bad write on context file.");
820               exit(1);
821           }
822           num_context_written++;
823           sequence_num_context++;
824       }
825 
826       packetsme = me;
827       me = displayme;
828 
829       if (buf[0] == SP_SEQUENCE || buf[0] == SP_SC_SEQUENCE
830           || buf[0] == SP_S_SEQUENCE)
831       {
832           pb_sequence_count++;
833 
834           if (pb_num_fast_forward > 0)
835               pb_num_fast_forward--;
836 
837           /* If we are creating an index of the recording, write out what
838              sequence number we are on, where we are in the file, and how
839              many context packets we have to read back in to get back
840              here. */
841           if ( pb_create_index && !(pb_sequence_count % INDEX_GRANULARITY) ) {
842               fprintf(recordIndexFile, INDEX_FORMAT, pb_sequence_count,
843                       sequence_start_pos, num_context_written - sequence_num_context);
844               fprintf(recordIndexFile, "\n");
845           }
846 
847           if ( pb_create_index && !(pb_sequence_count % 1000) )
848               printf("Indexed %d sequences.\n", pb_sequence_count);
849 
850           /* For testing, cut this short.  A recording should be under
851              100k sequences */
852           if (pb_sequence_count > 1000000) {
853               printf("I've seen enough, exiting!\n");
854               exit(0);
855           }
856 
857 
858           sequence_start_pos = -1;
859           sequence_num_context = 0;
860 
861           /* Return 1 if redraw needed. */
862           if (pb_create_index || pb_num_fast_forward)
863               ;
864           else
865               return 1;
866 
867       } /* if sequence packet */
868 
869   } /* while(1) */
870 
871 }
872 
pb_read_index()873 void pb_read_index() {
874     const int MAXLINE = 100;
875     char line[MAXLINE+1];
876     int num_lines = 0;
877 
878     if (!pb_index_exists) {
879         pb_index = NULL;
880         pb_num_index = 0;
881         return;
882     }
883 
884     rewind(recordIndexFile);
885 
886     /* Count how many lines we have. */
887     while (fgets(line, MAXLINE, recordIndexFile))
888         num_lines++;
889 
890     pb_index = calloc(3*num_lines, sizeof(int));
891     pb_num_index = num_lines;
892 
893     rewind(recordIndexFile);
894 
895     {
896         int i = 0;
897         while (fscanf(recordIndexFile,
898                       INDEX_FORMAT,
899                       &pb_index[i*3], /* Sequence num */
900                       &pb_index[i*3 + 1], /* offset into file */
901                       &pb_index[i*3 + 2]) /* number of context packets */
902                > 0)
903         {
904             i++;
905         }
906     }
907 
908 }
909 
pb_get_index(int sequence_num,int * p_jump_actual,int * offset,int * num_context)910 int pb_get_index(int sequence_num, int *p_jump_actual, int *offset, int *num_context) {
911     int *found;
912     int jump_actual;
913     int num_left;
914     int first_index_num;
915     int last_index_num;
916 
917     /* If the index doesn't exist go to the beginning and fast forward */
918     if (!pb_index_exists){
919         jump_actual = 1;
920         *offset = 0;
921         *num_context = 0;
922         num_left = sequence_num - 1;
923         goto end;
924     }
925 
926     first_index_num = pb_index[0];
927     last_index_num = pb_index[(pb_num_index-1) * 3];
928 
929     /* Convert the sequence number they want to goto to the closest sequence
930        we have indexed that is earlier.  For example, if they want to go to
931        #801 and we index every 100 sequences, look for #700.  We go back an
932        extra 100 so we can read in recent packets that contain info like what
933        ships are cloaked, armies carried, etc. */
934     jump_actual = (sequence_num / INDEX_GRANULARITY) * INDEX_GRANULARITY - INDEX_GRANULARITY;
935 
936     /* If they go before the first index, take them to the
937        beginning and fast forward. */
938     if (jump_actual < first_index_num) {
939         jump_actual = 1;
940         *offset = 0;
941         *num_context = 0;
942         num_left = sequence_num - 1;
943         goto end;
944     }
945 
946     /* If they go too far ahead, take them to the last sequence indexed. */
947     if (jump_actual > last_index_num) {
948         printf("Jumping ahead to last sequence.\n");
949         jump_actual = last_index_num;
950     }
951 
952     /* Calculate how many sequences we have to fast forward after jumping ahead. */
953     num_left = sequence_num - jump_actual;
954 
955     found = bsearch(&jump_actual, pb_index, pb_num_index, sizeof(int)*3, pb_index_compare);
956     if (found == NULL) {
957         /* Shouldn't happen. */
958         jump_actual = 1;
959         *offset = 0;
960         *num_context = 0;
961         num_left = sequence_num - 1;
962     }
963     else {
964         int *f = (int *)found;
965         *offset = *(f+1);
966         *num_context = *(f+2);
967     }
968 
969 
970  end:
971     *p_jump_actual = jump_actual;
972     num_left++;
973     return num_left;
974 }
975 
pb_index_compare(const void * a,const void * b)976 int pb_index_compare(const void *a, const void *b) {
977     int x = *(int *)a;
978     int y = *(int *)b;
979 
980     if (x > y)
981         return 1;
982     else if (x < y)
983         return -1;
984     else
985         return 0;
986 
987 }
988 
989 #ifdef REVERSE_PLAYBACK
990 
991 /**************************** Reverse playback ***************************/
992 /**************************** Reverse playback ***************************/
993 /**************************** Reverse playback ***************************/
994 
995 /* Reverse-Playback -
996  *  Reverse playback does just what it implies.  It allows the client to
997  * play a game recording in reverse.  It is a nifty feature useful
998  * for analyzing a game.  With forward only playback, if a desired point
999  * is played over, the only way to see that point again would be to reset
1000  * the playback to the very beginning and forward-play the recording until
1001  * the point is re-located.  With a long recording this quickly becomes
1002  * infeasible.  With reverse recording one can simply forward and reverse
1003  * over interesting points.
1004  *
1005  * That said, no sane person would have implemented this without
1006  * getting paid for it...
1007  *
1008  * Attempting to reset the netrek state to a position in the past is
1009  * a considerable challenge when the input is a dynamic stream.
1010  * I only implemented it because I thought it was an interesting challenge..
1011  * Ok, so I dont document my code very well - I'm not getting paid - so
1012  * here is a brief run-down of how I accomplished this task.
1013  *
1014  * First step is to analyze all incoming packets.  Each packet is then
1015  * flagged with a unique handler for each piece of unique data the server
1016  * sent.  (IE. the SP_HOSTILE packet contains RPB_PLAYER_HOSTILE data, while
1017  * SP_YOU contains RPB_PLAYER_HOSTILE, RPB_PLAYER_FUEL, RPB_PLAYER_ARMIES,
1018  * and RPB_PLAYER_FLAGS.  SP_TORP contains 1 RPB_TORP_POSITION data, while
1019  * SP_S_TORP contains 8 RPB_TORP_POSITIONs)
1020  *
1021  * The next step is to build a table from all these RPB_ server information
1022  * handles.  The key to this table is the ability to find the previous
1023  * packet that contained any given RPB_ server information, and be able
1024  * to backtrack through all the packets that the server ever sent that also
1025  * updated that RPB_ data.  In addition to being able to access the table
1026  * via RPB_ keys, it must also be accessed via relative disk
1027  * positions.  The table is in essence a series of multiply linked
1028  * linked-lists.
1029  * (I implemented the table using a series of singly linked lists that
1030  * are contained within a dynamicly resizing array of linked-list nodes.)
1031  *
1032  * The bad news is, the table uses alot of memory.  There is no way
1033  * around this.
1034  * The good news is, any virtual OS should be able to handle this with
1035  * no problems.  (Only recently used items are referenced within the
1036  * array, thus the OS can swap out earlier pages of the array.)
1037  *
1038  * Once it comes time to reverse playback, a frame is chosen to be replayed.
1039  * The program then finds all the packets that contributed to that
1040  * frame's information, and one by one re-reads them.  As long as
1041  * there are RPB_ handles for each piece of data the server can send,
1042  * reconstructing the frame is trivial.
1043  *
1044  * Notes:  This doesn't work for messages..  There is lots of room
1045  *  for improvement..  Reverse playback is pretty slow.. (but there
1046  *  is no need to reverse only 1 update at a time..)  I'm pretty
1047  *  sure this will start to exhibit 'weird' effects in high packet loss..
1048  *		-Kevin O'Connor 03/17/98
1049  */
1050 
1051 
1052 /* List of integer handles.
1053  * These handles represent each piece of information the server
1054  * can send within a packet
1055  */
1056 #define RPB_NORMAL_SEQUENCE 0
1057 #define RPB_NORMAL_STATUS 1
1058 #define RPB_NORMAL_MAX 2
1059 
1060 #define RPB_PLAYER_OFFSET (RPB_NORMAL_MAX)
1061 #define RPB_PLAYER_INFO 0
1062 #define RPB_PLAYER_LOGIN 1
1063 #define RPB_PLAYER_HOSTILE 2
1064 #define RPB_PLAYER_STATS 3
1065 #define RPB_PLAYER_FLAGS 4
1066 #define RPB_PLAYER_KILLS 5
1067 #define RPB_PLAYER_POSITION 6
1068 #define RPB_PLAYER_FUEL 7
1069 #define RPB_PLAYER_ARMIES 8
1070 #define RPB_PLAYER_STATUS 9
1071 #define RPB_PLAYER_PHASER 10
1072 #define RPB_PLAYER_MAX 11
1073 
1074 #define RPB_PLASMA_OFFSET (RPB_PLAYER_OFFSET + RPB_PLAYER_MAX*MAXPLAYER)
1075 #define RPB_PLASMA_POSITION 0
1076 #define RPB_PLASMA_INFO 1
1077 #define RPB_PLASMA_MAX 2
1078 
1079 #define RPB_TORP_OFFSET (RPB_PLASMA_OFFSET + RPB_PLASMA_MAX*MAXPLASMA*MAXPLAYER)
1080 #define RPB_TORP_POSITION 0
1081 #define RPB_TORP_INFO 1
1082 #define RPB_TORP_MAX 2
1083 
1084 #define RPB_PLANET_OFFSET (RPB_TORP_OFFSET + RPB_TORP_MAX*MAXTORP*MAXPLAYER)
1085 #define RPB_PLANET_INFO 0
1086 #define RPB_PLANET_POSITION 1
1087 #define RPB_PLANET_MAX 2
1088 
1089 #define RPB_TOTAL (RPB_PLANET_OFFSET + RPB_PLANET_MAX*MAXPLANETS)
1090 
1091 struct LocPacketInfo {
1092   int prev;
1093   uint diskpos;
1094 };
1095 
1096 struct LocPacketInfo *header = NULL;
1097 int lastPos[RPB_TOTAL];
1098 int max_alloc = 0;
1099 int current;
1100 
1101 /* Number of LocPacketInfo to re-allocate each time an expansion
1102  * is required.  This should be on the edge of a page boundry to reduce
1103  * system overhead. */
1104 #define EXPANDCOUNT 512
1105 
1106 /* Initialize the data structures used in reverse playback */
1107 void
rpb_init(void)1108 rpb_init(void)
1109 {
1110   int i;
1111   for (i=0; i<RPB_TOTAL; i++)
1112     lastPos[i] = -1;
1113   current = 0;
1114 }
1115 
1116 /* Add a server information handler to the current table */
1117 void
rpb_insert(int diskpos,int hdl)1118 rpb_insert(int diskpos, int hdl)
1119 {
1120   if (current >= max_alloc)
1121     {
1122       /* Expand table size */
1123       max_alloc += EXPANDCOUNT;
1124       header = realloc(header, max_alloc * sizeof(struct LocPacketInfo));
1125     }
1126   header[current].prev = lastPos[hdl];
1127   header[current].diskpos = diskpos;
1128   lastPos[hdl] = current;
1129   current++;
1130 }
1131 
1132 /* Given a packet, generate a set of integer handles for the data
1133  * contained within the packet, and then add them to the current table */
1134 void
rpb_analyze(int diskpos,void * packet)1135 rpb_analyze(int diskpos, void *packet)
1136 {
1137   int id;
1138 
1139   switch (*(char *) packet)
1140     {
1141     case SP_STATUS:
1142       rpb_insert(diskpos, RPB_NORMAL_STATUS);
1143       break;
1144     case SP_SEQUENCE:
1145     case SP_S_SEQUENCE:
1146     case SP_SC_SEQUENCE:
1147       rpb_insert(diskpos, RPB_NORMAL_SEQUENCE);
1148       break;
1149 
1150     case SP_PLAYER_INFO:
1151       id = ((struct plyr_info_spacket *)packet)->pnum;
1152       rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1153 	+ RPB_PLAYER_INFO);
1154       break;
1155     case SP_PL_LOGIN:
1156       id = ((struct plyr_login_spacket *)packet)->pnum;
1157       rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1158 	+ RPB_PLAYER_LOGIN);
1159       break;
1160     case SP_HOSTILE:
1161       id = ((struct hostile_spacket *)packet)->pnum;
1162       rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1163 	+ RPB_PLAYER_HOSTILE);
1164       break;
1165     case SP_STATS:
1166       id = ((struct stats_spacket *)packet)->pnum;
1167       rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1168 	+ RPB_PLAYER_STATS);
1169       break;
1170     case SP_FLAGS:
1171       id = ((struct flags_spacket *)packet)->pnum;
1172       rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1173 	+ RPB_PLAYER_FLAGS);
1174       break;
1175     case SP_KILLS:
1176       id = ((struct kills_spacket *)packet)->pnum;
1177       rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1178 	+ RPB_PLAYER_KILLS);
1179       break;
1180     case SP_PLAYER:
1181       id = ((struct player_spacket *)packet)->pnum;
1182       rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1183 	+ RPB_PLAYER_POSITION);
1184       break;
1185     case SP_YOU:
1186       id = ((struct you_spacket *)packet)->pnum;
1187       rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1188 	+ RPB_PLAYER_HOSTILE);
1189       rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1190 	+ RPB_PLAYER_FLAGS);
1191       rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1192 	+ RPB_PLAYER_FUEL);
1193       rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1194 	+ RPB_PLAYER_ARMIES);
1195       break;
1196     case SP_PSTATUS:
1197       id = ((struct pstatus_spacket *)packet)->pnum;
1198       rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1199 	+ RPB_PLAYER_STATUS);
1200       break;
1201     case SP_S_YOU:
1202        id = ((struct youshort_spacket *)packet)->pnum;
1203        rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1204  	+ RPB_PLAYER_HOSTILE);
1205        rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1206  	+ RPB_PLAYER_FLAGS);
1207        rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1208 	+ RPB_PLAYER_ARMIES);
1209       break;
1210     case SP_S_YOU_SS:
1211       if (F_many_self)
1212 	id = ((struct youss_spacket *)packet)->pad1;
1213       else
1214 	id = me->p_no;
1215       rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1216 	+ RPB_PLAYER_FUEL);
1217       break;
1218     case SP_S_KILLS:
1219       {
1220 	int i, numofkills;
1221 
1222 	numofkills = ((unsigned char *) packet)[1];
1223 
1224 	for (i = 0; i < numofkills; i++)
1225 	  {
1226 	    id = ((unsigned char *) packet)[i*2+3] >> 2;
1227  	    rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1228  		       + RPB_PLAYER_KILLS);
1229  	  }
1230       }
1231       break;
1232     case SP_S_PLAYER:
1233       {
1234 	int i, numofplayers, offset;
1235  	unsigned char *sbuf = (unsigned char *) packet;
1236 
1237  	numofplayers = sbuf[1] & 0x3f;
1238 
1239  	if (sbuf[1] & 128)
1240  	  {
1241  	    offset = 32;
1242  	    sbuf += 4;
1243  	  }
1244  	else if (sbuf[1] & 64)
1245  	  {
1246  	    offset = 0;
1247             if (shortversion == SHORTVERSION) {
1248               if (sbuf[2] == 2) {
1249                 sbuf += 8;
1250               } else if (sbuf[2] == 1) {
1251                 sbuf += 4;
1252               }
1253             }
1254  	  }
1255  	else
1256  	  {
1257  	    offset = 0;
1258  	    sbuf += 12;
1259  	    id = me->p_no;
1260  	    rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1261  		       + RPB_PLAYER_POSITION);
1262  	  }
1263 
1264  	for (i = 0; i < numofplayers; i++)
1265  	  {
1266  	    id = (sbuf[i*4] & 0x1f) + offset;
1267  	    rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1268  		       + RPB_PLAYER_POSITION);
1269  	  }
1270        }
1271        break;
1272      case SP_S_STATS:
1273        id = ((struct stats_s_spacket *)packet)->pnum;
1274        rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1275  	+ RPB_PLAYER_STATS);
1276        break;
1277      case SP_PHASER:
1278        id = ((struct phaser_spacket *)packet)->pnum;
1279        rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1280  		 + RPB_PLAYER_PHASER);
1281        break;
1282      case SP_S_PHASER:
1283        id = ((struct phaser_s_spacket *)packet)->pnum & 0x3f;
1284        rpb_insert(diskpos, RPB_PLAYER_OFFSET + id * RPB_PLAYER_MAX
1285  		 + RPB_PLAYER_PHASER);
1286        break;
1287 
1288      case SP_PLASMA_INFO:
1289        id = ntohs(((struct plasma_info_spacket *)packet)->pnum);
1290        rpb_insert(diskpos, RPB_PLASMA_OFFSET + id * RPB_PLASMA_MAX
1291  	+ RPB_PLASMA_INFO);
1292        break;
1293      case SP_PLASMA:
1294        id = ntohs(((struct plasma_spacket *)packet)->pnum);
1295        rpb_insert(diskpos, RPB_PLASMA_OFFSET + id * RPB_PLASMA_MAX
1296  	+ RPB_PLASMA_POSITION);
1297        break;
1298 
1299      case SP_TORP_INFO:
1300        id = ntohs(((struct torp_info_spacket *)packet)->tnum);
1301        rpb_insert(diskpos, RPB_TORP_OFFSET + id * RPB_TORP_MAX
1302  	+ RPB_TORP_INFO);
1303        break;
1304      case SP_TORP:
1305        id = ntohs(((struct torp_spacket *)packet)->tnum);
1306        rpb_insert(diskpos, RPB_TORP_OFFSET + id * RPB_TORP_MAX
1307  	+ RPB_TORP_POSITION);
1308        break;
1309      case SP_S_8_TORP:
1310      case SP_S_TORP:
1311      case SP_S_TORP_INFO:
1312        {
1313  	int i;
1314 
1315  	if (*(unsigned char *)packet == SP_S_8_TORP)
1316  	  id = ((unsigned char *)packet)[1] * 8;
1317  	else
1318  	  id = ((unsigned char *)packet)[2] * 8;
1319  	for (i=0; i<8; i++)
1320  	  {
1321  	    rpb_insert(diskpos, RPB_TORP_OFFSET + (id+i) * RPB_TORP_MAX
1322  		       + RPB_TORP_INFO);
1323  	    rpb_insert(diskpos, RPB_TORP_OFFSET + (id+i) * RPB_TORP_MAX
1324  		       + RPB_TORP_POSITION);
1325  	  }
1326        }
1327        break;
1328      case SP_PLANET:
1329        id = ((struct planet_spacket *)packet)->pnum;
1330        rpb_insert(diskpos, RPB_PLANET_OFFSET + id * RPB_PLANET_MAX
1331  	+ RPB_PLANET_INFO);
1332        break;
1333      case SP_PLANET_LOC:
1334        id = ((struct planet_loc_spacket *)packet)->pnum;
1335        rpb_insert(diskpos, RPB_PLANET_OFFSET + id * RPB_PLANET_MAX
1336  	+ RPB_PLANET_POSITION);
1337        break;
1338      case SP_S_PLANET:
1339        {
1340  	int i, numofplanets;
1341  	struct planet_s_spacket *ppacket;
1342 
1343  	numofplanets = ((unsigned char *)packet)[1];
1344  	ppacket = (struct planet_s_spacket *) &(((char *)packet)[2]);
1345 
1346  	for (i=0; i<numofplanets; i++, ppacket++)
1347  	  {
1348  	    id = ppacket->pnum;
1349  	    rpb_insert(diskpos, RPB_PLANET_OFFSET + id * RPB_PLANET_MAX
1350  		       + RPB_PLANET_INFO);
1351  	  }
1352        }
1353        break;
1354 #ifdef nodef
1355      default:
1356        printf("packet type %d", *(unsigned char *)packet);
1357 #endif
1358     }
1359 }
1360 
1361 /* silly compare function used by the build in qsort() function */
1362 int
intcomp(const void * a,const void * b)1363 intcomp(const void *a, const void *b)
1364 {
1365   if (*(int *)a < *(int *)b)
1366     return -1;
1367   else if (*(int *)a > *(int *)b)
1368     return 1;
1369   else
1370     return 0;
1371 }
1372 
1373 /* Called from the main playback loop.  This routine un-does
1374  * a server update. */
1375 void
rpb_dorev(char * buf)1376 rpb_dorev(char *buf)
1377 {
1378   int startpos;
1379   int i;
1380   int temp[RPB_TOTAL];
1381   int last = -1;
1382   /*  int min;
1383       int extra = RPB_TOTAL; */
1384 
1385   if (lastPos[RPB_NORMAL_SEQUENCE] == -1
1386       || header[lastPos[RPB_NORMAL_SEQUENCE]].prev == -1)
1387     {
1388       playback = PL_PAUSE;
1389       return;
1390     }
1391 
1392   startpos = header[lastPos[RPB_NORMAL_SEQUENCE]].prev;
1393 
1394   /*  min = startpos; */
1395   for (i=0; i<RPB_TOTAL; i++)
1396     while (lastPos[i] > startpos)
1397      {
1398 	/*	if (lastPos[i] < min)
1399 		min = lastPos[i]; */
1400  	lastPos[i] = header[lastPos[i]].prev;
1401        }
1402 
1403   /* Well, this hack doesn't seem neccessary afterall -
1404    * I'm leaving it as a comment just in case it evenutally is
1405    * required */
1406 #ifdef nodef
1407   /* HACK++ - add in extra me position/sequence packets inline with other
1408    * packets.
1409    * The me position packets are inserted because sp2 torp/player
1410    * packets are based upon the current position of me.
1411    * The sequence packets are inserted just for reference.
1412    * Although these packets are gouped together here, the qsort
1413    * procedure will arrange them inline with the other packets. */
1414    i=lastPos[RPB_PLAYER_OFFSET + RPB_PLAYER_POSITION
1415  	   + RPB_PLAYER_MAX * me->p_no];
1416    if (i != -1)
1417      do
1418       {
1419 	i = header[i].prev;
1420  	temp[extra++] = i;
1421       } while (i > min);
1422    i=startpos;
1423    do
1424      {
1425        i = header[i].prev;
1426        temp[extra++] = i;
1427      } while (i > min);
1428 #endif
1429 
1430   memcpy(temp, lastPos, sizeof(lastPos));
1431   qsort(temp, RPB_TOTAL, sizeof(int), intcomp);
1432 
1433   for (i=0; i<RPB_TOTAL; i++)
1434     if (temp[i] != last)
1435       {
1436 	last = temp[i];
1437 	fseek(recordFile, header[last].diskpos, SEEK_SET);
1438 	pb_dopacket(buf);
1439       }
1440   current = startpos + 1;
1441   pb_sequence_count--;
1442 }
1443 #endif /* REVERSE_PLAYBACK */
1444 
1445 #endif /* RECORDGAME */
1446