]> icculus.org git repositories - taylor/freespace2.git/blob - src/demo/demo.cpp
Initial revision
[taylor/freespace2.git] / src / demo / demo.cpp
1 /*
2  * $Logfile: /Freespace2/code/Demo/Demo.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  *
8  * $Log$
9  * Revision 1.1  2002/05/03 03:28:08  root
10  * Initial revision
11  *
12  * 
13  * 6     8/26/99 8:51p Dave
14  * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
15  * 
16  * 5     6/16/99 10:20a Dave
17  * Added send-message-list sexpression.
18  * 
19  * 4     4/16/99 5:54p Dave
20  * Support for on/off style "stream" weapons. Real early support for
21  * target-painting lasers.
22  * 
23  * 3     3/29/99 6:17p Dave
24  * More work on demo system. Got just about everything in except for
25  * blowing ships up, secondary weapons and player death/warpout.
26  * 
27  * 2     3/28/99 5:58p Dave
28  * Added early demo code. Make objects move. Nice and framerate
29  * independant, but not much else. Don't use yet unless you're me :)
30  *  
31  *  
32  * $NoKeywords: $
33  */
34
35 #include "demo.h"
36 #include "missionload.h"
37 #include "ship.h"
38 #include "linklist.h"
39 #include "freespace.h"
40 #include "object.h"
41 #include "timer.h"
42 #include "gamesequence.h"
43 #include "systemvars.h"
44 #include "cfile.h"
45 #include "missionmessage.h"
46 #include "missionparse.h"
47 #include "shipfx.h"
48 #include "shiphit.h"
49
50 // -----------------------------------------------------------------------------------------------------------------------------
51 // DEMO DEFINES/VARS
52 //
53
54 CFILE *Demo_file = NULL;
55
56 // how often we dump
57 #define DEMO_DEFAULT_FPS                                15
58 int Demo_fps = 1000 / DEMO_DEFAULT_FPS;
59
60 // timestamp for frame dumping
61 int Demo_stamp = -1;
62
63 // missiontime
64 float Demo_missiontime = 0.0f;
65
66 // buffer for reading and writing demo stuff
67 #define DEMO_BUF_SIZE                                   32768
68 char *Demo_buf = NULL;
69 int Demo_buf_pos = 0;
70
71 // # of events posted for this frame
72 int Demo_frame_events = 0;
73
74 // current offset into the demo file - only used for playback
75 int Demo_cur_offset = -1;
76
77 // demo version #
78 #define DEMO_VERSION                                            2
79
80 // an error reading or writing the demo file
81 int Demo_error = DEMO_ERROR_NONE;
82
83 // all strings read out of the demo file must be no longer than this
84 #define DEMO_STRING_LEN                                 255
85
86 // macros
87 #define DEMO_DATA_FRAME()                               do { \
88         if(Demo_file == NULL){\
89                 Int3();\
90                 DEMO_ERROR(DEMO_ERROR_GENERAL);\
91                 break;\
92         }\
93         if(Game_mode & GM_DEMO_RECORD){\
94                 if(Demo_buf_pos == 0) {\
95                         Int3();\
96                         break;\
97                 }\
98                 if(Demo_frame_events <= 0){\
99                         break;\
100                 }\
101                 if(!cfwrite_ushort((ushort)Demo_buf_pos, Demo_file)){\
102                         DEMO_ERROR(DEMO_ERROR_DISK_SPACE);\
103                         break;\
104                 }\
105                 if(!cfwrite(Demo_buf, Demo_buf_pos, 1, Demo_file)){\
106                         DEMO_ERROR(DEMO_ERROR_DISK_SPACE);\
107                         break;\
108                 }\
109         } else if(Game_mode & GM_DEMO_PLAYBACK){\
110                 Demo_buf_pos = (int)cfread_ushort(Demo_file);\
111                 if(!cfread(Demo_buf, Demo_buf_pos, 1, Demo_file)){\
112                         DEMO_ERROR(DEMO_ERROR_DISK_ACCESS);\
113                         break;\
114                 }\
115         }\
116  } while(0)
117 #define DEMO_DATA(vl, vl_size)          do { if(Demo_buf == NULL){ DEMO_ERROR(DEMO_ERROR_GENERAL); break; } if((Demo_buf_pos + vl_size) >= DEMO_BUF_SIZE){      Int3(); DEMO_ERROR(DEMO_ERROR_FRAMESIZE); break; } if(Game_mode & GM_DEMO_RECORD){ memcpy(Demo_buf + Demo_buf_pos, &vl, vl_size); } else if(Game_mode & GM_DEMO_PLAYBACK){ memcpy(&vl, Demo_buf + Demo_buf_pos, vl_size); } Demo_buf_pos += vl_size; } while(0)
118 #define DEMO_INT(vl)                                            do { DEMO_DATA(vl, sizeof(int)); } while(0)
119 #define DEMO_UINT(vl)                                   do { DEMO_DATA(vl, sizeof(uint)); } while(0)
120 #define DEMO_SHORT(vl)                                  do { DEMO_DATA(vl, sizeof(short)); } while(0)
121 #define DEMO_USHORT(vl)                                 do { DEMO_DATA(vl, sizeof(ushort)); } while(0)
122 #define DEMO_BYTE(vl)                                   do { DEMO_DATA(vl, sizeof(char)); } while(0)
123 #define DEMO_UBYTE(vl)                                  do { DEMO_DATA(vl, sizeof(ubyte)); } while(0)
124 #define DEMO_FLOAT(vl)                                  do { DEMO_DATA(vl, sizeof(float)); } while(0)
125 #define DEMO_VECTOR(vl)                                 do { DEMO_DATA(vl, sizeof(vector)); } while(0)
126 #define DEMO_MATRIX(vl)                                 do { DEMO_DATA(vl, sizeof(matrix)); } while(0)
127 #define DEMO_STRING(vl)                                 do { int stlen; if(Game_mode & GM_DEMO_RECORD){ stlen = strlen(vl); if(stlen <= 0){ break; }    DEMO_DATA(stlen, sizeof(ushort)); DEMO_DATA(*vl, strlen(vl)); } else { ushort len = 0; DEMO_USHORT(len); DEMO_DATA(*vl, len); vl[len] = '\0'; } } while(0)
128                 
129 // demo events types
130 #define DE_DUMP                                                 1                       // standard object dump
131 #define DE_TRAILER                                              2                       // end of demo trailer
132 #define DE_PRIMARY                                              3                       // primary weapon fired
133 #define DE_UNIQUE_MESSAGE                               4                       // unique hud message
134 #define DE_BUILTIN_MESSAGE                              5                       // builtin hud message
135 #define DE_OBJ_CREATE                                   6                       // object create message
136 #define DE_OBJ_WARPIN                                   7                       // ship warpin
137 #define DE_OBJ_WARPOUT                                  8                       // ship warpout
138 #define DE_OBJ_DEPARTED                                 9                       // ship departed
139 #define DE_SHIP_KILL                                            10                      // ship kill
140
141 // call this when posting an error
142 #define DEMO_ERROR(er)                                  do { Demo_error = er; Int3(); } while(0)
143
144 int Demo_make = 0;
145 DCF(demo, "")
146 {
147         Demo_make = !Demo_make;
148         if(Demo_make){
149                 dc_printf("Demo will be recorded\n");
150         } else {
151                 dc_printf("Demo will NOT be recorded\n");
152         }
153 }
154
155
156 // -----------------------------------------------------------------------------------------------------------------------------
157 // DEMO FORWARD DECLARATIONS
158 //
159
160 // write demo header
161 int demo_write_header();
162
163 // read the demo header
164 int demo_read_header();
165
166 // write the demo trailer
167 void demo_write_trailer();
168
169 // do a recording frame
170 void demo_do_recording_frame_start();
171
172 // do a recording frame
173 void demo_do_recording_frame_end();
174
175 // do a playback frame
176 void demo_do_playback_frame();
177
178 // seek through the demo file to the proper location
179 // return 0 on error, 1 on success/continue, 2 if the demo is done
180 int demo_playback_seek();
181
182 // scan through a read in frame of data and apply everything necessary. returns true if the trailer (end of demo) was found
183 int demo_playback_seek_sub(int frame_size);
184
185
186 // -----------------------------------------------------------------------------------------------------------------------------
187 // DEMO FUNCTIONS
188 //
189
190 // do frame for the demo - playback and recording, returns 0 if errors were encountered during frame processing
191 int demo_do_frame_start()
192 {
193 #ifndef DEMO_SYSTEM
194         return 1;
195 #else
196         // if we're not doing any demo stuff
197         if(!(Game_mode & GM_DEMO)){
198                 return 1;
199         }
200
201         // bad
202         if(Demo_file == NULL){          
203                 DEMO_ERROR(DEMO_ERROR_DISK_ACCESS);
204                 return 0;
205         }
206
207         // make sure we're not trying to record and playback at the same time
208         Assert( ((Game_mode & GM_DEMO_RECORD) && !(Game_mode & GM_DEMO_PLAYBACK)) || (!(Game_mode & GM_DEMO_RECORD) && (Game_mode & GM_DEMO_PLAYBACK)) );
209
210         // recording
211         if(Game_mode & GM_DEMO_RECORD){         
212                 demo_do_recording_frame_start();
213         } else {
214                 demo_do_playback_frame();
215         }       
216
217         // bad bad bad, get mwa
218         if(Demo_error){
219                 return 0;
220         }       
221
222         // continue
223         return 1;
224 #endif
225 }
226
227 // do frame for the demo - playback and recording, returns 0 if errors were encountered during frame processing
228 int demo_do_frame_end()
229 {
230 #ifndef DEMO_SYSTEM
231         return 1;
232 #else
233         // if we're not doing any demo stuff
234         if(!(Game_mode & GM_DEMO)){
235                 return 1;
236         }
237
238         // bad
239         if(Demo_file == NULL){          
240                 DEMO_ERROR(DEMO_ERROR_DISK_ACCESS);
241                 return 0;
242         }
243
244         // make sure we're not trying to record and playback at the same time
245         Assert( ((Game_mode & GM_DEMO_RECORD) && !(Game_mode & GM_DEMO_PLAYBACK)) || (!(Game_mode & GM_DEMO_RECORD) && (Game_mode & GM_DEMO_PLAYBACK)) );
246
247         // recording. there's nothing to do here for playback
248         if(Game_mode & GM_DEMO_RECORD){         
249                 demo_do_recording_frame_end();
250         }
251
252         // bad bad bad, get mwa
253         if(Demo_error){
254                 return 0;
255         }       
256
257         // continue
258         return 1;
259 #endif
260 }
261
262 // initialize a demo for recording
263 // NOTE : call this after loading the mission and going through the briefing, but _before_ physically moving into the mission
264 int demo_start_record(char *file)
265 {
266 #ifndef DEMO_SYSTEM
267         return 0;
268 #else
269         char full_name[MAX_FILENAME_LEN] = "";  
270
271         // try and allocate the buffer
272         Demo_buf = (char*)malloc(DEMO_BUF_SIZE);
273         if(Demo_buf == NULL){
274                 DEMO_ERROR(DEMO_ERROR_DISK_ACCESS);
275                 return 0;
276         }
277
278         // open the outfile
279         strcpy(full_name, file);
280         cf_add_ext(full_name, ".fsd");
281         Demo_file = cfopen(full_name, "wb", CFILE_NORMAL, CF_TYPE_DEMOS);
282         if(Demo_file == NULL){
283                 Int3();
284                 Demo_error = DEMO_ERROR_DISK_ACCESS;
285                 return 0;
286         }
287
288         // no errors
289         Demo_error = DEMO_ERROR_NONE;
290
291         // write the header
292         if(!demo_write_header()){
293                 return 0;
294         }
295
296         // flag demo mode
297         Game_mode |= GM_DEMO_RECORD;
298
299         // no events yet
300         Demo_frame_events = 0;
301
302         // success
303         return 1;
304 #endif
305 }
306
307 // initialize a demo for playback - calling this will load up the demo file and move the player into the playback state
308 int demo_start_playback(char *file)
309 {
310 #ifndef DEMO_SYSTEM
311         return 0;
312 #else
313         char full_name[MAX_FILENAME_LEN] = "";
314
315         // try and allocate the buffer
316         Demo_buf = (char*)malloc(DEMO_BUF_SIZE);
317         if(Demo_buf == NULL){           
318                 DEMO_ERROR(DEMO_ERROR_DISK_ACCESS);
319                 return 0;
320         }
321
322         // open the outfile
323         strcpy(full_name, file);
324         cf_add_ext(full_name, ".fsd");
325         Demo_file = cfopen(full_name, "rb", CFILE_NORMAL, CF_TYPE_DEMOS);
326         if(Demo_file == NULL){
327                 DEMO_ERROR(DEMO_ERROR_DISK_ACCESS);
328                 return 0;
329         }
330
331         // no errors
332         Demo_error = DEMO_ERROR_NONE;
333
334         // read the header
335         Demo_cur_offset = -1;
336         if(!demo_read_header()){
337                 return 0;
338         }
339
340         // flag demo mode
341         Game_mode |= GM_DEMO_PLAYBACK;  
342
343         // everything is cool, so jump into the mission
344         gameseq_post_event(GS_EVENT_ENTER_GAME);
345         return 1;
346 #endif
347 }
348
349 // finish the demo
350 void demo_close()
351 {
352 #ifdef DEMO_SYSTEM
353         // if we're recording, write the trailer
354         if(Game_mode & GM_DEMO_RECORD){
355                 demo_write_trailer();
356         }
357
358         // close the demo file
359         if(Demo_file != NULL){
360                 cfclose(Demo_file);
361                 Demo_file = NULL;
362         }
363
364         // free the buffer
365         if(Demo_buf != NULL){
366                 free(Demo_buf);
367                 Demo_buf = NULL;
368         }
369
370         // if we're playing back, go back to the main hall
371         if(Game_mode & GM_DEMO_PLAYBACK){
372                 gameseq_post_event(GS_EVENT_MAIN_MENU);
373         }
374
375         // unflag demo mode
376         Game_mode &= ~(GM_DEMO_RECORD | GM_DEMO_PLAYBACK);
377 #endif
378 }
379
380 // if we should run the simulation for this object, or let the demo system handle it
381 int demo_should_sim(object *objp)
382 {
383 #ifndef DEMO_SYSTEM
384         return 1;
385 #else
386         // always sim stuff in non-demo mode
387         if(!(Game_mode & GM_DEMO)){
388                 return 1;
389         }
390
391         // don't sim ships or missiles
392         if((objp->type == OBJ_SHIP) || ((objp->type == OBJ_WEAPON) && (objp->instance >= 0) && (Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE))){
393                 return 0;
394         }       
395
396         // sim everything else
397         return 1;
398 #endif
399 }
400
401
402 // -----------------------------------------------------------------------------------------------------------------------------
403 // DEMO RECORDING FUNCTIONS
404 //
405
406 // write demo header
407 int demo_write_header()
408 {
409         uint chksum;
410         char *full_name;
411
412         // write demo version #
413         if(!cfwrite_int(DEMO_VERSION, Demo_file)){
414                 DEMO_ERROR(DEMO_ERROR_DISK_ACCESS);
415                 return 0;
416         }
417
418         // write mission filename
419         if(!cfwrite_string_len(Game_current_mission_filename, Demo_file)){
420                 DEMO_ERROR(DEMO_ERROR_DISK_ACCESS);
421                 return 0;
422         }
423
424         // write mission checksum
425         full_name = cf_add_ext(Game_current_mission_filename, FS_MISSION_FILE_EXT);
426         cf_chksum_long(full_name, &chksum);
427         if(!cfwrite_int(chksum, Demo_file)){
428                 DEMO_ERROR(DEMO_ERROR_DISK_ACCESS);
429                 return 0;
430         }
431
432         // success
433         return 1;
434 }
435
436 // write the demo trailer
437 void demo_write_trailer()
438 {
439         // start a new chunk
440         demo_do_recording_frame_start();
441
442         // trailer event
443         ubyte frame_type = DE_TRAILER;
444         DEMO_UBYTE(frame_type);
445
446         // 1 event
447         Demo_frame_events++;
448
449         // write frame data to disk
450         DEMO_DATA_FRAME();
451 }
452
453 // start recording frame
454 void demo_do_recording_frame_start()
455 {
456         // if we're not physically doing the mission
457         if(gameseq_get_state() != GS_STATE_GAME_PLAY){
458                 return;
459         }
460
461         // add in frametime
462         Demo_missiontime += flFrametime;
463
464         // clear the buffer, set no events, and write the header
465         Demo_buf_pos = 0;
466         Demo_frame_events = 0;
467
468         // missiontime
469         float fl_time = f2fl(Missiontime);
470         DEMO_FLOAT(fl_time);    
471 }
472
473 // end recording frame
474 void demo_do_recording_frame_end()
475 {
476         // if we're not physically doing the mission
477         if(gameseq_get_state() != GS_STATE_GAME_PLAY){
478                 return;
479         }
480
481         // if its time to dump objects (the last thing we might dump per frame)
482         if((Demo_stamp == -1) || timestamp_elapsed(Demo_stamp)){
483                 // post an object dump event
484                 demo_POST_object_dump();
485
486                 // reset the stamp
487                 Demo_stamp = timestamp(Demo_fps);
488         }       
489
490         // write all accumulated frame data to disk if necessary
491         DEMO_DATA_FRAME();      
492 }
493
494 // post an object dump event
495 void demo_POST_object_dump()
496 {
497         ship_obj *sobjp;
498         object *objp;
499         ushort obj_count;       
500         ubyte team;
501
502         // object dump event
503         ubyte event_type = DE_DUMP;
504         DEMO_UBYTE(event_type);
505         
506         // go through the ship list and count
507         obj_count = 0;  
508         for ( sobjp = GET_FIRST(&Ship_obj_list); sobjp !=END_OF_LIST(&Ship_obj_list); sobjp = GET_NEXT(sobjp) ){
509                 // object pointer
510                 if(sobjp->objnum < 0){
511                         continue;
512                 }
513
514                 obj_count++;
515         }
516
517         // write out the object count
518         DEMO_USHORT(obj_count);
519
520         // go through the ship list and dump necessary stuff
521         for ( sobjp = GET_FIRST(&Ship_obj_list); sobjp !=END_OF_LIST(&Ship_obj_list); sobjp = GET_NEXT(sobjp) ){
522                 // object pointer
523                 if(sobjp->objnum < 0){
524                         continue;
525                 }
526                 objp = &Objects[sobjp->objnum];
527
528                 // just ships for now           
529                 DEMO_INT(objp->signature);
530                 DEMO_VECTOR(objp->pos);
531                 DEMO_MATRIX(objp->orient);
532                 DEMO_FLOAT(objp->phys_info.forward_thrust);     
533                 team = (ubyte)Ships[objp->instance].team;
534                 DEMO_UBYTE(team);
535                 DEMO_INT(Ships[objp->instance].flags);
536         }       
537
538         // up the event count
539         Demo_frame_events++;
540 }
541
542 // post a primary fired event
543 void demo_POST_primary_fired(object *objp, int banks, int linked)
544 {
545         ubyte fire_info = 0;
546
547         // object dump event
548         ubyte event_type = DE_PRIMARY;
549         DEMO_UBYTE(event_type);
550
551         // object signature
552         DEMO_INT(objp->signature);
553
554         // get fire info
555         fire_info = (ubyte)banks;
556         fire_info &= ~(1<<7);
557         if(linked){
558                 fire_info |= (1<<7);
559         } 
560         DEMO_UBYTE(fire_info);
561
562         // up the event count
563         Demo_frame_events++;
564 }
565
566 // post a unique message
567 void demo_POST_unique_message(char *id, char *who_from, int m_source, int priority)
568 {
569         // sanity
570         if((id == NULL) || (who_from == NULL) || (strlen(id) <= 0) || (strlen(who_from) <= 0)){
571                 return;
572         }
573
574         // write it
575         ubyte event = DE_UNIQUE_MESSAGE;
576         DEMO_UBYTE(event);
577         DEMO_STRING(id);
578         DEMO_STRING(who_from);
579         DEMO_INT(m_source);
580         DEMO_INT(priority);     
581
582         // up the event count
583         Demo_frame_events++;
584 }
585
586 // post a builtin message
587 void demo_POST_builtin_message(int type, ship *shipp, int priority, int timing)
588 {       
589         int sig = 0;
590
591         // write it
592         ubyte event = DE_BUILTIN_MESSAGE;
593         DEMO_UBYTE(event);
594         DEMO_INT(type);
595         if(shipp == NULL){
596                 sig = -1;
597         } else if(shipp->objnum >= 0){
598                 sig = Objects[shipp->objnum].signature;
599         }
600         DEMO_INT(sig);
601         DEMO_INT(priority);
602         DEMO_INT(timing);
603
604         // up the event count
605         Demo_frame_events++;
606 }
607
608 // post an object create message
609 void demo_POST_obj_create(char *pobj_name, int signature)
610 {
611         // write it
612         ubyte event = DE_OBJ_CREATE;
613         DEMO_UBYTE(event);
614         DEMO_STRING(pobj_name);
615         DEMO_INT(signature);
616         
617         // up the event count
618         Demo_frame_events++;
619 }
620
621 // post a warpin event
622 void demo_POST_warpin(int signature, int ship_flags)
623 {
624         // write it
625         ubyte event = DE_OBJ_WARPIN;    
626         DEMO_UBYTE(event);
627         DEMO_INT(signature);
628         DEMO_INT(ship_flags);
629         
630         // up the event count
631         Demo_frame_events++;
632 }
633
634 // post a warpout event
635 void demo_POST_warpout(int signature, int ship_flags)
636 {
637         // write it
638         ubyte event = DE_OBJ_WARPOUT;
639         DEMO_UBYTE(event);
640         DEMO_INT(signature);
641         DEMO_INT(ship_flags);
642         
643         // up the event count
644         Demo_frame_events++;
645 }
646
647 // post a departed event
648 void demo_POST_departed(int signature, int ship_flags)
649 {
650         // write it
651         ubyte event = DE_OBJ_DEPARTED;  
652         DEMO_UBYTE(event);
653         DEMO_INT(signature);
654         DEMO_INT(ship_flags);
655         
656         // up the event count
657         Demo_frame_events++;
658 }
659
660 // post a ship kill event
661 void demo_POST_ship_kill(object *objp)
662 {
663         // write it
664         ubyte event = DE_SHIP_KILL;     
665         DEMO_UBYTE(event);
666         DEMO_INT(objp->signature);      
667         
668         // up the event count
669         Demo_frame_events++;
670 }
671
672
673 // -----------------------------------------------------------------------------------------------------------------------------
674 // DEMO PLAYBACK FUNCTIONS
675 //
676
677 // read the demo header
678 int demo_read_header()
679 {
680         int version;
681         uint file_checksum, my_checksum;
682         char *full_name;
683
684         // read the version #
685         version = cfread_int(Demo_file);
686         if(version != DEMO_VERSION){
687                 DEMO_ERROR(DEMO_ERROR_VERSION);
688                 return 0;
689         }
690
691         // read mission filename
692         cfread_string_len(Game_current_mission_filename, MAX_FILENAME_LEN, Demo_file);
693
694         // get mission checksum
695         file_checksum = cfread_int(Demo_file);  
696         full_name = cf_add_ext(Game_current_mission_filename, FS_MISSION_FILE_EXT);
697         if(!cf_chksum_long(full_name, &my_checksum)){
698                 DEMO_ERROR(DEMO_ERROR_DISK_ACCESS);
699                 return 0;
700         }
701         if(file_checksum != my_checksum){
702                 DEMO_ERROR(DEMO_ERROR_MISSION);
703                 return 0;
704         }
705
706         // get the file offset of this first frame
707         Demo_cur_offset = cftell(Demo_file);
708
709         // success
710         return 1;
711 }
712
713 // do a playback frame
714 void demo_do_playback_frame()
715 {
716         // seek to the best location in the demo file
717         switch(demo_playback_seek()){
718         // error
719         case 0:
720                 return;
721
722         // continue
723         case 1:         
724                 break;
725
726         // found the trailer - this demo is done
727         case 2:
728                 demo_close();
729                 break;  
730         }
731 }
732
733 // seek through the demo file to the proper location
734 // return 0 on error, 1 on success/continue, 2 if the demo is done
735 int demo_playback_seek()
736 {
737         float this_time = 0.0f;
738         int frame_size;
739         int this_offset;
740         int ret = 0;    
741
742         // scan until we find something useful
743         while(1){
744                 // record the beginning of this chunk
745                 this_offset = cftell(Demo_file);
746
747                 // read in the data for the next frame          
748                 DEMO_DATA_FRAME();
749                 frame_size = Demo_buf_pos;
750                 Demo_buf_pos = 0;
751
752                 // check the time               
753                 DEMO_FLOAT(this_time);
754
755                 // ahead of us
756                 if(this_time > f2fl(Missiontime)){
757                         // success
758                         ret = 1;
759
760                         // seek back to the beginning of this chunk
761                         Demo_cur_offset = this_offset;
762
763                         // bust out
764                         break;
765                 }
766                 // we should scan through this chunk
767                 else {
768                         // returns true if it finds the demo trailer
769                         if(demo_playback_seek_sub(frame_size)){
770                                 return 2;
771                         }
772                 }
773         }
774
775         // seek back to the new frame
776         cfseek(Demo_file, Demo_cur_offset, CF_SEEK_SET);
777
778         // return
779         return ret;
780 }
781
782 // scan through a read in frame of data and apply everything necessary. returns true if the trailer (end of demo) was found
783 int demo_playback_seek_sub(int frame_size)
784 {
785         ubyte event = 0;
786         int idx;        
787
788         // apply everything
789         while(Demo_buf_pos < frame_size){
790                 // next event
791                 DEMO_UBYTE(event);
792
793                 // process it
794                 switch(event){
795                 // oops, trailer. we're done
796                 case DE_TRAILER:
797                         return 1;
798
799                 // object dump
800                 case DE_DUMP: {
801                         ushort obj_count = 0;
802                         int obj_sig = 0;
803                         ubyte team = 0;
804                         vector obj_pos = vmd_zero_vector;
805                         matrix obj_orient = vmd_identity_matrix;
806                         float obj_fthrust = 0;
807                         int ship_index = 0;
808                         int ship_flags = 0;
809
810                         // get the object count
811                         DEMO_USHORT(obj_count);
812
813                         // read in all the objects
814                         for(idx=0; idx<obj_count; idx++){
815                                 DEMO_INT(obj_sig);
816                                 DEMO_VECTOR(obj_pos);
817                                 DEMO_MATRIX(obj_orient);
818                                 DEMO_FLOAT(obj_fthrust);
819                                 DEMO_UBYTE(team);
820                                 DEMO_INT(ship_flags);
821
822                                 // find our ship object
823                                 ship_index = ship_get_by_signature(obj_sig);
824                                 // Assert(ship_index >= 0);
825                                 if(ship_index >= 0){
826                                         Objects[Ships[ship_index].objnum].pos = obj_pos;
827                                         Objects[Ships[ship_index].objnum].orient = obj_orient;
828                                         Objects[Ships[ship_index].objnum].phys_info.forward_thrust = obj_fthrust;
829                                         Ships[ship_index].team = team;
830                                         Ships[ship_index].flags = ship_flags;
831                                 }
832                         }               
833                         }
834                         break;                                            
835
836                 // primary fired
837                 case DE_PRIMARY: {
838                         int obj_sig = 0 ;
839                         ubyte fire_info = 0;
840                         int ship_index = -1;
841
842                         // get the data and ship
843                         DEMO_INT(obj_sig);
844                         DEMO_UBYTE(fire_info);
845                         ship_index = ship_get_by_signature(obj_sig);
846                         if(ship_index >= 0){
847                                 Ships[ship_index].weapons.current_primary_bank = (int)(fire_info & ~(1<<7));
848                                 if(fire_info & (1<<7)){
849                                         Ships[ship_index].flags |= SF_PRIMARY_LINKED;
850                                 } else {
851                                         Ships[ship_index].flags &= ~(SF_PRIMARY_LINKED);
852                                 }
853
854                                 // fire
855                                 ship_fire_primary(&Objects[Ships[ship_index].objnum], 0, 1);
856                         }
857                         }
858                         break;
859
860                 // unique message
861                 case DE_UNIQUE_MESSAGE: {
862                         char id[255] = "";
863                         char who_from[255] = "";
864                         int m_source = 0;
865                         int priority = 0;
866
867                         // read the stuff in
868                         DEMO_STRING(id);
869                         DEMO_STRING(who_from);
870                         DEMO_INT(m_source);
871                         DEMO_INT(priority);
872                         
873                         // send the message
874                         message_send_unique_to_player(id, who_from, m_source, priority, 0, 0);
875                         }
876                         break;
877
878                 // builtin message
879                 case DE_BUILTIN_MESSAGE:{
880                         int type = 0;
881                         int sig = 0;
882                         int ship_index = 0;
883                         int priority = 0;
884                         int timing = 0;
885
886                         // get data and ship
887                         DEMO_INT(type);
888                         DEMO_INT(sig);
889                         DEMO_INT(priority);
890                         DEMO_INT(timing);
891                         if(sig == -1){
892                                 // message_send_builtin_to_player(type, NULL, priority, timing, 0, 0);
893                         } else {
894                                 ship_index = ship_get_by_signature(sig);
895                                 if(ship_index >= 0){
896                                         // message_send_builtin_to_player(type, &Ships[ship_index], priority, timing, 0, 0);
897                                 }
898                         }
899                         }
900                         break;
901
902                 // obj create
903                 case DE_OBJ_CREATE:{
904                         char pobj_name[255] = "";
905                         int obj_sig = 0;
906                         int objnum = 0;
907                         p_object *objp = NULL;
908
909                         // try and create the ship
910                         DEMO_STRING(pobj_name);
911                         DEMO_INT(obj_sig);
912                         
913                         objp = mission_parse_get_arrival_ship( pobj_name );
914                         if(objp != NULL){
915                                 objnum = parse_create_object(objp);
916                                 if(objnum >= 0){
917                                         Objects[objnum].signature = obj_sig;
918                                 }
919                         }
920                         }
921                         break;
922
923                 // warpin effect
924                 case DE_OBJ_WARPIN:{
925                         int obj_sig = 0;
926                         int ship_flags = 0;
927                         int ship_index = 0;
928
929                         // get the data and ship
930                         DEMO_INT(obj_sig);
931                         DEMO_INT(ship_flags);
932                         ship_index = ship_get_by_signature(obj_sig);
933                         if(ship_index >= 0){
934                                 Ships[ship_index].flags = ship_flags;
935                                 shipfx_warpin_start(&Objects[Ships[ship_index].objnum]);
936                         }
937                         }
938                         break;
939
940                 // warpout effect
941                 case DE_OBJ_WARPOUT:{
942                         int obj_sig = 0;
943                         int ship_flags = 0;
944                         int ship_index = 0;
945
946                         // get the data and ship
947                         DEMO_INT(obj_sig);
948                         DEMO_INT(ship_flags);
949                         ship_index = ship_get_by_signature(obj_sig);
950                         if(ship_index >= 0){
951                                 Ships[ship_index].flags = ship_flags;
952                                 shipfx_warpout_start(&Objects[Ships[ship_index].objnum]);
953                         }
954                         }
955                         break;
956
957                 // departed
958                 case DE_OBJ_DEPARTED:{
959                         int obj_sig = 0;
960                         int ship_flags = 0;
961                         int ship_index = 0;
962
963                         // get the data and ship
964                         DEMO_INT(obj_sig);
965                         DEMO_INT(ship_flags);
966                         ship_index = ship_get_by_signature(obj_sig);
967                         if(ship_index >= 0){
968                                 Ships[ship_index].flags = ship_flags;
969                                 ship_departed(ship_index);
970                         }
971                         }
972                         break;
973
974                 // ship kill
975                 case DE_SHIP_KILL:{
976                         int obj_sig = 0;                        
977                         int ship_index = 0;
978
979                         // get the data and ship
980                         DEMO_INT(obj_sig);                      
981                         ship_index = ship_get_by_signature(obj_sig);
982                         if(ship_index >= 0){                            
983                                 Objects[Ships[ship_index].objnum].hull_strength = 0.0f;
984                                 ship_generic_kill_stuff(&Objects[Ships[ship_index].objnum], 1.0f);
985                         }
986                         }
987                         break;
988
989                 default:
990                         Int3();
991                         return 0;
992                 }
993         }
994
995         return 0;
996 }