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