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