]> icculus.org git repositories - btb/d2x.git/blob - main/newdemo.c
function prototypes
[btb/d2x.git] / main / newdemo.c
1 /* $Id: newdemo.c,v 1.15 2003-11-26 12:26:31 btb Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14
15 /*
16  *
17  * Code to make a complete demo playback system.
18  *
19  * Old Log:
20  * Revision 1.12  1995/10/31  10:19:43  allender
21  * shareware stuff
22  *
23  * Revision 1.11  1995/10/17  13:19:16  allender
24  * close boxes for demo save
25  *
26  * Revision 1.10  1995/10/05  10:36:07  allender
27  * fixed calls to digi_play_sample to account for differing volume and angle calculations
28  *
29  * Revision 1.9  1995/09/12  15:49:13  allender
30  * define __useAppleExts__ if not already defined
31  *
32  * Revision 1.8  1995/09/05  14:28:32  allender
33  * fixed previous N_players bug in newdemo_goto_end
34  *
35  * Revision 1.7  1995/09/05  14:16:51  allender
36  * added space to allowable demo filename characters
37  * and fixed bug with netgame demos N_players got getting
38  * set correctly
39  *
40  * Revision 1.6  1995/09/01  16:10:47  allender
41  * fixed bug with reading in N_players variable on
42  * netgame demo playback
43  *
44  * Revision 1.5  1995/08/24  16:04:11  allender
45  * fix signed byte problem
46  *
47  * Revision 1.4  1995/08/12  12:21:59  allender
48  * made call to create_shortpos not swap the shortpos
49  * elements
50  *
51  * Revision 1.3  1995/08/01  16:04:19  allender
52  * made random picking of demo work
53  *
54  * Revision 1.2  1995/08/01  13:56:56  allender
55  * demo system working on the mac
56  *
57  * Revision 1.1  1995/05/16  15:28:59  allender
58  * Initial revision
59  *
60  * Revision 2.7  1995/05/26  16:16:06  john
61  * Split SATURN into define's for requiring cd, using cd, etc.
62  * Also started adding all the Rockwell stuff.
63  *
64  * Revision 2.6  1995/03/21  14:39:38  john
65  * Ifdef'd out the NETWORK code.
66  *
67  * Revision 2.5  1995/03/14  18:24:31  john
68  * Force Destination Saturn to use CD-ROM drive.
69  *
70  * Revision 2.4  1995/03/14  16:22:29  john
71  * Added cdrom alternate directory stuff.
72  *
73  * Revision 2.3  1995/03/10  12:58:33  allender
74  * only display rear view cockpit when cockpit mode was CM_FULL_COCKPIT.
75  *
76  * Revision 2.2  1995/03/08  16:12:15  allender
77  * changes for Destination Saturn
78  *
79  * Revision 2.1  1995/03/08  12:11:26  allender
80  * fix shortpos reading
81  *
82  * Revision 2.0  1995/02/27  11:29:40  john
83  * New version 2.0, which has no anonymous unions, builds with
84  * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
85  *
86  * Revision 1.189  1995/02/22  14:53:42  allender
87  * missed some anonymous stuff
88  *
89  * Revision 1.188  1995/02/22  13:24:53  john
90  * Removed the vecmat anonymous unions.
91  *
92  * Revision 1.187  1995/02/22  13:13:54  allender
93  * remove anonymous unions from object structure
94  *
95  * Revision 1.186  1995/02/14  15:36:41  allender
96  * fix fix for morph effect
97  *
98  * Revision 1.185  1995/02/14  11:25:48  allender
99  * save cockpit mode and restore after playback.  get orientation for morph
100  * effect when object is morph vclip
101  *
102  * Revision 1.184  1995/02/13  12:18:14  allender
103  * change to decide about interpolating or not
104  *
105  * Revision 1.183  1995/02/12  00:46:23  adam
106  * don't decide to skip frames until after at least 10 frames have
107  * passed -- allender
108  *
109  * Revision 1.182  1995/02/11  22:34:01  john
110  * Made textures page in for newdemos before playback time.
111  *
112  * Revision 1.181  1995/02/11  17:28:32  allender
113  * strip frames from end of demo
114  *
115  * Revision 1.180  1995/02/11  16:40:35  allender
116  * start of frame stripping debug code
117  *
118  * Revision 1.179  1995/02/10  17:40:06  allender
119  * put back in wall_hit_process code to fix door problem
120  *
121  * Revision 1.178  1995/02/10  17:17:24  adam
122  * allender } fix
123  *
124  * Revision 1.177  1995/02/10  17:16:24  allender
125  * fix possible tmap problems
126  *
127  * Revision 1.176  1995/02/10  15:54:37  allender
128  * changes for out of space on device.
129  *
130  * Revision 1.175  1995/02/09  19:55:00  allender
131  * fix bug with morph recording -- force rendertype to RT_POLYOBJ when
132  * playing back since it won't render until fully morphed otherwise
133  *
134  * Revision 1.174  1995/02/07  17:15:35  allender
135  * DOH!!!!!
136  *
137  * Revision 1.173  1995/02/07  17:14:21  allender
138  * immediately return when loading bogus level stuff when reading a frame
139  *
140  * Revision 1.172  1995/02/02  11:15:03  allender
141  * after loading new level, read next frame (forward or back) always because
142  * of co-op ships showing up when level is loaded
143  *
144  * Revision 1.171  1995/02/02  10:24:16  allender
145  * removed cfile stuff.  Use standard FILE functions for demo playback
146  *
147  * Revision 1.170  1995/01/30  13:54:32  allender
148  * support for missions
149  *
150  * Revision 1.169  1995/01/27  16:27:35  allender
151  * put game mode to demo_game_mode when sorting multiplayer kill (and score)
152  * list
153  *
154  * Revision 1.168  1995/01/27  09:52:25  allender
155  * minor changes because of object/segment linking problems
156  *
157  * Revision 1.167  1995/01/27  09:22:28  allender
158  * changed way multi-player score is recorded.  Record difference, not
159  * actual
160  *
161  * Revision 1.166  1995/01/25  14:32:44  allender
162  * changed with recorded player flags.  More checks for paused state
163  * during interpolation reading of objects
164  *
165  * Revision 1.165  1995/01/25  11:23:32  allender
166  * found bug with out of disk space problem
167  *
168  * Revision 1.164  1995/01/25  11:11:33  allender
169  * coupla' things.  Fix problem with objects apparently being linked in
170  * the wrong segment.  Put an Int3 in to check why demos will write to
171  * end of space on drive. close demo file if demo doens't start playing
172  * back.  Put obj->type == OBJ_ROBOT around checking for boss cloaking
173  *
174  * Revision 1.163  1995/01/24  19:44:30  allender
175  * fix obscure problem with rewinding and having the wrong object linked
176  * to the wrong segments.  will investigate further.
177  *
178  * Revision 1.162  1995/01/23  09:31:28  allender
179  * add team score in team mode playback
180  *
181  * Revision 1.161  1995/01/20  22:47:39  matt
182  * Mission system implemented, though imcompletely
183  *
184  * Revision 1.160  1995/01/20  09:30:37  allender
185  * don't call LoadLevel with bogus data
186  *
187  * Revision 1.159  1995/01/20  09:13:23  allender
188  * *&^%&*%$ typo
189  *
190  * Revision 1.158  1995/01/20  09:12:04  allender
191  * record team names during demo recoring in GM_TEAM
192  *
193  * Revision 1.157  1995/01/19  16:31:09  allender
194  * forgot to bump demo version for new weapon change stuff
195  *
196  * Revision 1.156  1995/01/19  16:29:33  allender
197  * added new byte for weapon change (old weapon) so rewinding works
198  * correctly for weapon changes in registered
199  *
200  * Revision 1.155  1995/01/19  15:00:05  allender
201  * remove code to take away blastable walls in multiplayer demo playbacks
202  *
203  * Revision 1.154  1995/01/19  11:07:05  allender
204  * put in psuedo cloaking for boss robots.  Problem is that cloaking is
205  * time based, and that don't get done in demos, so bosses just disappear.
206  * oh well
207  *
208  * Revision 1.153  1995/01/19  09:42:29  allender
209  * record laser levels in demos
210  *
211  * Revision 1.152  1995/01/18  20:43:12  allender
212  * fix laser level stuff on goto-beginning and goto-end
213  *
214  * Revision 1.151  1995/01/18  20:28:18  allender
215  * cloak robots now cloak (except maybe for boss........)  Put in function
216  * to deal with control center triggers
217  *
218  * Revision 1.150  1995/01/18  18:55:07  allender
219  * bug fix
220  *
221  * Revision 1.149  1995/01/18  18:49:03  allender
222  * lots 'o stuff....record laser level.  Record beginning of door opening
223  * sequence.  Fix some problems with control center stuff.  Control center
224  * triggers now work in reverse
225  *
226  * Revision 1.148  1995/01/18  08:51:40  allender
227  * forgot to record ammo counts at beginning of demo
228  *
229  * Revision 1.147  1995/01/17  17:42:07  allender
230  * added primary and secondary ammo counts.  Changed goto_end routine
231  * to be more efficient
232  *
233  * Revision 1.146  1995/01/17  13:46:35  allender
234  * fix problem with destroyed control center and rewinding a demo.
235  * Save callsign and restore after demo playback
236  *
237  * Revision 1.145  1995/01/12  10:21:53  allender
238  * fixes for 1.0 to 1.1 demo incompatibility
239  *
240  * Revision 1.144  1995/01/05  13:51:43  allender
241  * fixed type of player num variable
242  *
243  * Revision 1.143  1995/01/04  16:58:28  allender
244  * bumped up demo version number
245  *
246  * Revision 1.142  1995/01/04  14:59:02  allender
247  * added more information to end of demo for registered.  Forced game mode
248  * to be GM_NORMAL on demo playback
249  *
250  * Revision 1.141  1995/01/03  17:30:47  allender
251  * fixed logic problem with cloak stuf
252  *
253  * Revision 1.140  1995/01/03  17:12:23  allender
254  * fix for getting cloak stuff at end of demo for shareware
255  *
256  * Revision 1.139  1995/01/03  15:20:24  allender
257  * fix goto_end for shareware -- changes to goto_end for registered
258  *
259  * Revision 1.138  1995/01/03  13:13:26  allender
260  * add } I forgot
261  *
262  * Revision 1.137  1995/01/03  13:10:29  allender
263  * make score work forwards and backwards
264  *
265  * Revision 1.136  1995/01/03  11:45:20  allender
266  * added code to record players scores
267  *
268  * Revision 1.135  1994/12/30  10:03:57  allender
269  * put cloak stuff at end of demo for fast forward to the end
270  *
271  * Revision 1.134  1994/12/29  17:02:55  allender
272  * spelling fix on SHAREWARE
273  *
274  * Revision 1.133  1994/12/29  16:43:41  allender
275  * lots of new multiplayer stuff.  wrapped much code with SHAREWARE defines
276  *
277  * Revision 1.132  1994/12/28  14:15:01  allender
278  * added routines to deal with connecting and reconnecting players when
279  * recording multiplayer demos
280  *
281  * Revision 1.131  1994/12/21  12:57:59  allender
282  * bug fix
283  *
284  * Revision 1.130  1994/12/21  12:46:53  allender
285  * record multi player deaths and kills
286  *
287  * Revision 1.129  1994/12/19  16:37:27  allender
288  * pick good filename when trying to save in network play and player
289  * gets bumped out of menu by multi-player code
290  *
291  * Revision 1.128  1994/12/14  10:49:01  allender
292  * reset bad_read variable when starting demo playback
293  *
294  * Revision 1.127  1994/12/14  08:53:06  allender
295  * lowered watermark for out of space
296  *
297  * Revision 1.126  1994/12/14  08:49:52  allender
298  * put up warning when starting demo recording if not enough space and
299  * not let them record
300  *
301  * Revision 1.125  1994/12/13  00:01:37  allender
302  * CLOAK FIX -- (I'm tempted to take cloak out of game because I can't
303  * seem to get it right in demo playback)
304  *
305  * Revision 1.124  1994/12/12  14:51:21  allender
306  * more fixed to multiplayer cloak stuff
307  *
308  * Revision 1.123  1994/12/12  11:33:11  allender
309  * fixed rearview mode to work again
310  *
311  * Revision 1.122  1994/12/12  11:00:16  matt
312  * Added code to handle confusion with attached objects
313  *
314  * Revision 1.121  1994/12/12  00:31:29  allender
315  * give better warning when out of space when recording.  Don't record
316  * until no space left.  We have 500K watermark when we now stop recording
317  *
318  * Revision 1.120  1994/12/10  16:44:54  matt
319  * Added debugging code to track down door that turns into rock
320  *
321  * Revision 1.119  1994/12/09  18:46:15  matt
322  * Added code to handle odd error condition
323  *
324  * Revision 1.118  1994/12/09  17:27:37  allender
325  * force playernum to 0 when demo is done playing
326  *
327  * Revision 1.117  1994/12/09  16:40:39  allender
328  * yet more cloak stuff.  Assign cloak/invuln time when starting demo
329  * if flags are set.  Check cloak and invuln time when demo
330  * even when paused
331  *
332  * Revision 1.116  1994/12/09  14:59:22  matt
333  * Added system to attach a fireball to another object for rendering purposes,
334  * so the fireball always renders on top of (after) the object.
335  *
336  * Revision 1.115  1994/12/09  12:21:45  allender
337  * only allow valid chars when typing in demo filename
338  *
339  * Revision 1.114  1994/12/08  23:19:02  allender
340  * final(?) fix for getting cloak gauge to work on demo playback
341  * with forward and reverse
342  *
343  * Revision 1.113  1994/12/08  21:34:38  allender
344  * record old and new player flags to accuratedly record cloaking and
345  * decloaking
346  * ./
347  *
348  * Revision 1.112  1994/12/08  18:04:47  allender
349  * bashed playernum right after reading it in demo header so shields
350  * and energy are put in right place
351  *
352  * Revision 1.111  1994/12/08  17:10:07  allender
353  * encode playernum in demo header.  Bash viewer segment to 0 if in
354  * bogus segnum.  Don't link render objs for same reason
355  *
356  * Revision 1.110  1994/12/08  15:36:12  allender
357  * cloak stuff works forwards and backwards
358  *
359  * Revision 1.109  1994/12/08  13:46:03  allender
360  * don't record rearview anymore, but leave in case statement for playback
361  * purposes.  change the way letterbox <--> cockpit transitions happen
362  *
363  * Revision 1.108  1994/12/08  12:36:06  matt
364  * Added new object allocation & deallocation functions so other code
365  * could stop messing around with internal object data structures.
366  *
367  * Revision 1.107  1994/12/08  11:19:04  allender
368  * handle out of space (more) gracefully then before
369  *
370  * Revision 1.106  1994/12/08  00:29:49  allender
371  * fixed bug that didn't load level on goto_beginning
372  *
373  * Revision 1.105  1994/12/08  00:11:51  mike
374  * change matrix interpolation.
375  *
376  * Revision 1.104  1994/12/07  23:46:37  allender
377  * changed invulnerability and cloak to work (almost) correctly both
378  * in single and multi player
379  *
380  * Revision 1.103  1994/12/07  11:48:49  adam
381  * BY ALLENDER -- added dampening of interpolation factor to 1 if greater
382  * than 1 (although I have not seen this happen).  this is attempt to
383  * get wobbling problem solved
384  *
385  * Revision 1.102  1994/12/07  11:23:56  allender
386  * attempt at getting rid of wobbling on demo playback
387  *
388  * Revision 1.101  1994/12/06  19:31:17  allender
389  * moved blastable wall stuff code to where we load level during demo
390  * playback
391  *
392  * Revision 1.100  1994/12/06  19:21:51  allender
393  * multi games, destroy blastable walls.  Do wall toggle when control center
394  * destroyed
395  *
396  * Revision 1.99  1994/12/06  16:54:48  allender
397  * fixed code so if demo automatically started from menu, don't bring up
398  * message if demo is too old
399  *
400  * Revision 1.98  1994/12/06  13:55:15  matt
401  * Use new rounding func, f2ir()
402  *
403  * Revision 1.97  1994/12/06  13:44:45  allender
404  * suppressed compiler warnings
405  *
406  * Revision 1.96  1994/12/06  13:38:03  allender
407  * removed recording of wall hit process.  I think that all bases are covered
408  * elsewhere
409  *
410  * Revision 1.95  1994/12/06  12:57:35  allender
411  * added recording of multi_decloaking.  Fixed some other cloaking code so
412  * that cloak should last as long as player was cloaked.  We will lose the
413  * guage effect, but the time is probably more important on playback
414  *
415  * Revision 1.94  1994/12/05  23:37:17  matt
416  * Took out calls to warning() function
417  *
418  * Revision 1.93  1994/12/03  17:52:04  yuan
419  * Localization 380ish
420  *
421  * Revision 1.92  1994/12/02  12:53:39  allender
422  * fixed goto_beginning and goto_end on demo playback
423  *
424  * Revision 1.91  1994/12/01  12:01:49  allender
425  * added multi player cloak stuff
426  *
427  * Revision 1.90  1994/11/30  09:33:58  allender
428  * added field in header to tell what version (shareware or registered)
429  * demo was recorded with.  Don't allow demo recorded on one to playback
430  * on the other
431  *
432  * Revision 1.89  1994/11/29  00:31:01  allender
433  * major changes -- added level recording feature which records level
434  * advancement.  Changes to internal code to handle this.
435  *
436  * Revision 1.88  1994/11/27  23:13:54  matt
437  * Made changes for new mprintf calling convention
438  *
439  * Revision 1.87  1994/11/27  23:07:35  allender
440  * starting on code to get all level transitions recorded. not done yet
441  *
442  * Revision 1.86  1994/11/27  17:39:47  matt
443  * Don't xlate tmap numbers when editor compiled out
444  *
445  * Revision 1.85  1994/11/23  09:27:21  allender
446  * put up info box with message if demo version is too old or level
447  * cannot be loaded
448  *
449  * Revision 1.84  1994/11/22  19:37:39  allender
450  * fix array mistake
451  *
452  * Revision 1.83  1994/11/22  19:35:09  allender
453  * record player ship colors in multiplayer demo recordings
454  *
455  * Revision 1.82  1994/11/19  15:36:42  mike
456  * fix fix.
457  *
458  * Revision 1.81  1994/11/19  15:23:21  mike
459  * rip out unused code
460  *
461  * Revision 1.80  1994/11/16  14:51:49  rob
462  * Fixed network/demo incompatibility.
463  *
464  * Revision 1.79  1994/11/15  10:55:48  allender
465  * made start of demo playback read initial demo information so
466  * level will get loaded.  Made demo record to single file which
467  * will get renamed.  Added numerics after old filename so
468  * sequential filenames would be defaulted to
469  *
470  * Revision 1.78  1994/11/15  09:46:06  allender
471  * added versioning.  Fixed problems with trying to interpolating a completely
472  * 0 orientation matrix
473  *
474  * Revision 1.77  1994/11/14  14:34:31  matt
475  * Fixed up handling when textures can't be found during remap
476  *
477  * Revision 1.76  1994/11/14  09:15:29  allender
478  * make ESC from file save menu exit w/o saving.  Fix letterbox, rear view,
479  * to normal cockpit mode transition to work correctly when skipping and
480  * interpolating frames
481  *
482  * Revision 1.75  1994/11/11  16:22:07  allender
483  * made morphing objects record only the object being morphed.
484  *
485  * Revision 1.74  1994/11/08  14:59:19  john
486  * Added code to respond to network while in menus.
487  *
488  * Revision 1.73  1994/11/08  14:52:20  adam
489  * *** empty log message ***
490  *
491  * Revision 1.72  1994/11/07  15:47:04  allender
492  * prompt for filename when done recording demo
493  *
494  * Revision 1.71  1994/11/07  11:47:19  allender
495  * when interpolating frames, delete weapon, fireball, and debris objects
496  * from an inpolated frame if they don't appear in the next recorded
497  * frame
498  *
499  * Revision 1.70  1994/11/07  11:02:41  allender
500  * more with interpolation. I believe that I have it right now
501  *
502  * Revision 1.69  1994/11/07  08:47:40  john
503  * Made wall state record.
504  *
505  * Revision 1.68  1994/11/05  17:22:51  john
506  * Fixed lots of sequencing problems with newdemo stuff.
507  *
508  * Revision 1.67  1994/11/04  20:11:52  john
509  * Neatening up palette stuff with demos.
510  *
511  * Revision 1.66  1994/11/04  16:49:44  allender
512  * changed newdemo_do_interpolate to default to on
513  *
514  * Revision 1.65  1994/11/04  16:44:51  allender
515  * added filename support for demo recording.  more auto demo stuff
516  *
517  * Revision 1.64  1994/11/04  13:05:31  allender
518  * fixing the lifeleft variable again.  (I think I got it right this time)
519  *
520  * Revision 1.63  1994/11/04  11:37:37  allender
521  * commented out fprintfs and fixed compiler warning
522  *
523  * Revision 1.62  1994/11/04  11:33:50  allender
524  * added OBJ_FLARE and OBJ_LIGHT to obj->lifeleft recording
525  *
526  * Revision 1.61  1994/11/04  11:29:21  allender
527  * more interpolation stuff -- not done yet.  Fixed so hostage vclips
528  * render correctly.  Changed lifeleft to full precision, but only
529  * write it when object is fireball or weapon type of object
530  *
531  * Revision 1.60  1994/11/03  10:00:11  allender
532  * fixed divide by zero in calculating render time.  more interpolation
533  * stuff which isn't quite done
534  *
535  * Revision 1.59  1994/11/02  17:10:59  allender
536  * never play recorded frames when interpolation is occuring
537  *
538  * Revision 1.58  1994/11/02  14:28:58  allender
539  * profile total playback time and average frame render time
540  *
541  * Revision 1.57  1994/11/02  14:09:03  allender
542  * record rear view.  start of playback interpolation code -- this
543  * is not yet done
544  *
545  * Revision 1.56  1994/11/01  13:25:30  allender
546  * drop frames if playing back demo on slower machine
547  *
548  * Revision 1.55  1994/10/31  16:10:40  allender
549  * record letterbox mode on death seq, and then restore
550  *
551  * Revision 1.54  1994/10/29  16:01:38  allender
552  * added ND_STATE_NODEMOS to indicate that there are no demos currently
553  * available for playback
554  *
555  * Revision 1.53  1994/10/29  15:38:42  allender
556  * in newdemo_start_playback, make Newdemo_at_eof = 0
557  *
558  * Revision 1.52  1994/10/28  14:45:28  john
559  * fixed typo from last checkin.
560  *
561  * Revision 1.51  1994/10/28  14:42:55  john
562  * Added sound volumes to all sound calls.
563  *
564  * Revision 1.50  1994/10/28  14:31:57  allender
565  * homing missle and autodemo stuff
566  *
567  * Revision 1.49  1994/10/28  12:42:14  allender
568  * record homing distance
569  *
570  * Revision 1.48  1994/10/27  16:57:54  allender
571  * changed demo vcr to be able to play any number of frames by storing
572  * frame length (in bytes) in the demo file.  Added blowing up monitors
573  *
574  * Revision 1.47  1994/10/26  16:50:50  allender
575  * put two functions inside of VCR_MODE ifdef
576  *
577  * Revision 1.46  1994/10/26  15:20:32  allender
578  * added CT_REMOTE as valid control type for recording
579  *
580  * Revision 1.45  1994/10/26  14:45:35  allender
581  * completed hacked in vcr demo playback stuff
582  *
583  * Revision 1.44  1994/10/26  13:40:52  allender
584  * vcr playback of demo stuff
585  *
586  * Revision 1.43  1994/10/26  08:51:57  allender
587  * record player weapon change
588  *
589  * Revision 1.42  1994/10/25  15:48:01  allender
590  * add shields, energy, and player flags to demo recording.
591  * ,
592  *
593  * Revision 1.41  1994/10/24  08:19:35  allender
594  * fixed compilation errors
595  *
596  * Revision 1.40  1994/10/23  19:17:08  matt
597  * Fixed bug with "no key" messages
598  *
599  * Revision 1.39  1994/10/22  14:15:08  mike
600  * Suppress compiler warnings.
601  *
602  * Revision 1.38  1994/10/21  15:24:55  allender
603  * compressed writing of object structures with specialized code
604  * to write out only pertinent object structures.
605  *
606  * Revision 1.37  1994/10/20  13:03:17  matt
607  * Replaced old save files (MIN/SAV/HOT) with new LVL files
608  *
609  * Revision 1.36  1994/09/28  23:13:10  matt
610  * Macroized palette flash system
611  *
612  * Revision 1.35  1994/09/26  17:28:32  matt
613  * Made new multiple-object morph code work with the demo system
614  *
615  * Revision 1.34  1994/09/10  13:31:54  matt
616  * Made exploding walls a type of blastable walls.
617  * Cleaned up blastable walls, making them tmap2 bitmaps.
618  *
619  * Revision 1.33  1994/08/15  18:05:28  john
620  * *** empty log message ***
621  *
622  * Revision 1.32  1994/08/15  17:56:38  john
623  * ,
624  *
625  * Revision 1.31  1994/08/10  09:44:54  john
626  * *** empty log message ***
627  *
628  * Revision 1.30  1994/07/22  12:35:48  matt
629  * Cleaned up editor/game interactions some more.
630  *
631  * Revision 1.29  1994/07/21  13:06:45  matt
632  * Ripped out remants of old demo system, and added demo only system that
633  * disables object movement and game options from menu.
634  *
635  * Revision 1.28  1994/07/18  16:22:44  john
636  * Made all file read/writes call the same routine.
637  *
638  * Revision 1.27  1994/07/14  22:38:27  matt
639  * Added exploding doors
640  *
641  * Revision 1.26  1994/07/05  12:49:04  john
642  * Put functionality of New Hostage spec into code.
643  *
644  * Revision 1.25  1994/06/29  11:05:38  john
645  * Made demos read in compressed.
646  *
647  * Revision 1.24  1994/06/29  09:14:06  john
648  * Made files write out uncompressed and read in compressed.
649  *
650  * Revision 1.23  1994/06/28  11:55:28  john
651  * Made newdemo system record/play directly to/from disk, so
652  * we don't need the 4 MB buffer anymore.
653  *
654  * Revision 1.22  1994/06/27  15:52:38  john
655  * #define'd out the newdemo stuff
656  *
657  *
658  * Revision 1.21  1994/06/22  00:29:04  john
659  * Fixed bug with playing demo then playing game without
660  * loading new mine.
661  *
662  * Revision 1.20  1994/06/22  00:14:23  john
663  * Attempted to fix sign.
664  *
665  * Revision 1.19  1994/06/21  23:57:54  john
666  * Hopefully fixed bug with negative countdowns.
667  *
668  * Revision 1.18  1994/06/21  23:47:44  john
669  * MAde Malloc always 4*1024*1024.
670  *
671  * Revision 1.17  1994/06/21  22:58:47  john
672  * Added error if out of memory.
673  *
674  * Revision 1.16  1994/06/21  22:15:48  john
675  * Added  % done to demo recording.
676  *
677  *
678  * Revision 1.15  1994/06/21  19:45:55  john
679  * Added palette effects to demo recording.
680  *
681  * Revision 1.14  1994/06/21  15:08:54  john
682  * Made demo record HUD message and cleaned up the HUD code.
683  *
684  * Revision 1.13  1994/06/21  14:20:08  john
685  * Put in hooks to record HUD messages.
686  *
687  * Revision 1.12  1994/06/20  11:50:15  john
688  * Made demo record flash effect, and control center triggers.
689  *
690  * Revision 1.11  1994/06/17  18:01:33  john
691  * A bunch of new stuff by John
692  *
693  * Revision 1.10  1994/06/17  12:13:31  john
694  * More newdemo stuff; made editor->game transition start in slew mode.
695  *
696  * Revision 1.9  1994/06/16  13:14:36  matt
697  * Fixed typo
698  *
699  * Revision 1.8  1994/06/16  13:02:07  john
700  * Added morph hooks.
701  *
702  * Revision 1.7  1994/06/15  19:01:33  john
703  * Added the capability to make 3d sounds play just once for the
704  * laser hit wall effects.
705  *
706  * Revision 1.6  1994/06/15  14:56:59  john
707  * Added triggers to demo recording.
708  *
709  * Revision 1.5  1994/06/14  20:42:15  john
710  * Made robot matztn cntr not work until no robots or player are
711  * in the segment.
712  *
713  * Revision 1.4  1994/06/14  14:43:27  john
714  * Made doors work with newdemo system.
715  *
716  * Revision 1.3  1994/06/14  11:32:29  john
717  * Made Newdemo record & restore the current mine.
718  *
719  * Revision 1.2  1994/06/13  21:02:43  john
720  * Initial version of new demo recording system.
721  *
722  * Revision 1.1  1994/06/13  11:09:00  john
723  * Initial revision
724  *
725  *
726  */
727
728
729 #ifdef HAVE_CONFIG_H
730 #include <conf.h>
731 #endif
732
733 #include <stdlib.h>
734 #include <stdio.h>
735 #include <stdarg.h>
736 #include <string.h> // for memset
737 #ifndef _WIN32_WCE
738 #include <errno.h>
739 #endif
740 #include <ctype.h>      /* for isdigit */
741 #include <limits.h>
742 #ifdef __unix__
743 #include <sys/stat.h>
744 #include <sys/types.h>
745 #endif
746
747 #include "u_mem.h"
748 #include "inferno.h"
749 #include "game.h"
750 #include "gr.h"
751 #include "stdlib.h"
752 #include "bm.h"
753 //#include "error.h"
754 #include "mono.h"
755 #include "3d.h"
756 #include "segment.h"
757 #include "texmap.h"
758 #include "laser.h"
759 #include "key.h"
760 #include "gameseg.h"
761
762 #include "object.h"
763 #include "physics.h"
764 #include "slew.h"
765 #include "render.h"
766 #include "wall.h"
767 #include "vclip.h"
768 #include "polyobj.h"
769 #include "fireball.h"
770 #include "laser.h"
771 #include "error.h"
772 #include "ai.h"
773 #include "hostage.h"
774 #include "morph.h"
775
776 #include "powerup.h"
777 #include "fuelcen.h"
778
779 #include "sounds.h"
780 #include "collide.h"
781
782 #include "lighting.h"
783 #include "newdemo.h"
784 #include "gameseq.h"
785 #include "gamesave.h"
786 #include "gamemine.h"
787 #include "switch.h"
788 #include "gauges.h"
789 #include "player.h"
790 #include "vecmat.h"
791 #include "newmenu.h"
792 #include "args.h"
793 #include "palette.h"
794 #include "multi.h"
795 #ifdef NETWORK
796 #include "network.h"
797 #endif
798 #include "text.h"
799 #include "cntrlcen.h"
800 #include "aistruct.h"
801 #include "mission.h"
802 #include "piggy.h"
803 #include "controls.h"
804 #include "d_io.h"
805 #include "timer.h"
806
807 #include "findfile.h"
808
809 #ifdef EDITOR
810 #include "editor/editor.h"
811 #endif
812
813 #ifdef MACINTOSH
814 #pragma global_optimizer off        // pretty much sucks...need to look into this
815 #endif
816
817 void DoJasonInterpolate (fix recorded_time);
818
819 //#include "nocfile.h"
820
821 //Does demo start automatically?
822 int Auto_demo = 0;
823
824 sbyte WasRecorded [MAX_OBJECTS];
825 sbyte ViewWasRecorded[MAX_OBJECTS];
826 sbyte RenderingWasRecorded[32];
827
828 #define ND_EVENT_EOF                0   // EOF
829 #define ND_EVENT_START_DEMO         1   // Followed by 16 character, NULL terminated filename of .SAV file to use
830 #define ND_EVENT_START_FRAME        2   // Followed by integer frame number, then a fix FrameTime
831 #define ND_EVENT_VIEWER_OBJECT      3   // Followed by an object structure
832 #define ND_EVENT_RENDER_OBJECT      4   // Followed by an object structure
833 #define ND_EVENT_SOUND              5   // Followed by int soundum
834 #define ND_EVENT_SOUND_ONCE         6   // Followed by int soundum
835 #define ND_EVENT_SOUND_3D           7   // Followed by int soundum, int angle, int volume
836 #define ND_EVENT_WALL_HIT_PROCESS   8   // Followed by int segnum, int side, fix damage
837 #define ND_EVENT_TRIGGER            9   // Followed by int segnum, int side, int objnum
838 #define ND_EVENT_HOSTAGE_RESCUED    10  // Followed by int hostage_type
839 #define ND_EVENT_SOUND_3D_ONCE      11  // Followed by int soundum, int angle, int volume
840 #define ND_EVENT_MORPH_FRAME        12  // Followed by ? data
841 #define ND_EVENT_WALL_TOGGLE        13  // Followed by int seg, int side
842 #define ND_EVENT_HUD_MESSAGE        14  // Followed by char size, char * string (+null)
843 #define ND_EVENT_CONTROL_CENTER_DESTROYED 15 // Just a simple flag
844 #define ND_EVENT_PALETTE_EFFECT     16  // Followed by short r,g,b
845 #define ND_EVENT_PLAYER_ENERGY      17  // followed by byte energy
846 #define ND_EVENT_PLAYER_SHIELD      18  // followed by byte shields
847 #define ND_EVENT_PLAYER_FLAGS       19  // followed by player flags
848 #define ND_EVENT_PLAYER_WEAPON      20  // followed by weapon type and weapon number
849 #define ND_EVENT_EFFECT_BLOWUP      21  // followed by segment, side, and pnt
850 #define ND_EVENT_HOMING_DISTANCE    22  // followed by homing distance
851 #define ND_EVENT_LETTERBOX          23  // letterbox mode for death seq.
852 #define ND_EVENT_RESTORE_COCKPIT    24  // restore cockpit after death
853 #define ND_EVENT_REARVIEW           25  // going to rear view mode
854 #define ND_EVENT_WALL_SET_TMAP_NUM1 26  // Wall changed
855 #define ND_EVENT_WALL_SET_TMAP_NUM2 27  // Wall changed
856 #define ND_EVENT_NEW_LEVEL          28  // followed by level number
857 #define ND_EVENT_MULTI_CLOAK        29  // followed by player num
858 #define ND_EVENT_MULTI_DECLOAK      30  // followed by player num
859 #define ND_EVENT_RESTORE_REARVIEW   31  // restore cockpit after rearview mode
860 #define ND_EVENT_MULTI_DEATH        32  // with player number
861 #define ND_EVENT_MULTI_KILL         33  // with player number
862 #define ND_EVENT_MULTI_CONNECT      34  // with player number
863 #define ND_EVENT_MULTI_RECONNECT    35  // with player number
864 #define ND_EVENT_MULTI_DISCONNECT   36  // with player number
865 #define ND_EVENT_MULTI_SCORE        37  // playernum / score
866 #define ND_EVENT_PLAYER_SCORE       38  // followed by score
867 #define ND_EVENT_PRIMARY_AMMO       39  // with old/new ammo count
868 #define ND_EVENT_SECONDARY_AMMO     40  // with old/new ammo count
869 #define ND_EVENT_DOOR_OPENING       41  // with segment/side
870 #define ND_EVENT_LASER_LEVEL        42  // no data
871 #define ND_EVENT_PLAYER_AFTERBURNER 43  // followed by byte old ab, current ab
872 #define ND_EVENT_CLOAKING_WALL      44  // info changing while wall cloaking
873 #define ND_EVENT_CHANGE_COCKPIT     45  // change the cockpit
874 #define ND_EVENT_START_GUIDED       46  // switch to guided view
875 #define ND_EVENT_END_GUIDED         47  // stop guided view/return to ship
876 #define ND_EVENT_SECRET_THINGY      48  // 0/1 = secret exit functional/non-functional
877 #define ND_EVENT_LINK_SOUND_TO_OBJ  49  // record digi_link_sound_to_object3
878 #define ND_EVENT_KILL_SOUND_TO_OBJ  50  // record digi_kill_sound_linked_to_object
879
880
881 #define NORMAL_PLAYBACK         0
882 #define SKIP_PLAYBACK           1
883 #define INTERPOLATE_PLAYBACK    2
884 #define INTERPOL_FACTOR         (F1_0 + (F1_0/5))
885
886 #define DEMO_VERSION            15      // last D1 version was 13
887 #define DEMO_GAME_TYPE          3       // 1 was shareware, 2 registered
888
889 #define DEMO_FILENAME           DEMO_DIR "tmpdemo.dem"
890
891 #define DEMO_MAX_LEVELS         29
892
893
894 char nd_save_callsign[CALLSIGN_LEN+1];
895 int Newdemo_state = 0;
896 int Newdemo_vcr_state = 0;
897 int Newdemo_start_frame = -1;
898 unsigned int Newdemo_size;
899 int Newdemo_num_written;
900 int Newdemo_game_mode;
901 int Newdemo_old_cockpit;
902 sbyte Newdemo_no_space;
903 sbyte Newdemo_at_eof;
904 sbyte Newdemo_do_interpolate = 0; // 1
905 sbyte Newdemo_players_cloaked;
906 sbyte Newdemo_warning_given = 0;
907 sbyte Newdemo_cntrlcen_destroyed = 0;
908 static sbyte nd_bad_read;
909 int NewdemoFrameCount;
910 short frame_bytes_written = 0;
911 fix nd_playback_total;
912 fix nd_recorded_total;
913 fix nd_recorded_time;
914 sbyte playback_style;
915 sbyte First_time_playback=1;
916 fix JasonPlaybackTotal=0;
917
918
919 CFILE *infile;
920 CFILE *outfile = NULL;
921
922 int newdemo_get_percent_done() {
923         if ( Newdemo_state == ND_STATE_PLAYBACK ) {
924                 return (cftell(infile) * 100) / Newdemo_size;
925         }
926         if ( Newdemo_state == ND_STATE_RECORDING ) {
927                 return cftell(outfile);
928         }
929         return 0;
930 }
931
932 #define VEL_PRECISION 12
933
934 void my_extract_shortpos(object *objp, shortpos *spp)
935 {
936         int segnum;
937         sbyte *sp;
938
939         sp = spp->bytemat;
940         objp->orient.rvec.x = *sp++ << MATRIX_PRECISION;
941         objp->orient.uvec.x = *sp++ << MATRIX_PRECISION;
942         objp->orient.fvec.x = *sp++ << MATRIX_PRECISION;
943
944         objp->orient.rvec.y = *sp++ << MATRIX_PRECISION;
945         objp->orient.uvec.y = *sp++ << MATRIX_PRECISION;
946         objp->orient.fvec.y = *sp++ << MATRIX_PRECISION;
947
948         objp->orient.rvec.z = *sp++ << MATRIX_PRECISION;
949         objp->orient.uvec.z = *sp++ << MATRIX_PRECISION;
950         objp->orient.fvec.z = *sp++ << MATRIX_PRECISION;
951
952         segnum = spp->segment;
953         objp->segnum = segnum;
954
955         objp->pos.x = (spp->xo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].x;
956         objp->pos.y = (spp->yo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].y;
957         objp->pos.z = (spp->zo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].z;
958
959         objp->mtype.phys_info.velocity.x = (spp->velx << VEL_PRECISION);
960         objp->mtype.phys_info.velocity.y = (spp->vely << VEL_PRECISION);
961         objp->mtype.phys_info.velocity.z = (spp->velz << VEL_PRECISION);
962 }
963
964 int newdemo_read( void *buffer, int elsize, int nelem )
965 {
966         int num_read;
967         num_read = cfread(buffer, elsize, nelem, infile);
968         if (cferror(infile) || cfeof(infile))
969                 nd_bad_read = -1;
970
971         return num_read;
972 }
973
974 int newdemo_find_object( int signature )
975 {
976         int i;
977         object * objp;
978         objp = Objects;
979         for (i=0; i<=Highest_object_index; i++, objp++ ) {
980                 if ( (objp->type != OBJ_NONE) && (objp->signature == signature))
981                         return i;
982         }
983         return -1;
984 }
985
986 int newdemo_write( void *buffer, int elsize, int nelem )
987 {
988         int num_written, total_size;
989
990         total_size = elsize * nelem;
991         frame_bytes_written += total_size;
992         Newdemo_num_written += total_size;
993         Assert(outfile != NULL);
994         num_written = cfwrite(buffer, elsize, nelem, outfile);
995         //if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space) {
996         //      Newdemo_no_space=1;
997         //      newdemo_stop_recording();
998         //      return -1;
999         //}
1000         if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space)
1001                 Newdemo_no_space=1;
1002         if (num_written == nelem && !Newdemo_no_space)
1003                 return num_written;
1004
1005         Newdemo_no_space=2;
1006         newdemo_stop_recording();
1007         return -1;
1008 }
1009
1010 /*
1011  *  The next bunch of files taken from Matt's gamesave.c.  We have to modify
1012  *  these since the demo must save more information about objects that
1013  *  just a gamesave
1014 */
1015
1016 static void nd_write_byte(sbyte b)
1017 {
1018         newdemo_write(&b, 1, 1);
1019 }
1020
1021 static void nd_write_short(short s)
1022 {
1023         newdemo_write(&s, 2, 1);
1024 }
1025
1026 static void nd_write_int(int i)
1027 {
1028         newdemo_write(&i, 4, 1);
1029 }
1030
1031 static void nd_write_string(char *str)
1032 {
1033         nd_write_byte(strlen(str) + 1);
1034         newdemo_write(str, strlen(str) + 1, 1);
1035 }
1036
1037 static void nd_write_fix(fix f)
1038 {
1039         newdemo_write(&f, sizeof(fix), 1);
1040 }
1041
1042 static void nd_write_fixang(fixang f)
1043 {
1044         newdemo_write(&f, sizeof(fixang), 1);
1045 }
1046
1047 static void nd_write_vector(vms_vector *v)
1048 {
1049         nd_write_fix(v->x);
1050         nd_write_fix(v->y);
1051         nd_write_fix(v->z);
1052 }
1053
1054 static void nd_write_angvec(vms_angvec *v)
1055 {
1056         nd_write_fixang(v->p);
1057         nd_write_fixang(v->b);
1058         nd_write_fixang(v->h);
1059 }
1060
1061 void nd_write_shortpos(object *obj)
1062 {
1063         int i;
1064         shortpos sp;
1065         ubyte render_type;
1066
1067         create_shortpos(&sp, obj, 0);
1068
1069         render_type = obj->render_type;
1070         if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
1071                 for (i = 0; i < 9; i++)
1072                         nd_write_byte(sp.bytemat[i]);
1073                 for (i = 0; i < 9; i++) {
1074                         if (sp.bytemat[i] != 0)
1075                                 break;
1076                 }
1077                 if (i == 9) {
1078                         Int3();         // contact Allender about this.
1079                 }
1080         }
1081
1082         nd_write_short(sp.xo);
1083         nd_write_short(sp.yo);
1084         nd_write_short(sp.zo);
1085         nd_write_short(sp.segment);
1086         nd_write_short(sp.velx);
1087         nd_write_short(sp.vely);
1088         nd_write_short(sp.velz);
1089 }
1090
1091 static void nd_read_byte(sbyte *b)
1092 {
1093         newdemo_read(b, 1, 1);
1094 }
1095
1096 static void nd_read_short(short *s)
1097 {
1098         newdemo_read(s, 2, 1);
1099 }
1100
1101 static void nd_read_int(int *i)
1102 {
1103         newdemo_read(i, 4, 1);
1104 }
1105
1106 static void nd_read_string(char *str)
1107 {
1108         sbyte len;
1109
1110         nd_read_byte(&len);
1111         newdemo_read(str, len, 1);
1112 }
1113
1114 static void nd_read_fix(fix *f)
1115 {
1116         newdemo_read(f, sizeof(fix), 1);
1117 }
1118
1119 static void nd_read_fixang(fixang *f)
1120 {
1121         newdemo_read(f, sizeof(fixang), 1);
1122 }
1123
1124 static void nd_read_vector(vms_vector *v)
1125 {
1126         nd_read_fix(&(v->x));
1127         nd_read_fix(&(v->y));
1128         nd_read_fix(&(v->z));
1129 }
1130
1131 static void nd_read_angvec(vms_angvec *v)
1132 {
1133         nd_read_fixang(&(v->p));
1134         nd_read_fixang(&(v->b));
1135         nd_read_fixang(&(v->h));
1136 }
1137
1138 static void nd_read_shortpos(object *obj)
1139 {
1140         shortpos sp;
1141         int i;
1142         ubyte render_type;
1143
1144         render_type = obj->render_type;
1145         if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
1146                 for (i = 0; i < 9; i++)
1147                         nd_read_byte(&(sp.bytemat[i]));
1148         }
1149
1150         nd_read_short(&(sp.xo));
1151         nd_read_short(&(sp.yo));
1152         nd_read_short(&(sp.zo));
1153         nd_read_short(&(sp.segment));
1154         nd_read_short(&(sp.velx));
1155         nd_read_short(&(sp.vely));
1156         nd_read_short(&(sp.velz));
1157
1158         my_extract_shortpos(obj, &sp);
1159         if ((obj->id == VCLIP_MORPHING_ROBOT) && (render_type == RT_FIREBALL) && (obj->control_type == CT_EXPLOSION))
1160                 extract_orient_from_segment(&obj->orient,&Segments[obj->segnum]);
1161
1162 }
1163
1164 object *prev_obj=NULL;      //ptr to last object read in
1165
1166 void nd_read_object(object *obj)
1167 {
1168         memset(obj, 0, sizeof(object));
1169
1170         /*
1171          * Do render type first, since with render_type == RT_NONE, we
1172          * blow by all other object information
1173          */
1174         nd_read_byte(&(obj->render_type));
1175         nd_read_byte(&(obj->type));
1176         if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
1177                 return;
1178
1179         nd_read_byte(&(obj->id));
1180         nd_read_byte(&(obj->flags));
1181         nd_read_short((short *)&(obj->signature));
1182         nd_read_shortpos(obj);
1183
1184         if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
1185                 Int3();
1186
1187         obj->attached_obj = -1;
1188
1189         switch(obj->type) {
1190
1191         case OBJ_HOSTAGE:
1192                 obj->control_type = CT_POWERUP;
1193                 obj->movement_type = MT_NONE;
1194                 obj->size = HOSTAGE_SIZE;
1195                 break;
1196
1197         case OBJ_ROBOT:
1198                 obj->control_type = CT_AI;
1199                 // (MarkA and MikeK said we should not do the crazy last secret stuff with multiple reactors...
1200                 // This necessary code is our vindication. --MK, 2/15/96)
1201                 if (obj->id != SPECIAL_REACTOR_ROBOT)
1202                         obj->movement_type = MT_PHYSICS;
1203                 else
1204                         obj->movement_type = MT_NONE;
1205                 obj->size = Polygon_models[Robot_info[obj->id].model_num].rad;
1206                 obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
1207                 obj->rtype.pobj_info.subobj_flags = 0;
1208                 obj->ctype.ai_info.CLOAKED = (Robot_info[obj->id].cloak_type?1:0);
1209                 break;
1210
1211         case OBJ_POWERUP:
1212                 obj->control_type = CT_POWERUP;
1213                 nd_read_byte(&(obj->movement_type));        // might have physics movement
1214                 obj->size = Powerup_info[obj->id].size;
1215                 break;
1216
1217         case OBJ_PLAYER:
1218                 obj->control_type = CT_NONE;
1219                 obj->movement_type = MT_PHYSICS;
1220                 obj->size = Polygon_models[Player_ship->model_num].rad;
1221                 obj->rtype.pobj_info.model_num = Player_ship->model_num;
1222                 obj->rtype.pobj_info.subobj_flags = 0;
1223                 break;
1224
1225         case OBJ_CLUTTER:
1226                 obj->control_type = CT_NONE;
1227                 obj->movement_type = MT_NONE;
1228                 obj->size = Polygon_models[obj->id].rad;
1229                 obj->rtype.pobj_info.model_num = obj->id;
1230                 obj->rtype.pobj_info.subobj_flags = 0;
1231                 break;
1232
1233         default:
1234                 nd_read_byte(&(obj->control_type));
1235                 nd_read_byte(&(obj->movement_type));
1236                 nd_read_fix(&(obj->size));
1237                 break;
1238         }
1239
1240
1241         nd_read_vector(&(obj->last_pos));
1242         if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
1243                 nd_read_fix(&(obj->lifeleft));
1244         else {
1245                 ubyte b;
1246
1247                 nd_read_byte(&b);
1248                 obj->lifeleft = (fix)b;
1249                 // MWA old way -- won't work with big endian machines       nd_read_byte((ubyte *)&(obj->lifeleft));
1250                 obj->lifeleft = (fix)((int)obj->lifeleft << 12);
1251         }
1252
1253         if (obj->type == OBJ_ROBOT) {
1254                 if (Robot_info[obj->id].boss_flag) {
1255                         sbyte cloaked;
1256
1257                         nd_read_byte(&cloaked);
1258                         obj->ctype.ai_info.CLOAKED = cloaked;
1259                 }
1260         }
1261
1262         switch (obj->movement_type) {
1263
1264         case MT_PHYSICS:
1265                 nd_read_vector(&(obj->mtype.phys_info.velocity));
1266                 nd_read_vector(&(obj->mtype.phys_info.thrust));
1267                 break;
1268
1269         case MT_SPINNING:
1270                 nd_read_vector(&(obj->mtype.spin_rate));
1271                 break;
1272
1273         case MT_NONE:
1274                 break;
1275
1276         default:
1277                 Int3();
1278         }
1279
1280         switch (obj->control_type) {
1281
1282         case CT_EXPLOSION:
1283
1284                 nd_read_fix(&(obj->ctype.expl_info.spawn_time));
1285                 nd_read_fix(&(obj->ctype.expl_info.delete_time));
1286                 nd_read_short(&(obj->ctype.expl_info.delete_objnum));
1287
1288                 obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
1289
1290                 if (obj->flags & OF_ATTACHED) {     //attach to previous object
1291                         Assert(prev_obj!=NULL);
1292                         if (prev_obj->control_type == CT_EXPLOSION) {
1293                                 if (prev_obj->flags & OF_ATTACHED && prev_obj->ctype.expl_info.attach_parent!=-1)
1294                                         obj_attach(&Objects[prev_obj->ctype.expl_info.attach_parent],obj);
1295                                 else
1296                                         obj->flags &= ~OF_ATTACHED;
1297                         }
1298                         else
1299                                 obj_attach(prev_obj,obj);
1300                 }
1301
1302                 break;
1303
1304         case CT_LIGHT:
1305                 nd_read_fix(&(obj->ctype.light_info.intensity));
1306                 break;
1307
1308         case CT_AI:
1309         case CT_WEAPON:
1310         case CT_NONE:
1311         case CT_FLYING:
1312         case CT_DEBRIS:
1313         case CT_POWERUP:
1314         case CT_SLEW:
1315         case CT_CNTRLCEN:
1316         case CT_REMOTE:
1317         case CT_MORPH:
1318                 break;
1319
1320         case CT_FLYTHROUGH:
1321         case CT_REPAIRCEN:
1322         default:
1323                 Int3();
1324
1325         }
1326
1327         switch (obj->render_type) {
1328
1329         case RT_NONE:
1330                 break;
1331
1332         case RT_MORPH:
1333         case RT_POLYOBJ: {
1334                 int i, tmo;
1335
1336                 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
1337                         nd_read_int(&(obj->rtype.pobj_info.model_num));
1338                         nd_read_int(&(obj->rtype.pobj_info.subobj_flags));
1339                 }
1340
1341                 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
1342 #if 0
1343                         for (i=0;i<MAX_SUBMODELS;i++)
1344                                 nd_read_angvec(&(obj->pobj_info.anim_angles[i]));
1345 #endif
1346                 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
1347                         nd_read_angvec(&obj->rtype.pobj_info.anim_angles[i]);
1348
1349                 nd_read_int(&tmo);
1350
1351 #ifndef EDITOR
1352                 obj->rtype.pobj_info.tmap_override = tmo;
1353 #else
1354                 if (tmo==-1)
1355                         obj->rtype.pobj_info.tmap_override = -1;
1356                 else {
1357                         int xlated_tmo = tmap_xlate_table[tmo];
1358                         if (xlated_tmo < 0) {
1359                                 //mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->pobj_info.model_num));
1360                                 Int3();
1361                                 xlated_tmo = 0;
1362                         }
1363                         obj->rtype.pobj_info.tmap_override = xlated_tmo;
1364                 }
1365 #endif
1366
1367                 break;
1368         }
1369
1370         case RT_POWERUP:
1371         case RT_WEAPON_VCLIP:
1372         case RT_FIREBALL:
1373         case RT_HOSTAGE:
1374                 nd_read_int(&(obj->rtype.vclip_info.vclip_num));
1375                 nd_read_fix(&(obj->rtype.vclip_info.frametime));
1376                 nd_read_byte(&(obj->rtype.vclip_info.framenum));
1377                 break;
1378
1379         case RT_LASER:
1380                 break;
1381
1382         default:
1383                 Int3();
1384
1385         }
1386
1387         prev_obj = obj;
1388 }
1389
1390 void nd_write_object(object *obj)
1391 {
1392         int life;
1393
1394         if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
1395                 Int3();
1396
1397         /*
1398          * Do render_type first so on read, we can make determination of
1399          * what else to read in
1400          */
1401         nd_write_byte(obj->render_type);
1402         nd_write_byte(obj->type);
1403         if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
1404                 return;
1405
1406         nd_write_byte(obj->id);
1407         nd_write_byte(obj->flags);
1408         nd_write_short((short)obj->signature);
1409         nd_write_shortpos(obj);
1410
1411         if ((obj->type != OBJ_HOSTAGE) && (obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_POWERUP) && (obj->type != OBJ_CLUTTER)) {
1412                 nd_write_byte(obj->control_type);
1413                 nd_write_byte(obj->movement_type);
1414                 nd_write_fix(obj->size);
1415         }
1416         if (obj->type == OBJ_POWERUP)
1417                 nd_write_byte(obj->movement_type);
1418
1419         nd_write_vector(&obj->last_pos);
1420
1421         if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
1422                 nd_write_fix(obj->lifeleft);
1423         else {
1424                 life = (int)obj->lifeleft;
1425                 life = life >> 12;
1426                 if (life > 255)
1427                         life = 255;
1428                 nd_write_byte((ubyte)life);
1429         }
1430
1431         if (obj->type == OBJ_ROBOT) {
1432                 if (Robot_info[obj->id].boss_flag) {
1433                         if ((GameTime > Boss_cloak_start_time) && (GameTime < Boss_cloak_end_time))
1434                                 nd_write_byte(1);
1435                         else
1436                                 nd_write_byte(0);
1437                 }
1438         }
1439
1440         switch (obj->movement_type) {
1441
1442         case MT_PHYSICS:
1443                 nd_write_vector(&obj->mtype.phys_info.velocity);
1444                 nd_write_vector(&obj->mtype.phys_info.thrust);
1445                 break;
1446
1447         case MT_SPINNING:
1448                 nd_write_vector(&obj->mtype.spin_rate);
1449                 break;
1450
1451         case MT_NONE:
1452                 break;
1453
1454         default:
1455                 Int3();
1456         }
1457
1458         switch (obj->control_type) {
1459
1460         case CT_AI:
1461                 break;
1462
1463         case CT_EXPLOSION:
1464                 nd_write_fix(obj->ctype.expl_info.spawn_time);
1465                 nd_write_fix(obj->ctype.expl_info.delete_time);
1466                 nd_write_short(obj->ctype.expl_info.delete_objnum);
1467                 break;
1468
1469         case CT_WEAPON:
1470                 break;
1471
1472         case CT_LIGHT:
1473
1474                 nd_write_fix(obj->ctype.light_info.intensity);
1475                 break;
1476
1477         case CT_NONE:
1478         case CT_FLYING:
1479         case CT_DEBRIS:
1480         case CT_POWERUP:
1481         case CT_SLEW:       //the player is generally saved as slew
1482         case CT_CNTRLCEN:
1483         case CT_REMOTE:
1484         case CT_MORPH:
1485                 break;
1486
1487         case CT_REPAIRCEN:
1488         case CT_FLYTHROUGH:
1489         default:
1490                 Int3();
1491
1492         }
1493
1494         switch (obj->render_type) {
1495
1496         case RT_NONE:
1497                 break;
1498
1499         case RT_MORPH:
1500         case RT_POLYOBJ: {
1501                 int i;
1502
1503                 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
1504                         nd_write_int(obj->rtype.pobj_info.model_num);
1505                         nd_write_int(obj->rtype.pobj_info.subobj_flags);
1506                 }
1507
1508                 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
1509 #if 0
1510                         for (i=0;i<MAX_SUBMODELS;i++)
1511                                 nd_write_angvec(&obj->pobj_info.anim_angles[i]);
1512 #endif
1513                 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
1514                         nd_write_angvec(&obj->rtype.pobj_info.anim_angles[i]);
1515
1516                 nd_write_int(obj->rtype.pobj_info.tmap_override);
1517
1518                 break;
1519         }
1520
1521         case RT_POWERUP:
1522         case RT_WEAPON_VCLIP:
1523         case RT_FIREBALL:
1524         case RT_HOSTAGE:
1525                 nd_write_int(obj->rtype.vclip_info.vclip_num);
1526                 nd_write_fix(obj->rtype.vclip_info.frametime);
1527                 nd_write_byte(obj->rtype.vclip_info.framenum);
1528                 break;
1529
1530         case RT_LASER:
1531                 break;
1532
1533         default:
1534                 Int3();
1535
1536         }
1537
1538 }
1539
1540 int JustStartedRecording=0,JustStartedPlayback=0;
1541
1542 void newdemo_record_start_demo()
1543 {
1544         int i;
1545
1546         stop_time();
1547         nd_write_byte(ND_EVENT_START_DEMO);
1548         nd_write_byte(DEMO_VERSION);
1549         nd_write_byte(DEMO_GAME_TYPE);
1550         nd_write_fix(GameTime);
1551
1552 #ifdef NETWORK
1553         if (Game_mode & GM_MULTI)
1554                 nd_write_int(Game_mode | (Player_num << 16));
1555         else
1556 #endif
1557                 // NOTE LINK TO ABOVE!!!
1558                 nd_write_int(Game_mode);
1559 #ifdef NETWORK
1560
1561         if (Game_mode & GM_TEAM) {
1562                 nd_write_byte(Netgame.team_vector);
1563                 nd_write_string(Netgame.team_name[0]);
1564                 nd_write_string(Netgame.team_name[1]);
1565         }
1566
1567         if (Game_mode & GM_MULTI) {
1568                 nd_write_byte((sbyte)N_players);
1569                 for (i = 0; i < N_players; i++) {
1570                         nd_write_string(Players[i].callsign);
1571                         nd_write_byte(Players[i].connected);
1572
1573                         if (Game_mode & GM_MULTI_COOP) {
1574                                 nd_write_int(Players[i].score);
1575                         } else {
1576                                 nd_write_short((short)Players[i].net_killed_total);
1577                                 nd_write_short((short)Players[i].net_kills_total);
1578                         }
1579                 }
1580         } else
1581 #endif
1582                 // NOTE LINK TO ABOVE!!!
1583                 nd_write_int(Players[Player_num].score);
1584
1585         for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
1586                 nd_write_short((short)Players[Player_num].primary_ammo[i]);
1587
1588         for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
1589                 nd_write_short((short)Players[Player_num].secondary_ammo[i]);
1590
1591         nd_write_byte((sbyte)Players[Player_num].laser_level);
1592
1593 //  Support for missions added here
1594
1595         nd_write_string(Current_mission_filename);
1596
1597         nd_write_byte((sbyte)(f2ir(Players[Player_num].energy)));
1598         nd_write_byte((sbyte)(f2ir(Players[Player_num].shields)));
1599         nd_write_int(Players[Player_num].flags);        // be sure players flags are set
1600         nd_write_byte((sbyte)Primary_weapon);
1601         nd_write_byte((sbyte)Secondary_weapon);
1602         Newdemo_start_frame = FrameCount;
1603         JustStartedRecording=1;
1604
1605         newdemo_set_new_level(Current_level_num);
1606         start_time();
1607
1608 }
1609
1610 void newdemo_record_start_frame(int frame_number, fix frame_time )
1611 {
1612         int i;
1613
1614         if (Newdemo_no_space) {
1615                 newdemo_stop_playback();
1616                 return;
1617         }
1618
1619         stop_time();
1620
1621         for (i=0;i<MAX_OBJECTS;i++)
1622         {
1623                 WasRecorded[i]=0;
1624                 ViewWasRecorded[i]=0;
1625         }
1626         for (i=0;i<32;i++)
1627                 RenderingWasRecorded[i]=0;
1628
1629         frame_number -= Newdemo_start_frame;
1630
1631         Assert(frame_number >= 0 );
1632
1633         nd_write_byte(ND_EVENT_START_FRAME);
1634         nd_write_short(frame_bytes_written - 1);        // from previous frame
1635         frame_bytes_written=3;
1636         nd_write_int(frame_number);
1637         nd_write_int(frame_time);
1638         start_time();
1639
1640 }
1641
1642 void newdemo_record_render_object(object * obj)
1643 {
1644         if (ViewWasRecorded[obj-Objects])
1645                 return;
1646
1647         //if (obj==&Objects[Players[Player_num].objnum] && !Player_is_dead)
1648         //      return;
1649
1650         stop_time();
1651         nd_write_byte(ND_EVENT_RENDER_OBJECT);
1652         nd_write_object(obj);
1653         start_time();
1654 }
1655
1656 extern ubyte RenderingType;
1657
1658 void newdemo_record_viewer_object(object * obj)
1659 {
1660
1661         if (ViewWasRecorded[obj-Objects] && (ViewWasRecorded[obj-Objects]-1)==RenderingType)
1662                 return;
1663         //if (WasRecorded[obj-Objects])
1664         //      return;
1665         if (RenderingWasRecorded[RenderingType])
1666                 return;
1667
1668         ViewWasRecorded[obj-Objects]=RenderingType+1;
1669         RenderingWasRecorded[RenderingType]=1;
1670         stop_time();
1671         nd_write_byte(ND_EVENT_VIEWER_OBJECT);
1672         nd_write_byte(RenderingType);
1673         nd_write_object(obj);
1674         start_time();
1675 }
1676
1677 void newdemo_record_sound( int soundno )
1678 {
1679         stop_time();
1680         nd_write_byte(ND_EVENT_SOUND);
1681         nd_write_int( soundno );
1682         start_time();
1683 }
1684
1685 //--unused-- void newdemo_record_sound_once( int soundno ) {
1686 //--unused--    stop_time();
1687 //--unused--    nd_write_byte( ND_EVENT_SOUND_ONCE );
1688 //--unused--    nd_write_int( soundno );
1689 //--unused--    start_time();
1690 //--unused-- }
1691 //--unused--
1692
1693 void newdemo_record_cockpit_change (int mode)
1694 {
1695         stop_time();
1696         nd_write_byte (ND_EVENT_CHANGE_COCKPIT);
1697         nd_write_int(mode);
1698         start_time();
1699 }
1700
1701
1702 void newdemo_record_sound_3d( int soundno, int angle, int volume )
1703 {
1704         stop_time();
1705         nd_write_byte( ND_EVENT_SOUND_3D );
1706         nd_write_int( soundno );
1707         nd_write_int( angle );
1708         nd_write_int( volume );
1709         start_time();
1710 }
1711
1712 void newdemo_record_sound_3d_once( int soundno, int angle, int volume )
1713 {
1714         stop_time();
1715         nd_write_byte( ND_EVENT_SOUND_3D_ONCE );
1716         nd_write_int( soundno );
1717         nd_write_int( angle );
1718         nd_write_int( volume );
1719         start_time();
1720 }
1721
1722
1723 void newdemo_record_link_sound_to_object3( int soundno, short objnum, fix max_volume, fix  max_distance, int loop_start, int loop_end )
1724 {
1725         stop_time();
1726         nd_write_byte( ND_EVENT_LINK_SOUND_TO_OBJ );
1727         nd_write_int( soundno );
1728         nd_write_int( Objects[objnum].signature );
1729         nd_write_int( max_volume );
1730         nd_write_int( max_distance );
1731         nd_write_int( loop_start );
1732         nd_write_int( loop_end );
1733         start_time();
1734 }
1735
1736 void newdemo_record_kill_sound_linked_to_object( int objnum )
1737 {
1738         stop_time();
1739         nd_write_byte( ND_EVENT_KILL_SOUND_TO_OBJ );
1740         nd_write_int( Objects[objnum].signature );
1741         start_time();
1742 }
1743
1744
1745 void newdemo_record_wall_hit_process( int segnum, int side, int damage, int playernum )
1746 {
1747         stop_time();
1748         //segnum = segnum;
1749         //side = side;
1750         //damage = damage;
1751         //playernum = playernum;
1752         nd_write_byte( ND_EVENT_WALL_HIT_PROCESS );
1753         nd_write_int( segnum );
1754         nd_write_int( side );
1755         nd_write_int( damage );
1756         nd_write_int( playernum );
1757         start_time();
1758 }
1759
1760 void newdemo_record_guided_start ()
1761 {
1762         nd_write_byte (ND_EVENT_START_GUIDED);
1763 }
1764
1765 void newdemo_record_guided_end ()
1766 {
1767         nd_write_byte (ND_EVENT_END_GUIDED);
1768 }
1769
1770 void newdemo_record_secret_exit_blown(int truth)
1771 {
1772         stop_time();
1773         nd_write_byte( ND_EVENT_SECRET_THINGY );
1774         nd_write_int( truth );
1775         start_time();
1776 }
1777
1778 void newdemo_record_trigger( int segnum, int side, int objnum,int shot )
1779 {
1780         stop_time();
1781         nd_write_byte( ND_EVENT_TRIGGER );
1782         nd_write_int( segnum );
1783         nd_write_int( side );
1784         nd_write_int( objnum );
1785         nd_write_int(shot);
1786         start_time();
1787 }
1788
1789 void newdemo_record_hostage_rescued( int hostage_number ) {
1790         stop_time();
1791         nd_write_byte( ND_EVENT_HOSTAGE_RESCUED );
1792         nd_write_int( hostage_number );
1793         start_time();
1794 }
1795
1796 void newdemo_record_morph_frame(morph_data *md)
1797 {
1798         stop_time();
1799
1800         nd_write_byte( ND_EVENT_MORPH_FRAME );
1801 #if 0
1802         newdemo_write( md->morph_vecs, sizeof(md->morph_vecs), 1 );
1803         newdemo_write( md->submodel_active, sizeof(md->submodel_active), 1 );
1804         newdemo_write( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 );
1805 #endif
1806         nd_write_object( md->obj );
1807         start_time();
1808 }
1809
1810 void newdemo_record_wall_toggle( int segnum, int side )
1811 {
1812         stop_time();
1813         nd_write_byte( ND_EVENT_WALL_TOGGLE );
1814         nd_write_int( segnum );
1815         nd_write_int( side );
1816         start_time();
1817 }
1818
1819 void newdemo_record_control_center_destroyed()
1820 {
1821         stop_time();
1822         nd_write_byte( ND_EVENT_CONTROL_CENTER_DESTROYED );
1823         nd_write_int( Countdown_seconds_left );
1824         start_time();
1825 }
1826
1827 void newdemo_record_hud_message( char * message )
1828 {
1829         stop_time();
1830         nd_write_byte( ND_EVENT_HUD_MESSAGE );
1831         nd_write_string(message);
1832         start_time();
1833 }
1834
1835 void newdemo_record_palette_effect(short r, short g, short b )
1836 {
1837         stop_time();
1838         nd_write_byte( ND_EVENT_PALETTE_EFFECT );
1839         nd_write_short( r );
1840         nd_write_short( g );
1841         nd_write_short( b );
1842         start_time();
1843 }
1844
1845 void newdemo_record_player_energy(int old_energy, int energy)
1846 {
1847         stop_time();
1848         nd_write_byte( ND_EVENT_PLAYER_ENERGY );
1849         nd_write_byte((sbyte) old_energy);
1850         nd_write_byte((sbyte) energy);
1851         start_time();
1852 }
1853
1854 void newdemo_record_player_afterburner(fix old_afterburner, fix afterburner)
1855 {
1856         stop_time();
1857         nd_write_byte( ND_EVENT_PLAYER_AFTERBURNER );
1858         nd_write_byte((sbyte) (old_afterburner>>9));
1859         nd_write_byte((sbyte) (afterburner>>9));
1860         start_time();
1861 }
1862
1863 void newdemo_record_player_shields(int old_shield, int shield)
1864 {
1865         stop_time();
1866         nd_write_byte( ND_EVENT_PLAYER_SHIELD );
1867         nd_write_byte((sbyte)old_shield);
1868         nd_write_byte((sbyte)shield);
1869         start_time();
1870 }
1871
1872 void newdemo_record_player_flags(uint oflags, uint flags)
1873 {
1874         stop_time();
1875         nd_write_byte( ND_EVENT_PLAYER_FLAGS );
1876         nd_write_int(((short)oflags << 16) | (short)flags);
1877         start_time();
1878 }
1879
1880 void newdemo_record_player_weapon(int weapon_type, int weapon_num)
1881 {
1882         stop_time();
1883         nd_write_byte( ND_EVENT_PLAYER_WEAPON );
1884         nd_write_byte((sbyte)weapon_type);
1885         nd_write_byte((sbyte)weapon_num);
1886         if (weapon_type)
1887                 nd_write_byte((sbyte)Secondary_weapon);
1888         else
1889                 nd_write_byte((sbyte)Primary_weapon);
1890         start_time();
1891 }
1892
1893 void newdemo_record_effect_blowup(short segment, int side, vms_vector *pnt)
1894 {
1895         stop_time();
1896         nd_write_byte (ND_EVENT_EFFECT_BLOWUP);
1897         nd_write_short(segment);
1898         nd_write_byte((sbyte)side);
1899         nd_write_vector(pnt);
1900         start_time();
1901 }
1902
1903 void newdemo_record_homing_distance(fix distance)
1904 {
1905         stop_time();
1906         nd_write_byte(ND_EVENT_HOMING_DISTANCE);
1907         nd_write_short((short)(distance>>16));
1908         start_time();
1909 }
1910
1911 void newdemo_record_letterbox(void)
1912 {
1913         stop_time();
1914         nd_write_byte(ND_EVENT_LETTERBOX);
1915         start_time();
1916 }
1917
1918 void newdemo_record_rearview(void)
1919 {
1920         stop_time();
1921         nd_write_byte(ND_EVENT_REARVIEW);
1922         start_time();
1923 }
1924
1925 void newdemo_record_restore_cockpit(void)
1926 {
1927         stop_time();
1928         nd_write_byte(ND_EVENT_RESTORE_COCKPIT);
1929         start_time();
1930 }
1931
1932 void newdemo_record_restore_rearview(void)
1933 {
1934         stop_time();
1935         nd_write_byte(ND_EVENT_RESTORE_REARVIEW);
1936         start_time();
1937 }
1938
1939 void newdemo_record_wall_set_tmap_num1(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1940 {
1941         stop_time();
1942         nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM1);
1943         nd_write_short(seg);
1944         nd_write_byte(side);
1945         nd_write_short(cseg);
1946         nd_write_byte(cside);
1947         nd_write_short(tmap);
1948         start_time();
1949 }
1950
1951 void newdemo_record_wall_set_tmap_num2(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1952 {
1953         stop_time();
1954         nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM2);
1955         nd_write_short(seg);
1956         nd_write_byte(side);
1957         nd_write_short(cseg);
1958         nd_write_byte(cside);
1959         nd_write_short(tmap);
1960         start_time();
1961 }
1962
1963 void newdemo_record_multi_cloak(int pnum)
1964 {
1965         stop_time();
1966         nd_write_byte(ND_EVENT_MULTI_CLOAK);
1967         nd_write_byte((sbyte)pnum);
1968         start_time();
1969 }
1970
1971 void newdemo_record_multi_decloak(int pnum)
1972 {
1973         stop_time();
1974         nd_write_byte(ND_EVENT_MULTI_DECLOAK);
1975         nd_write_byte((sbyte)pnum);
1976         start_time();
1977 }
1978
1979 void newdemo_record_multi_death(int pnum)
1980 {
1981         stop_time();
1982         nd_write_byte(ND_EVENT_MULTI_DEATH);
1983         nd_write_byte((sbyte)pnum);
1984         start_time();
1985 }
1986
1987 void newdemo_record_multi_kill(int pnum, sbyte kill)
1988 {
1989         stop_time();
1990         nd_write_byte(ND_EVENT_MULTI_KILL);
1991         nd_write_byte((sbyte)pnum);
1992         nd_write_byte(kill);
1993         start_time();
1994 }
1995
1996 void newdemo_record_multi_connect(int pnum, int new_player, char *new_callsign)
1997 {
1998         stop_time();
1999         nd_write_byte(ND_EVENT_MULTI_CONNECT);
2000         nd_write_byte((sbyte)pnum);
2001         nd_write_byte((sbyte)new_player);
2002         if (!new_player) {
2003                 nd_write_string(Players[pnum].callsign);
2004                 nd_write_int(Players[pnum].net_killed_total);
2005                 nd_write_int(Players[pnum].net_kills_total);
2006         }
2007         nd_write_string(new_callsign);
2008         start_time();
2009 }
2010
2011 void newdemo_record_multi_reconnect(int pnum)
2012 {
2013         stop_time();
2014         nd_write_byte(ND_EVENT_MULTI_RECONNECT);
2015         nd_write_byte((sbyte)pnum);
2016         start_time();
2017 }
2018
2019 void newdemo_record_multi_disconnect(int pnum)
2020 {
2021         stop_time();
2022         nd_write_byte(ND_EVENT_MULTI_DISCONNECT);
2023         nd_write_byte((sbyte)pnum);
2024         start_time();
2025 }
2026
2027 void newdemo_record_player_score(int score)
2028 {
2029         stop_time();
2030         nd_write_byte(ND_EVENT_PLAYER_SCORE);
2031         nd_write_int(score);
2032         start_time();
2033 }
2034
2035 void newdemo_record_multi_score(int pnum, int score)
2036 {
2037         stop_time();
2038         nd_write_byte(ND_EVENT_MULTI_SCORE);
2039         nd_write_byte((sbyte)pnum);
2040         nd_write_int(score - Players[pnum].score);      // called before score is changed!!!!
2041         start_time();
2042 }
2043
2044 void newdemo_record_primary_ammo(int old_ammo, int new_ammo)
2045 {
2046         stop_time();
2047         nd_write_byte(ND_EVENT_PRIMARY_AMMO);
2048         if (old_ammo < 0)
2049                 nd_write_short((short)new_ammo);
2050         else
2051                 nd_write_short((short)old_ammo);
2052         nd_write_short((short)new_ammo);
2053         start_time();
2054 }
2055
2056 void newdemo_record_secondary_ammo(int old_ammo, int new_ammo)
2057 {
2058         stop_time();
2059         nd_write_byte(ND_EVENT_SECONDARY_AMMO);
2060         if (old_ammo < 0)
2061                 nd_write_short((short)new_ammo);
2062         else
2063                 nd_write_short((short)old_ammo);
2064         nd_write_short((short)new_ammo);
2065         start_time();
2066 }
2067
2068 void newdemo_record_door_opening(int segnum, int side)
2069 {
2070         stop_time();
2071         nd_write_byte(ND_EVENT_DOOR_OPENING);
2072         nd_write_short((short)segnum);
2073         nd_write_byte((sbyte)side);
2074         start_time();
2075 }
2076
2077 void newdemo_record_laser_level(sbyte old_level, sbyte new_level)
2078 {
2079         stop_time();
2080         nd_write_byte(ND_EVENT_LASER_LEVEL);
2081         nd_write_byte(old_level);
2082         nd_write_byte(new_level);
2083         start_time();
2084 }
2085
2086 void newdemo_record_cloaking_wall(int front_wall_num, int back_wall_num, ubyte type, ubyte state, fix cloak_value, fix l0, fix l1, fix l2, fix l3)
2087 {
2088         Assert(front_wall_num <= 255 && back_wall_num <= 255);
2089
2090         stop_time();
2091         nd_write_byte(ND_EVENT_CLOAKING_WALL);
2092         nd_write_byte(front_wall_num);
2093         nd_write_byte(back_wall_num);
2094         nd_write_byte(type);
2095         nd_write_byte(state);
2096         nd_write_byte(cloak_value);
2097         nd_write_short(l0>>8);
2098         nd_write_short(l1>>8);
2099         nd_write_short(l2>>8);
2100         nd_write_short(l3>>8);
2101         start_time();
2102 }
2103
2104 void newdemo_set_new_level(int level_num)
2105 {
2106         int i;
2107         int side;
2108         segment *seg;
2109
2110         stop_time();
2111         nd_write_byte(ND_EVENT_NEW_LEVEL);
2112         nd_write_byte((sbyte)level_num);
2113         nd_write_byte((sbyte)Current_level_num);
2114
2115         if (JustStartedRecording==1)
2116         {
2117                 nd_write_int(Num_walls);
2118                 for (i=0;i<Num_walls;i++)
2119                 {
2120                         nd_write_byte (Walls[i].type);
2121                         nd_write_byte (Walls[i].flags);
2122                         nd_write_byte (Walls[i].state);
2123
2124                         seg = &Segments[Walls[i].segnum];
2125                         side = Walls[i].sidenum;
2126                         nd_write_short (seg->sides[side].tmap_num);
2127                         nd_write_short (seg->sides[side].tmap_num2);
2128                         JustStartedRecording=0;
2129                 }
2130         }
2131
2132         start_time();
2133 }
2134
2135 int newdemo_read_demo_start(int rnd_demo)
2136 {
2137         sbyte i, version, game_type, laser_level;
2138         char c, energy, shield;
2139         char text[128], current_mission[9];
2140
2141         nd_read_byte(&c);
2142         if ((c != ND_EVENT_START_DEMO) || nd_bad_read) {
2143                 newmenu_item m[1];
2144
2145                 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_CORRUPT);
2146                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
2147                 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2148                 return 1;
2149         }
2150         nd_read_byte(&version);
2151         nd_read_byte(&game_type);
2152         if (game_type < DEMO_GAME_TYPE) {
2153                 newmenu_item m[2];
2154
2155                 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
2156                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
2157                 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = "    In Descent: First Strike";
2158
2159                 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2160                 return 1;
2161         }
2162         if (game_type != DEMO_GAME_TYPE) {
2163                 newmenu_item m[2];
2164
2165                 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
2166                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
2167                 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = "   In Unknown Descent version";
2168
2169                 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2170                 return 1;
2171         }
2172         if (version < DEMO_VERSION) {
2173                 if (!rnd_demo) {
2174                         newmenu_item m[1];
2175                         sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_OLD);
2176                         m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
2177                         newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2178                 }
2179                 return 1;
2180         }
2181         nd_read_fix(&GameTime);
2182         Boss_cloak_start_time=Boss_cloak_end_time=GameTime;
2183         JasonPlaybackTotal=0;
2184
2185         nd_read_int(&Newdemo_game_mode);
2186
2187 #ifdef NETWORK
2188         change_playernum_to((Newdemo_game_mode >> 16) & 0x7);
2189         if (Newdemo_game_mode & GM_TEAM) {
2190                 nd_read_byte(&(Netgame.team_vector));
2191                 nd_read_string(Netgame.team_name[0]);
2192                 nd_read_string(Netgame.team_name[1]);
2193         }
2194         if (Newdemo_game_mode & GM_MULTI) {
2195
2196                 multi_new_game();
2197                 nd_read_byte(&c);
2198                 N_players = (int)c;
2199                 // changed this to above two lines -- breaks on the mac because of
2200                 // endian issues
2201                 //              nd_read_byte((sbyte *)&N_players);
2202                 for (i = 0 ; i < N_players; i++) {
2203                         Players[i].cloak_time = 0;
2204                         Players[i].invulnerable_time = 0;
2205                         nd_read_string(Players[i].callsign);
2206                         nd_read_byte(&(Players[i].connected));
2207
2208                         if (Newdemo_game_mode & GM_MULTI_COOP) {
2209                                 nd_read_int(&(Players[i].score));
2210                         } else {
2211                                 nd_read_short((short *)&(Players[i].net_killed_total));
2212                                 nd_read_short((short *)&(Players[i].net_kills_total));
2213                         }
2214                 }
2215                 Game_mode = Newdemo_game_mode;
2216                 multi_sort_kill_list();
2217                 Game_mode = GM_NORMAL;
2218         } else
2219 #endif
2220                 nd_read_int(&(Players[Player_num].score));      // Note link to above if!
2221
2222         for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
2223                 nd_read_short((short*)&(Players[Player_num].primary_ammo[i]));
2224
2225         for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
2226                         nd_read_short((short*)&(Players[Player_num].secondary_ammo[i]));
2227
2228         nd_read_byte(&laser_level);
2229         if (laser_level != Players[Player_num].laser_level) {
2230                 Players[Player_num].laser_level = laser_level;
2231                 update_laser_weapon_info();
2232         }
2233
2234         // Support for missions
2235
2236         nd_read_string(current_mission);
2237         if (!load_mission_by_name(current_mission)) {
2238                 if (!rnd_demo) {
2239                         newmenu_item m[1];
2240
2241                         sprintf(text, TXT_NOMISSION4DEMO, current_mission);
2242                         m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
2243                         newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2244                 }
2245                 return 1;
2246         }
2247
2248         nd_recorded_total = 0;
2249         nd_playback_total = 0;
2250         nd_read_byte(&energy);
2251         nd_read_byte(&shield);
2252
2253         nd_read_int((int *)&(Players[Player_num].flags));
2254         if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
2255                 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2256                 Newdemo_players_cloaked |= (1 << Player_num);
2257         }
2258         if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2259                 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2260
2261         nd_read_byte((sbyte *)&Primary_weapon);
2262         nd_read_byte((sbyte *)&Secondary_weapon);
2263
2264         // Next bit of code to fix problem that I introduced between 1.0 and 1.1
2265         // check the next byte -- it _will_ be a load_new_level event.  If it is
2266         // not, then we must shift all bytes up by one.
2267
2268         Players[Player_num].energy = i2f(energy);
2269         Players[Player_num].shields = i2f(shield);
2270         JustStartedPlayback=1;
2271         return 0;
2272 }
2273
2274 void newdemo_pop_ctrlcen_triggers()
2275 {
2276         int anim_num, n, i;
2277         int side, cside;
2278         segment *seg, *csegp;
2279
2280         for (i = 0; i < ControlCenterTriggers.num_links; i++) {
2281                 seg = &Segments[ControlCenterTriggers.seg[i]];
2282                 side = ControlCenterTriggers.side[i];
2283                 csegp = &Segments[seg->children[side]];
2284                 cside = find_connect_side(seg, csegp);
2285                 anim_num = Walls[seg->sides[side].wall_num].clip_num;
2286                 n = WallAnims[anim_num].num_frames;
2287                 if (WallAnims[anim_num].flags & WCF_TMAP1) {
2288                 seg->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[n-1];
2289                 } else {
2290                         seg->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[n-1];
2291                 }
2292         }
2293 }
2294
2295 #define N_PLAYER_SHIP_TEXTURES 6
2296
2297 void nd_render_extras (ubyte,object *);
2298 extern void multi_apply_goal_textures ();
2299 ubyte Newdemo_flying_guided=0;
2300
2301 int newdemo_read_frame_information()
2302 {
2303         int done, segnum, side, objnum, soundno, angle, volume, i,shot;
2304         object *obj;
2305         ubyte c,WhichWindow;
2306         static sbyte saved_letter_cockpit;
2307         static sbyte saved_rearview_cockpit;
2308         object extraobj;
2309         static char LastReadValue=101;
2310         segment *seg;
2311
2312         done = 0;
2313
2314         if (Newdemo_vcr_state != ND_STATE_PAUSED)
2315                 for (segnum=0; segnum <= Highest_segment_index; segnum++)
2316                         Segments[segnum].objects = -1;
2317
2318         reset_objects(1);
2319         Players[Player_num].homing_object_dist = -F1_0;
2320
2321         prev_obj = NULL;
2322
2323         while( !done ) {
2324                 nd_read_byte(&c);
2325                 if (nd_bad_read) { done = -1; break; }
2326
2327                 switch( c ) {
2328
2329                 case ND_EVENT_START_FRAME: {        // Followed by an integer frame number, then a fix FrameTime
2330                         short last_frame_length;
2331
2332                         done=1;
2333                         nd_read_short(&last_frame_length);
2334                         nd_read_int(&NewdemoFrameCount);
2335                         nd_read_int((int *)&nd_recorded_time);
2336                         if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2337                                 nd_recorded_total += nd_recorded_time;
2338                         NewdemoFrameCount--;
2339
2340                         if (nd_bad_read) { done = -1; break; }
2341                         break;
2342                 }
2343
2344                 case ND_EVENT_VIEWER_OBJECT:        // Followed by an object structure
2345                         nd_read_byte (&WhichWindow);
2346                         if (WhichWindow&15)
2347                         {
2348                                 //mprintf ((0,"Reading extra!\n"));
2349                                 nd_read_object (&extraobj);
2350                                 if (Newdemo_vcr_state!=ND_STATE_PAUSED)
2351                                 {
2352                                         if (nd_bad_read) { done = -1; break; }
2353
2354                                         nd_render_extras (WhichWindow,&extraobj);
2355                                 }
2356                         }
2357                         else
2358                         {
2359                                 //mprintf ((0,"Reading viewer!\n"));
2360                                 //Viewer=&Objects[0];
2361                                 nd_read_object(Viewer);
2362
2363                                 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
2364                                         if (nd_bad_read) { done = -1; break; }
2365                                         segnum = Viewer->segnum;
2366                                         Viewer->next = Viewer->prev = Viewer->segnum = -1;
2367
2368                                         // HACK HACK HACK -- since we have multiple level recording, it can be the case
2369                                         // HACK HACK HACK -- that when rewinding the demo, the viewer is in a segment
2370                                         // HACK HACK HACK -- that is greater than the highest index of segments.  Bash
2371                                         // HACK HACK HACK -- the viewer to segment 0 for bogus view.
2372
2373                                         if (segnum > Highest_segment_index)
2374                                                 segnum = 0;
2375                                         obj_link(Viewer-Objects,segnum);
2376                                 }
2377                         }
2378                         break;
2379
2380                 case ND_EVENT_RENDER_OBJECT:       // Followed by an object structure
2381                         objnum = obj_allocate();
2382                         if (objnum==-1)
2383                                 break;
2384                         obj = &Objects[objnum];
2385                         nd_read_object(obj);
2386                         if (nd_bad_read) { done = -1; break; }
2387                         if (Newdemo_vcr_state != ND_STATE_PAUSED) {
2388                                 segnum = obj->segnum;
2389                                 obj->next = obj->prev = obj->segnum = -1;
2390
2391                                 // HACK HACK HACK -- don't render objects is segments greater than Highest_segment_index
2392                                 // HACK HACK HACK -- (see above)
2393
2394                                 if (segnum > Highest_segment_index)
2395                                         break;
2396
2397                                 obj_link(obj-Objects,segnum);
2398 #ifdef NETWORK
2399                                 if ((obj->type == OBJ_PLAYER) && (Newdemo_game_mode & GM_MULTI)) {
2400                                         int player;
2401
2402                                         if (Newdemo_game_mode & GM_TEAM)
2403                                                 player = get_team(obj->id);
2404                                         else
2405                                                 player = obj->id;
2406                                         if (player == 0)
2407                                                 break;
2408                                         player--;
2409
2410                                         for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
2411                                                 multi_player_textures[player][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[obj->rtype.pobj_info.model_num].first_texture+i]];
2412
2413                                         multi_player_textures[player][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2]];
2414                                         multi_player_textures[player][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2+1]];
2415                                         obj->rtype.pobj_info.alt_textures = player+1;
2416                                 }
2417 #endif
2418                         }
2419                         break;
2420
2421                 case ND_EVENT_SOUND:
2422                         nd_read_int(&soundno);
2423                         if (nd_bad_read) {done = -1; break; }
2424                         if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2425                                 digi_play_sample( soundno, F1_0 );
2426                         break;
2427
2428                         //--unused              case ND_EVENT_SOUND_ONCE:
2429                         //--unused                      nd_read_int(&soundno);
2430                         //--unused                      if (nd_bad_read) { done = -1; break; }
2431                         //--unused                      if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2432                         //--unused                              digi_play_sample_once( soundno, F1_0 );
2433                         //--unused                      break;
2434
2435                 case ND_EVENT_SOUND_3D:
2436                         nd_read_int(&soundno);
2437                         nd_read_int(&angle);
2438                         nd_read_int(&volume);
2439                         if (nd_bad_read) { done = -1; break; }
2440                         if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2441                                 digi_play_sample_3d( soundno, angle, volume, 0 );
2442                         break;
2443
2444                 case ND_EVENT_SOUND_3D_ONCE:
2445                         nd_read_int(&soundno);
2446                         nd_read_int(&angle);
2447                         nd_read_int(&volume);
2448                         if (nd_bad_read) { done = -1; break; }
2449                         if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2450                                 digi_play_sample_3d( soundno, angle, volume, 1 );
2451                         break;
2452
2453                 case ND_EVENT_LINK_SOUND_TO_OBJ:
2454                         {
2455                                 int soundno, objnum, max_volume, max_distance, loop_start, loop_end;
2456                                 int signature;
2457                                 nd_read_int( &soundno );
2458                                 nd_read_int( &signature );
2459                                 nd_read_int( &max_volume );
2460                                 nd_read_int( &max_distance );
2461                                 nd_read_int( &loop_start );
2462                                 nd_read_int( &loop_end );
2463                                 objnum = newdemo_find_object( signature );
2464                                 if ( objnum > -1 )  {   //  @mk, 2/22/96, John told me to.
2465                                         digi_link_sound_to_object3( soundno, objnum, 1, max_volume, max_distance, loop_start, loop_end );
2466                                 }
2467                         }
2468                         break;
2469
2470                 case ND_EVENT_KILL_SOUND_TO_OBJ:
2471                         {
2472                                 int objnum, signature;
2473                                 nd_read_int( &signature );
2474                                 objnum = newdemo_find_object( signature );
2475                                 if ( objnum > -1 )  {   //  @mk, 2/22/96, John told me to.
2476                                         digi_kill_sound_linked_to_object(objnum);
2477                                 }
2478                         }
2479                         break;
2480
2481                 case ND_EVENT_WALL_HIT_PROCESS: {
2482                         int player, segnum;
2483                         fix damage;
2484
2485                         nd_read_int(&segnum);
2486                         nd_read_int(&side);
2487                         nd_read_fix(&damage);
2488                         nd_read_int(&player);
2489                         if (nd_bad_read) { done = -1; break; }
2490                         if (Newdemo_vcr_state != ND_STATE_PAUSED)
2491                                 wall_hit_process(&Segments[segnum], side, damage, player, &(Objects[0]) );
2492                         break;
2493                 }
2494
2495                 case ND_EVENT_TRIGGER:
2496                         nd_read_int(&segnum);
2497                         nd_read_int(&side);
2498                         nd_read_int(&objnum);
2499                         nd_read_int(&shot);
2500                         if (nd_bad_read) { done = -1; break; }
2501                         if (Newdemo_vcr_state != ND_STATE_PAUSED)
2502                         {
2503                                 mprintf ((0,"EVENT TRIGGER! shot=%d\n",shot));
2504
2505                                 if (Triggers[Walls[Segments[segnum].sides[side].wall_num].trigger].type == TT_SECRET_EXIT) {
2506                                         int truth;
2507
2508                                         nd_read_byte(&c);
2509                                         Assert(c == ND_EVENT_SECRET_THINGY);
2510                                         nd_read_int(&truth);
2511                                         if (!truth)
2512                                                 check_trigger(&Segments[segnum], side, objnum,shot);
2513                                 } else
2514                                         check_trigger(&Segments[segnum], side, objnum,shot);
2515                         }
2516                         break;
2517
2518                 case ND_EVENT_HOSTAGE_RESCUED: {
2519                         int hostage_number;
2520
2521                         nd_read_int(&hostage_number);
2522                         if (nd_bad_read) { done = -1; break; }
2523                         if (Newdemo_vcr_state != ND_STATE_PAUSED)
2524                                 hostage_rescue( hostage_number );
2525                         break;
2526                 }
2527
2528                 case ND_EVENT_MORPH_FRAME: {
2529 #if 0
2530                         morph_data *md;
2531
2532                         md = &morph_objects[0];
2533                         if (newdemo_read( md->morph_vecs, sizeof(md->morph_vecs), 1 )!=1) { done=-1; break; }
2534                         if (newdemo_read( md->submodel_active, sizeof(md->submodel_active), 1 )!=1) { done=-1; break; }
2535                         if (newdemo_read( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 )!=1) { done=-1; break; }
2536 #endif
2537                         objnum = obj_allocate();
2538                         if (objnum==-1)
2539                                 break;
2540                         obj = &Objects[objnum];
2541                         nd_read_object(obj);
2542                         obj->render_type = RT_POLYOBJ;
2543                         if (Newdemo_vcr_state != ND_STATE_PAUSED) {
2544                                 if (nd_bad_read) { done = -1; break; }
2545                                 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
2546                                         segnum = obj->segnum;
2547                                         obj->next = obj->prev = obj->segnum = -1;
2548                                         obj_link(obj-Objects,segnum);
2549                                 }
2550                         }
2551                         break;
2552                 }
2553
2554                 case ND_EVENT_WALL_TOGGLE:
2555                         nd_read_int(&segnum);
2556                         nd_read_int(&side);
2557                         if (nd_bad_read) {done = -1; break; }
2558                         if (Newdemo_vcr_state != ND_STATE_PAUSED)
2559                                 wall_toggle(&Segments[segnum], side);
2560                         break;
2561
2562                 case ND_EVENT_CONTROL_CENTER_DESTROYED:
2563                         nd_read_int(&Countdown_seconds_left);
2564                         Control_center_destroyed = 1;
2565                         if (nd_bad_read) { done = -1; break; }
2566                         if (!Newdemo_cntrlcen_destroyed) {
2567                                 newdemo_pop_ctrlcen_triggers();
2568                                 Newdemo_cntrlcen_destroyed = 1;
2569                                 //do_controlcen_destroyed_stuff(NULL);
2570                         }
2571                         break;
2572
2573                 case ND_EVENT_HUD_MESSAGE: {
2574                         char hud_msg[60];
2575
2576                         nd_read_string(&(hud_msg[0]));
2577                         if (nd_bad_read) { done = -1; break; }
2578                         HUD_init_message( hud_msg );
2579                         break;
2580                         }
2581                 case ND_EVENT_START_GUIDED:
2582                         Newdemo_flying_guided=1;
2583                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2584                                 Newdemo_flying_guided=0;
2585                         break;
2586                 case ND_EVENT_END_GUIDED:
2587                         Newdemo_flying_guided=0;
2588                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2589                                 Newdemo_flying_guided=1;
2590                         break;
2591
2592                 case ND_EVENT_PALETTE_EFFECT: {
2593                         short r, g, b;
2594
2595                         nd_read_short(&r);
2596                         nd_read_short(&g);
2597                         nd_read_short(&b);
2598                         if (nd_bad_read) { done = -1; break; }
2599                         PALETTE_FLASH_SET(r,g,b);
2600                         break;
2601                 }
2602
2603                 case ND_EVENT_PLAYER_ENERGY: {
2604                         ubyte energy;
2605                         ubyte old_energy;
2606
2607                         nd_read_byte(&old_energy);
2608                         nd_read_byte(&energy);
2609                         if (nd_bad_read) {done = -1; break; }
2610                         if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2611                                 Players[Player_num].energy = i2f(energy);
2612                         } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2613                                 if (old_energy != 255)
2614                                         Players[Player_num].energy = i2f(old_energy);
2615                         }
2616                         break;
2617                 }
2618
2619                 case ND_EVENT_PLAYER_AFTERBURNER: {
2620                         ubyte afterburner;
2621                         ubyte old_afterburner;
2622
2623                         nd_read_byte(&old_afterburner);
2624                         nd_read_byte(&afterburner);
2625                         if (nd_bad_read) {done = -1; break; }
2626                         if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2627                                 Afterburner_charge = afterburner<<9;
2628                         } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2629                                 if (old_afterburner != 255)
2630                                         Afterburner_charge = old_afterburner<<9;
2631                         }
2632                         break;
2633                 }
2634
2635                 case ND_EVENT_PLAYER_SHIELD: {
2636                         ubyte shield;
2637                         ubyte old_shield;
2638
2639                         nd_read_byte(&old_shield);
2640                         nd_read_byte(&shield);
2641                         if (nd_bad_read) {done = -1; break; }
2642                         if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2643                                 Players[Player_num].shields = i2f(shield);
2644                         } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2645                                 if (old_shield != 255)
2646                                         Players[Player_num].shields = i2f(old_shield);
2647                         }
2648                         break;
2649                 }
2650
2651                 case ND_EVENT_PLAYER_FLAGS: {
2652                         uint oflags;
2653
2654                         nd_read_int((int *)&(Players[Player_num].flags));
2655                         if (nd_bad_read) {done = -1; break; }
2656
2657                         oflags = Players[Player_num].flags >> 16;
2658                         Players[Player_num].flags &= 0xffff;
2659
2660                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || ((Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) && (oflags != 0xffff)) ) {
2661                                 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
2662                                         Players[Player_num].cloak_time = 0;
2663                                         Newdemo_players_cloaked &= ~(1 << Player_num);
2664                                 }
2665                                 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
2666                                         Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2667                                         Newdemo_players_cloaked |= (1 << Player_num);
2668                                 }
2669                                 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
2670                                         Players[Player_num].invulnerable_time = 0;
2671                                 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
2672                                         Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2673                                 Players[Player_num].flags = oflags;
2674                         } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2675                                 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
2676                                         Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2677                                         Newdemo_players_cloaked |= (1 << Player_num);
2678                                 }
2679                                 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
2680                                         Players[Player_num].cloak_time = 0;
2681                                         Newdemo_players_cloaked &= ~(1 << Player_num);
2682                                 }
2683                                 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
2684                                         Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2685                                 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
2686                                         Players[Player_num].invulnerable_time = 0;
2687                         }
2688                         update_laser_weapon_info();     // in case of quad laser change
2689                         break;
2690                 }
2691
2692                 case ND_EVENT_PLAYER_WEAPON: {
2693                         sbyte weapon_type, weapon_num;
2694                         sbyte old_weapon;
2695
2696                         nd_read_byte(&weapon_type);
2697                         nd_read_byte(&weapon_num);
2698                         nd_read_byte(&old_weapon);
2699                         if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2700                                 if (weapon_type == 0)
2701                                         Primary_weapon = (int)weapon_num;
2702                                 else
2703                                         Secondary_weapon = (int)weapon_num;
2704                         } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2705                                 if (weapon_type == 0)
2706                                         Primary_weapon = (int)old_weapon;
2707                                 else
2708                                         Secondary_weapon = (int)old_weapon;
2709                         }
2710                         break;
2711                 }
2712
2713                 case ND_EVENT_EFFECT_BLOWUP: {
2714                         short segnum;
2715                         sbyte side;
2716                         vms_vector pnt;
2717                         object dummy;
2718
2719                         //create a dummy object which will be the weapon that hits
2720                         //the monitor. the blowup code wants to know who the parent of the
2721                         //laser is, so create a laser whose parent is the player
2722                         dummy.ctype.laser_info.parent_type = OBJ_PLAYER;
2723
2724                         nd_read_short(&segnum);
2725                         nd_read_byte(&side);
2726                         nd_read_vector(&pnt);
2727                         if (Newdemo_vcr_state != ND_STATE_PAUSED)
2728                                 check_effect_blowup(&(Segments[segnum]), side, &pnt, &dummy, 0);
2729                         break;
2730                 }
2731
2732                 case ND_EVENT_HOMING_DISTANCE: {
2733                         short distance;
2734
2735                         nd_read_short(&distance);
2736                         Players[Player_num].homing_object_dist = i2f((int)(distance << 16));
2737                         break;
2738                 }
2739
2740                 case ND_EVENT_LETTERBOX:
2741                         if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2742                                 saved_letter_cockpit = Cockpit_mode;
2743                                 select_cockpit(CM_LETTERBOX);
2744                         } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2745                                 select_cockpit(saved_letter_cockpit);
2746                         break;
2747
2748                 case ND_EVENT_CHANGE_COCKPIT: {
2749                         int dummy;
2750
2751                         nd_read_int (&dummy);
2752                         select_cockpit (dummy);
2753
2754                         break;
2755                 }
2756                 case ND_EVENT_REARVIEW:
2757                         if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2758                                 saved_rearview_cockpit = Cockpit_mode;
2759                                 if (Cockpit_mode == CM_FULL_COCKPIT)
2760                                         select_cockpit(CM_REAR_VIEW);
2761                                 Rear_view=1;
2762                         } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2763                                 if (saved_rearview_cockpit == CM_REAR_VIEW)     // hack to be sure we get a good cockpit on restore
2764                                         saved_rearview_cockpit = CM_FULL_COCKPIT;
2765                                 select_cockpit(saved_rearview_cockpit);
2766                                 Rear_view=0;
2767                         }
2768                         break;
2769
2770                 case ND_EVENT_RESTORE_COCKPIT:
2771                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2772                                 saved_letter_cockpit = Cockpit_mode;
2773                                 select_cockpit(CM_LETTERBOX);
2774                         } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2775                                 select_cockpit(saved_letter_cockpit);
2776                         break;
2777
2778
2779                 case ND_EVENT_RESTORE_REARVIEW:
2780                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2781                                 saved_rearview_cockpit = Cockpit_mode;
2782                                 if (Cockpit_mode == CM_FULL_COCKPIT)
2783                                         select_cockpit(CM_REAR_VIEW);
2784                                 Rear_view=1;
2785                         } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2786                                 if (saved_rearview_cockpit == CM_REAR_VIEW)     // hack to be sure we get a good cockpit on restore
2787                                         saved_rearview_cockpit = CM_FULL_COCKPIT;
2788                                 select_cockpit(saved_rearview_cockpit);
2789                                 Rear_view=0;
2790                         }
2791                         break;
2792
2793
2794                 case ND_EVENT_WALL_SET_TMAP_NUM1: {
2795                         short seg, cseg, tmap;
2796                         ubyte side,cside;
2797
2798                         nd_read_short(&seg);
2799                         nd_read_byte(&side);
2800                         nd_read_short(&cseg);
2801                         nd_read_byte(&cside);
2802                         nd_read_short( &tmap );
2803                         if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD))
2804                                 Segments[seg].sides[side].tmap_num = Segments[cseg].sides[cside].tmap_num = tmap;
2805                         break;
2806                 }
2807
2808                 case ND_EVENT_WALL_SET_TMAP_NUM2: {
2809                         short seg, cseg, tmap;
2810                         ubyte side,cside;
2811
2812                         nd_read_short(&seg);
2813                         nd_read_byte(&side);
2814                         nd_read_short(&cseg);
2815                         nd_read_byte(&cside);
2816                         nd_read_short( &tmap );
2817                         if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD)) {
2818                                 Assert(tmap!=0 && Segments[seg].sides[side].tmap_num2!=0);
2819                                 Segments[seg].sides[side].tmap_num2 = Segments[cseg].sides[cside].tmap_num2 = tmap;
2820                         }
2821                         break;
2822                 }
2823
2824                 case ND_EVENT_MULTI_CLOAK: {
2825                         sbyte pnum;
2826
2827                         nd_read_byte(&pnum);
2828                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2829                                 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2830                                 Players[pnum].cloak_time = 0;
2831                                 Newdemo_players_cloaked &= ~(1 << pnum);
2832                         } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2833                                 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2834                                 Players[pnum].cloak_time = GameTime  - (CLOAK_TIME_MAX / 2);
2835                                 Newdemo_players_cloaked |= (1 << pnum);
2836                         }
2837                         break;
2838                 }
2839
2840                 case ND_EVENT_MULTI_DECLOAK: {
2841                         sbyte pnum;
2842
2843                         nd_read_byte(&pnum);
2844
2845                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2846                                 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2847                                 Players[pnum].cloak_time = GameTime  - (CLOAK_TIME_MAX / 2);
2848                                 Newdemo_players_cloaked |= (1 << pnum);
2849                         } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2850                                 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2851                                 Players[pnum].cloak_time = 0;
2852                                 Newdemo_players_cloaked &= ~(1 << pnum);
2853                         }
2854                         break;
2855                 }
2856
2857                 case ND_EVENT_MULTI_DEATH: {
2858                         sbyte pnum;
2859
2860                         nd_read_byte(&pnum);
2861                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2862                                 Players[pnum].net_killed_total--;
2863                         else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2864                                 Players[pnum].net_killed_total++;
2865                         break;
2866                 }
2867
2868 #ifdef NETWORK
2869                 case ND_EVENT_MULTI_KILL: {
2870                         sbyte pnum, kill;
2871
2872                         nd_read_byte(&pnum);
2873                         nd_read_byte(&kill);
2874                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2875                                 Players[pnum].net_kills_total -= kill;
2876                                 if (Newdemo_game_mode & GM_TEAM)
2877                                         team_kills[get_team(pnum)] -= kill;
2878                         } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2879                                 Players[pnum].net_kills_total += kill;
2880                                 if (Newdemo_game_mode & GM_TEAM)
2881                                         team_kills[get_team(pnum)] += kill;
2882                         }
2883                         Game_mode = Newdemo_game_mode;
2884                         multi_sort_kill_list();
2885                         Game_mode = GM_NORMAL;
2886                         break;
2887                 }
2888
2889                 case ND_EVENT_MULTI_CONNECT: {
2890                         sbyte pnum, new_player;
2891                         int killed_total, kills_total;
2892                         char new_callsign[CALLSIGN_LEN+1], old_callsign[CALLSIGN_LEN+1];
2893
2894                         nd_read_byte(&pnum);
2895                         nd_read_byte(&new_player);
2896                         if (!new_player) {
2897                                 nd_read_string(old_callsign);
2898                                 nd_read_int(&killed_total);
2899                                 nd_read_int(&kills_total);
2900                         }
2901                         nd_read_string(new_callsign);
2902                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2903                                 Players[pnum].connected = 0;
2904                                 if (!new_player) {
2905                                         memcpy(Players[pnum].callsign, old_callsign, CALLSIGN_LEN+1);
2906                                         Players[pnum].net_killed_total = killed_total;
2907                                         Players[pnum].net_kills_total = kills_total;
2908                                 } else {
2909                                         N_players--;
2910                                 }
2911                         } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2912                                 Players[pnum].connected = 1;
2913                                 Players[pnum].net_kills_total = 0;
2914                                 Players[pnum].net_killed_total = 0;
2915                                 memcpy(Players[pnum].callsign, new_callsign, CALLSIGN_LEN+1);
2916                                 if (new_player)
2917                                         N_players++;
2918                         }
2919                         break;
2920                 }
2921
2922                 case ND_EVENT_MULTI_RECONNECT: {
2923                         sbyte pnum;
2924
2925                         nd_read_byte(&pnum);
2926                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2927                                 Players[pnum].connected = 0;
2928                         else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2929                                 Players[pnum].connected = 1;
2930                         break;
2931                 }
2932
2933                 case ND_EVENT_MULTI_DISCONNECT: {
2934                         sbyte pnum;
2935
2936                         nd_read_byte(&pnum);
2937                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2938                                 Players[pnum].connected = 1;
2939                         else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2940                                 Players[pnum].connected = 0;
2941                         break;
2942                 }
2943
2944                 case ND_EVENT_MULTI_SCORE: {
2945                         int score;
2946                         sbyte pnum;
2947
2948                         nd_read_byte(&pnum);
2949                         nd_read_int(&score);
2950                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2951                                 Players[pnum].score -= score;
2952                         else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2953                                 Players[pnum].score += score;
2954                         Game_mode = Newdemo_game_mode;
2955                         multi_sort_kill_list();
2956                         Game_mode = GM_NORMAL;
2957                         break;
2958                 }
2959
2960 #endif // NETWORK
2961                 case ND_EVENT_PLAYER_SCORE: {
2962                         int score;
2963
2964                         nd_read_int(&score);
2965                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2966                                 Players[Player_num].score -= score;
2967                         else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2968                                 Players[Player_num].score += score;
2969                         break;
2970                 }
2971
2972
2973                 case ND_EVENT_PRIMARY_AMMO: {
2974                         short old_ammo, new_ammo;
2975
2976                         nd_read_short(&old_ammo);
2977                         nd_read_short(&new_ammo);
2978
2979                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2980                                 Players[Player_num].primary_ammo[Primary_weapon] = old_ammo;
2981                         else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2982                                 Players[Player_num].primary_ammo[Primary_weapon] = new_ammo;
2983                         break;
2984                 }
2985
2986                 case ND_EVENT_SECONDARY_AMMO: {
2987                         short old_ammo, new_ammo;
2988
2989                         nd_read_short(&old_ammo);
2990                         nd_read_short(&new_ammo);
2991
2992                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2993                                 Players[Player_num].secondary_ammo[Secondary_weapon] = old_ammo;
2994                         else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2995                                 Players[Player_num].secondary_ammo[Secondary_weapon] = new_ammo;
2996                         break;
2997                 }
2998
2999                 case ND_EVENT_DOOR_OPENING: {
3000                         short segnum;
3001                         sbyte side;
3002
3003                         nd_read_short(&segnum);
3004                         nd_read_byte(&side);
3005                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
3006                                 int anim_num;
3007                                 int cside;
3008                                 segment *segp, *csegp;
3009
3010                                 segp = &Segments[segnum];
3011                                 csegp = &Segments[segp->children[side]];
3012                                 cside = find_connect_side(segp, csegp);
3013                                 anim_num = Walls[segp->sides[side].wall_num].clip_num;
3014
3015                                 if (WallAnims[anim_num].flags & WCF_TMAP1) {
3016                                         segp->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[0];
3017                                 } else {
3018                                         segp->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[0];
3019                                 }
3020                         }
3021                         break;
3022                 }
3023
3024                 case ND_EVENT_LASER_LEVEL: {
3025                         sbyte old_level, new_level;
3026
3027                         nd_read_byte(&old_level);
3028                         nd_read_byte(&new_level);
3029                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
3030                                 Players[Player_num].laser_level = old_level;
3031                                 update_laser_weapon_info();
3032                         } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
3033                                 Players[Player_num].laser_level = new_level;
3034                                 update_laser_weapon_info();
3035                         }
3036                         break;
3037                 }
3038
3039                 case ND_EVENT_CLOAKING_WALL: {
3040                         ubyte back_wall_num,front_wall_num,type,state,cloak_value;
3041                         short l0,l1,l2,l3;
3042                         segment *segp;
3043                         int sidenum;
3044
3045                         nd_read_byte(&front_wall_num);
3046                         nd_read_byte(&back_wall_num);
3047                         nd_read_byte(&type);
3048                         nd_read_byte(&state);
3049                         nd_read_byte(&cloak_value);
3050                         nd_read_short(&l0);
3051                         nd_read_short(&l1);
3052                         nd_read_short(&l2);
3053                         nd_read_short(&l3);
3054
3055                         Walls[front_wall_num].type = type;
3056                         Walls[front_wall_num].state = state;
3057                         Walls[front_wall_num].cloak_value = cloak_value;
3058                         segp = &Segments[Walls[front_wall_num].segnum];
3059                         sidenum = Walls[front_wall_num].sidenum;
3060                         segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
3061                         segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
3062                         segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
3063                         segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
3064
3065                         Walls[back_wall_num].type = type;
3066                         Walls[back_wall_num].state = state;
3067                         Walls[back_wall_num].cloak_value = cloak_value;
3068                         segp = &Segments[Walls[back_wall_num].segnum];
3069                         sidenum = Walls[back_wall_num].sidenum;
3070                         segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
3071                         segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
3072                         segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
3073                         segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
3074
3075                         break;
3076                 }
3077
3078                 case ND_EVENT_NEW_LEVEL: {
3079                         sbyte new_level, old_level, loaded_level;
3080
3081                         nd_read_byte (&new_level);
3082                         nd_read_byte (&old_level);
3083                         if (Newdemo_vcr_state == ND_STATE_PAUSED)
3084                                 break;
3085
3086                         stop_time();
3087                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
3088                                 loaded_level = old_level;
3089                         else {
3090                                 loaded_level = new_level;
3091                                 for (i = 0; i < MAX_PLAYERS; i++) {
3092                                         Players[i].cloak_time = 0;
3093                                         Players[i].flags &= ~PLAYER_FLAGS_CLOAKED;
3094                                 }
3095                         }
3096                         if ((loaded_level < Last_secret_level) || (loaded_level > Last_level)) {
3097                                 newmenu_item m[3];
3098
3099                                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
3100                                 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
3101                                 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
3102                                 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
3103                                 return -1;
3104                         }
3105
3106                         LoadLevel((int)loaded_level,1);
3107                         Newdemo_cntrlcen_destroyed = 0;
3108
3109                         if (JustStartedPlayback)
3110                         {
3111                                 nd_read_int (&Num_walls);
3112                                 for (i=0;i<Num_walls;i++)    // restore the walls
3113                                 {
3114                                         nd_read_byte (&Walls[i].type);
3115                                         nd_read_byte (&Walls[i].flags);
3116                                         nd_read_byte (&Walls[i].state);
3117
3118                                         seg = &Segments[Walls[i].segnum];
3119                                         side = Walls[i].sidenum;
3120                                         nd_read_short (&seg->sides[side].tmap_num);
3121                                         nd_read_short (&seg->sides[side].tmap_num2);
3122                                 }
3123 #ifdef NETWORK
3124                                 if (Newdemo_game_mode & GM_CAPTURE)
3125                                         multi_apply_goal_textures ();
3126 #endif
3127                                 JustStartedPlayback=0;
3128                         }
3129
3130
3131                         // so says Rob H.!!!                    if (Newdemo_game_mode & GM_MULTI) {
3132                         // so says Rob H.!!!                            for (i = 0; i < Num_walls; i++) {
3133                         // so says Rob H.!!!                                    if (Walls[i].type == WALL_BLASTABLE)
3134                         // so says Rob H.!!!                                    {
3135                         // so says Rob H.!!!                                            int a, n;
3136                         // so says Rob H.!!!                                            int side;
3137                         // so says Rob H.!!!                                            segment *seg;
3138                         // so says Rob H.!!!
3139                         // so says Rob H.!!!                                            seg = &Segments[Walls[i].segnum];
3140                         // so says Rob H.!!!                                            side = Walls[i].sidenum;
3141                         // so says Rob H.!!!                                            a = Walls[i].clip_num;
3142                         // so says Rob H.!!!                                            n = WallAnims[a].num_frames;
3143                         // so says Rob H.!!!                                            seg->sides[side].tmap_num = WallAnims[a].frames[n-1];
3144                         // so says Rob H.!!!                                            Walls[i].flags |= WALL_BLASTED;
3145                         // so says Rob H.!!!                                    }
3146                         // so says Rob H.!!!                            }
3147                         // so says Rob H.!!!                    }
3148
3149                         reset_palette_add();                // get palette back to normal
3150                         start_time();
3151                         break;
3152                 }
3153
3154                 case ND_EVENT_EOF: {
3155                         done=-1;
3156                         cfseek(infile, -1, SEEK_CUR);        // get back to the EOF marker
3157                         Newdemo_at_eof = 1;
3158                         NewdemoFrameCount++;
3159                         break;
3160                 }
3161
3162                 default:
3163                         Int3();
3164                 }
3165         }
3166
3167         LastReadValue=c;
3168
3169         if (nd_bad_read) {
3170                 newmenu_item m[2];
3171
3172                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_ERR_READING;
3173                 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_DEMO_OLD_CORRUPT;
3174                 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
3175         }
3176
3177         return done;
3178 }
3179
3180 void newdemo_goto_beginning()
3181 {
3182         //if (NewdemoFrameCount == 0)
3183         //      return;
3184         cfseek(infile, 0, SEEK_SET);
3185         Newdemo_vcr_state = ND_STATE_PLAYBACK;
3186         if (newdemo_read_demo_start(0))
3187                 newdemo_stop_playback();
3188         if (newdemo_read_frame_information() == -1)
3189                 newdemo_stop_playback();
3190         if (newdemo_read_frame_information() == -1)
3191                 newdemo_stop_playback();
3192         Newdemo_vcr_state = ND_STATE_PAUSED;
3193         Newdemo_at_eof = 0;
3194 }
3195
3196 void newdemo_goto_end()
3197 {
3198         short frame_length, byte_count, bshort;
3199         sbyte level, bbyte, laser_level;
3200         ubyte energy, shield, c;
3201         int i, loc, bint;
3202
3203         cfseek(infile, -2, SEEK_END);
3204         nd_read_byte(&level);
3205
3206         if ((level < Last_secret_level) || (level > Last_level)) {
3207                 newmenu_item m[3];
3208
3209                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
3210                 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
3211                 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
3212                 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
3213                 newdemo_stop_playback();
3214                 return;
3215         }
3216         if (level != Current_level_num)
3217                 LoadLevel(level,1);
3218
3219         cfseek(infile, -4, SEEK_END);
3220         nd_read_short(&byte_count);
3221         cfseek(infile, -2 - byte_count, SEEK_CUR);
3222
3223         nd_read_short(&frame_length);
3224         loc = cftell(infile);
3225         if (Newdemo_game_mode & GM_MULTI)
3226                 nd_read_byte(&Newdemo_players_cloaked);
3227         else
3228                 nd_read_byte(&bbyte);
3229         nd_read_byte(&bbyte);
3230         nd_read_short(&bshort);
3231         nd_read_int(&bint);
3232
3233         nd_read_byte(&energy);
3234         nd_read_byte(&shield);
3235         Players[Player_num].energy = i2f(energy);
3236         Players[Player_num].shields = i2f(shield);
3237         nd_read_int((int *)&(Players[Player_num].flags));
3238         if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
3239                 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
3240                 Newdemo_players_cloaked |= (1 << Player_num);
3241         }
3242         if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
3243                 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
3244         nd_read_byte((sbyte *)&Primary_weapon);
3245         nd_read_byte((sbyte *)&Secondary_weapon);
3246         for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
3247                 nd_read_short((short *)&(Players[Player_num].primary_ammo[i]));
3248         for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
3249                 nd_read_short((short *)&(Players[Player_num].secondary_ammo[i]));
3250         nd_read_byte(&laser_level);
3251         if (laser_level != Players[Player_num].laser_level) {
3252                 Players[Player_num].laser_level = laser_level;
3253                 update_laser_weapon_info();
3254         }
3255
3256         if (Newdemo_game_mode & GM_MULTI) {
3257                 nd_read_byte(&c);
3258                 N_players = (int)c;
3259                 // see newdemo_read_start_demo for explanation of
3260                 // why this is commented out
3261                 //              nd_read_byte((sbyte *)&N_players);
3262                 for (i = 0; i < N_players; i++) {
3263                         nd_read_string(Players[i].callsign);
3264                         nd_read_byte(&(Players[i].connected));
3265                         if (Newdemo_game_mode & GM_MULTI_COOP) {
3266                                 nd_read_int(&(Players[i].score));
3267                         } else {
3268                                 nd_read_short((short *)&(Players[i].net_killed_total));
3269                                 nd_read_short((short *)&(Players[i].net_kills_total));
3270                         }
3271                 }
3272         } else {
3273                 nd_read_int(&(Players[Player_num].score));
3274         }
3275
3276         cfseek(infile, loc, SEEK_SET);
3277         cfseek(infile, -frame_length, SEEK_CUR);
3278         nd_read_int(&NewdemoFrameCount);            // get the frame count
3279         NewdemoFrameCount--;
3280         cfseek(infile, 4, SEEK_CUR);
3281         Newdemo_vcr_state = ND_STATE_PLAYBACK;
3282         newdemo_read_frame_information();           // then the frame information
3283         Newdemo_vcr_state = ND_STATE_PAUSED;
3284         return;
3285 }
3286
3287 void newdemo_back_frames(int frames)
3288 {
3289         short last_frame_length;
3290         int i;
3291
3292         for (i = 0; i < frames; i++)
3293         {
3294                 cfseek(infile, -10, SEEK_CUR);
3295                 nd_read_short(&last_frame_length);
3296                 cfseek(infile, 8 - last_frame_length, SEEK_CUR);
3297
3298                 if (!Newdemo_at_eof && newdemo_read_frame_information() == -1) {
3299                         newdemo_stop_playback();
3300                         return;
3301                 }
3302                 if (Newdemo_at_eof)
3303                         Newdemo_at_eof = 0;
3304
3305                 cfseek(infile, -10, SEEK_CUR);
3306                 nd_read_short(&last_frame_length);
3307                 cfseek(infile, 8 - last_frame_length, SEEK_CUR);
3308         }
3309
3310 }
3311
3312 /*
3313  *  routine to interpolate the viewer position.  the current position is
3314  *  stored in the Viewer object.  Save this position, and read the next
3315  *  frame to get all objects read in.  Calculate the delta playback and
3316  *  the delta recording frame times between the two frames, then intepolate
3317  *  the viewers position accordingly.  nd_recorded_time is the time that it
3318  *  took the recording to render the frame that we are currently looking
3319  *  at.
3320 */
3321
3322 void interpolate_frame(fix d_play, fix d_recorded)
3323 {
3324         int i, j, num_cur_objs;
3325         fix factor;
3326         object *cur_objs;
3327
3328         factor = fixdiv(d_play, d_recorded);
3329         if (factor > F1_0)
3330                 factor = F1_0;
3331
3332         num_cur_objs = Highest_object_index;
3333         cur_objs = (object *)d_malloc(sizeof(object) * (num_cur_objs + 1));
3334         if (cur_objs == NULL) {
3335                 mprintf((0,"Couldn't get %d bytes for cur_objs in interpolate_frame\n", sizeof(object) * num_cur_objs));
3336                 Int3();
3337                 return;
3338         }
3339         for (i = 0; i <= num_cur_objs; i++)
3340                 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
3341
3342         Newdemo_vcr_state = ND_STATE_PAUSED;
3343         if (newdemo_read_frame_information() == -1) {
3344                 d_free(cur_objs);
3345                 newdemo_stop_playback();
3346                 return;
3347         }
3348
3349         for (i = 0; i <= num_cur_objs; i++) {
3350                 for (j = 0; j <= Highest_object_index; j++) {
3351                         if (cur_objs[i].signature == Objects[j].signature) {
3352                                 ubyte render_type = cur_objs[i].render_type;
3353                                 //fix delta_p, delta_h, delta_b;
3354                                 fix delta_x, delta_y, delta_z;
3355                                 //vms_angvec cur_angles, dest_angles;
3356
3357                                 //  Extract the angles from the object orientation matrix.
3358                                 //  Some of this code taken from ai_turn_towards_vector
3359                                 //  Don't do the interpolation on certain render types which don't use an orientation matrix
3360
3361                                 if (!((render_type == RT_LASER) || (render_type == RT_FIREBALL) || (render_type == RT_POWERUP))) {
3362
3363                                         vms_vector  fvec1, fvec2, rvec1, rvec2;
3364                                         fix         mag1;
3365
3366                                         fvec1 = cur_objs[i].orient.fvec;
3367                                         vm_vec_scale(&fvec1, F1_0-factor);
3368                                         fvec2 = Objects[j].orient.fvec;
3369                                         vm_vec_scale(&fvec2, factor);
3370                                         vm_vec_add2(&fvec1, &fvec2);
3371                                         mag1 = vm_vec_normalize_quick(&fvec1);
3372                                         if (mag1 > F1_0/256) {
3373                                                 rvec1 = cur_objs[i].orient.rvec;
3374                                                 vm_vec_scale(&rvec1, F1_0-factor);
3375                                                 rvec2 = Objects[j].orient.rvec;
3376                                                 vm_vec_scale(&rvec2, factor);
3377                                                 vm_vec_add2(&rvec1, &rvec2);
3378                                                 vm_vec_normalize_quick(&rvec1); // Note: Doesn't matter if this is null, if null, vm_vector_2_matrix will just use fvec1
3379                                                 vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
3380                                         }
3381
3382                                         //--old new way --      vms_vector fvec1, fvec2, rvec1, rvec2;
3383                                         //--old new way --
3384                                         //--old new way --      fvec1 = cur_objs[i].orient.fvec;
3385                                         //--old new way --      vm_vec_scale(&fvec1, F1_0-factor);
3386                                         //--old new way --      fvec2 = Objects[j].orient.fvec;
3387                                         //--old new way --      vm_vec_scale(&fvec2, factor);
3388                                         //--old new way --      vm_vec_add2(&fvec1, &fvec2);
3389                                         //--old new way --      vm_vec_normalize_quick(&fvec1);
3390                                         //--old new way --
3391                                         //--old new way --      rvec1 = cur_objs[i].orient.rvec;
3392                                         //--old new way --      vm_vec_scale(&rvec1, F1_0-factor);
3393                                         //--old new way --      rvec2 = Objects[j].orient.rvec;
3394                                         //--old new way --      vm_vec_scale(&rvec2, factor);
3395                                         //--old new way --      vm_vec_add2(&rvec1, &rvec2);
3396                                         //--old new way --      vm_vec_normalize_quick(&rvec1);
3397                                         //--old new way --
3398                                         //--old new way --      vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
3399
3400                                         // -- old fashioned way --                                      vm_extract_angles_matrix(&cur_angles, &(cur_objs[i].orient));
3401                                         // -- old fashioned way --                                      vm_extract_angles_matrix(&dest_angles, &(Objects[j].orient));
3402                                         // -- old fashioned way --
3403                                         // -- old fashioned way --                                      delta_p = (dest_angles.p - cur_angles.p);
3404                                         // -- old fashioned way --                                      delta_h = (dest_angles.h - cur_angles.h);
3405                                         // -- old fashioned way --                                      delta_b = (dest_angles.b - cur_angles.b);
3406                                         // -- old fashioned way --
3407                                         // -- old fashioned way --                                      if (delta_p != 0) {
3408                                         // -- old fashioned way --                                              if (delta_p > F1_0/2) delta_p = dest_angles.p - cur_angles.p - F1_0;
3409                                         // -- old fashioned way --                                              if (delta_p < -F1_0/2) delta_p = dest_angles.p - cur_angles.p + F1_0;
3410                                         // -- old fashioned way --                                              delta_p = fixmul(delta_p, factor);
3411                                         // -- old fashioned way --                                              cur_angles.p += delta_p;
3412                                         // -- old fashioned way --                                      }
3413                                         // -- old fashioned way --                                      if (delta_h != 0) {
3414                                         // -- old fashioned way --                                              if (delta_h > F1_0/2) delta_h = dest_angles.h - cur_angles.h - F1_0;
3415                                         // -- old fashioned way --                                              if (delta_h < -F1_0/2) delta_h = dest_angles.h - cur_angles.h + F1_0;
3416                                         // -- old fashioned way --                                              delta_h = fixmul(delta_h, factor);
3417                                         // -- old fashioned way --                                              cur_angles.h += delta_h;
3418                                         // -- old fashioned way --                                      }
3419                                         // -- old fashioned way --                                      if (delta_b != 0) {
3420                                         // -- old fashioned way --                                              if (delta_b > F1_0/2) delta_b = dest_angles.b - cur_angles.b - F1_0;
3421                                         // -- old fashioned way --                                              if (delta_b < -F1_0/2) delta_b = dest_angles.b - cur_angles.b + F1_0;
3422                                         // -- old fashioned way --                                              delta_b = fixmul(delta_b, factor);
3423                                         // -- old fashioned way --                                              cur_angles.b += delta_b;
3424                                         // -- old fashioned way --                                      }
3425                                 }
3426
3427                                 // Interpolate the object position.  This is just straight linear
3428                                 // interpolation.
3429
3430                                 delta_x = Objects[j].pos.x - cur_objs[i].pos.x;
3431                                 delta_y = Objects[j].pos.y - cur_objs[i].pos.y;
3432                                 delta_z = Objects[j].pos.z - cur_objs[i].pos.z;
3433
3434                                 delta_x = fixmul(delta_x, factor);
3435                                 delta_y = fixmul(delta_y, factor);
3436                                 delta_z = fixmul(delta_z, factor);
3437
3438                                 cur_objs[i].pos.x += delta_x;
3439                                 cur_objs[i].pos.y += delta_y;
3440                                 cur_objs[i].pos.z += delta_z;
3441
3442                                 // -- old fashioned way --// stuff the new angles back into the object structure
3443                                 // -- old fashioned way --                              vm_angles_2_matrix(&(cur_objs[i].orient), &cur_angles);
3444                         }
3445                 }
3446         }
3447
3448         // get back to original position in the demo file.  Reread the current
3449         // frame information again to reset all of the object stuff not covered
3450         // with Highest_object_index and the object array (previously rendered
3451         // objects, etc....)
3452
3453         newdemo_back_frames(1);
3454         newdemo_back_frames(1);
3455         if (newdemo_read_frame_information() == -1)
3456                 newdemo_stop_playback();
3457         Newdemo_vcr_state = ND_STATE_PLAYBACK;
3458
3459         for (i = 0; i <= num_cur_objs; i++)
3460                 memcpy(&(Objects[i]), &(cur_objs[i]), sizeof(object));
3461         Highest_object_index = num_cur_objs;
3462         d_free(cur_objs);
3463 }
3464
3465 void newdemo_playback_one_frame()
3466 {
3467         int frames_back, i, level;
3468         static fix base_interpol_time = 0;
3469         static fix d_recorded = 0;
3470
3471         for (i = 0; i < MAX_PLAYERS; i++)
3472                 if (Newdemo_players_cloaked & (1 << i))
3473                         Players[i].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
3474
3475         if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
3476                 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
3477
3478         if (Newdemo_vcr_state == ND_STATE_PAUSED)       // render a frame or not
3479                 return;
3480
3481         if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
3482                 DoJasonInterpolate(nd_recorded_time);
3483
3484         Control_center_destroyed = 0;
3485         Countdown_seconds_left = -1;
3486         PALETTE_FLASH_SET(0,0,0);       //clear flash
3487
3488         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
3489         {
3490                 level = Current_level_num;
3491                 if (NewdemoFrameCount == 0)
3492                         return;
3493                 else if ((Newdemo_vcr_state == ND_STATE_REWINDING) && (NewdemoFrameCount < 10)) {
3494                         newdemo_goto_beginning();
3495                         return;
3496                 }
3497                 if (Newdemo_vcr_state == ND_STATE_REWINDING)
3498                         frames_back = 10;
3499                 else
3500                         frames_back = 1;
3501                 if (Newdemo_at_eof) {
3502                         cfseek(infile, 11, SEEK_CUR);
3503                 }
3504                 newdemo_back_frames(frames_back);
3505
3506                 if (level != Current_level_num)
3507                         newdemo_pop_ctrlcen_triggers();
3508
3509                 if (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) {
3510                         if (level != Current_level_num)
3511                                 newdemo_back_frames(1);
3512                         Newdemo_vcr_state = ND_STATE_PAUSED;
3513                 }
3514         }
3515         else if (Newdemo_vcr_state == ND_STATE_FASTFORWARD) {
3516                 if (!Newdemo_at_eof)
3517                 {
3518                         for (i = 0; i < 10; i++)
3519                         {
3520                                 if (newdemo_read_frame_information() == -1)
3521                                 {
3522                                         if (Newdemo_at_eof)
3523                                                 Newdemo_vcr_state = ND_STATE_PAUSED;
3524                                         else
3525                                                 newdemo_stop_playback();
3526                                         break;
3527                                 }
3528                         }
3529                 }
3530                 else
3531                         Newdemo_vcr_state = ND_STATE_PAUSED;
3532         }
3533         else if (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD) {
3534                 if (!Newdemo_at_eof) {
3535                         level = Current_level_num;
3536                         if (newdemo_read_frame_information() == -1) {
3537                                 if (!Newdemo_at_eof)
3538                                         newdemo_stop_playback();
3539                         }
3540                         if (level != Current_level_num) {
3541                                 if (newdemo_read_frame_information() == -1) {
3542                                         if (!Newdemo_at_eof)
3543                                                 newdemo_stop_playback();
3544                                 }
3545                         }
3546                         Newdemo_vcr_state = ND_STATE_PAUSED;
3547                 } else
3548                         Newdemo_vcr_state = ND_STATE_PAUSED;
3549         }
3550         else {
3551
3552                 //  First, uptate the total playback time to date.  Then we check to see
3553                 //  if we need to change the playback style to interpolate frames or
3554                 //  skip frames based on where the playback time is relative to the
3555                 //  recorded time.
3556
3557                 if (NewdemoFrameCount <= 0)
3558                         nd_playback_total = nd_recorded_total;      // baseline total playback time
3559                 else
3560                         nd_playback_total += FrameTime;
3561                 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
3562                         if ((nd_playback_total * INTERPOL_FACTOR) < nd_recorded_total) {
3563                                 playback_style = INTERPOLATE_PLAYBACK;
3564                                 nd_playback_total = nd_recorded_total + FrameTime;  // baseline playback time
3565                                 base_interpol_time = nd_recorded_total;
3566                                 d_recorded = nd_recorded_time;                      // baseline delta recorded
3567                         }
3568                 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
3569                         if (nd_playback_total > nd_recorded_total)
3570                                 playback_style = SKIP_PLAYBACK;
3571
3572
3573                 if ((playback_style == INTERPOLATE_PLAYBACK) && Newdemo_do_interpolate) {
3574                         fix d_play = 0;
3575
3576                         if (nd_recorded_total - nd_playback_total < FrameTime) {
3577                                 d_recorded = nd_recorded_total - nd_playback_total;
3578
3579                                 while (nd_recorded_total - nd_playback_total < FrameTime) {
3580                                         object *cur_objs;
3581                                         int i, j, num_objs, level;
3582
3583                                         num_objs = Highest_object_index;
3584                                         cur_objs = (object *)d_malloc(sizeof(object) * (num_objs + 1));
3585                                         if (cur_objs == NULL) {
3586                                                 Warning ("Couldn't get %d bytes for objects in interpolate playback\n", sizeof(object) * num_objs);
3587                                                 break;
3588                                         }
3589                                         for (i = 0; i <= num_objs; i++)
3590                                                 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
3591
3592                                         level = Current_level_num;
3593                                         if (newdemo_read_frame_information() == -1) {
3594                                                 d_free(cur_objs);
3595                                                 newdemo_stop_playback();
3596                                                 return;
3597                                         }
3598                                         if (level != Current_level_num) {
3599                                                 d_free(cur_objs);
3600                                                 if (newdemo_read_frame_information() == -1)
3601                                                         newdemo_stop_playback();
3602                                                 break;
3603                                         }
3604
3605                                         //  for each new object in the frame just read in, determine if there is
3606                                         //  a corresponding object that we have been interpolating.  If so, then
3607                                         //  copy that interpolated object to the new Objects array so that the
3608                                         //  interpolated position and orientation can be preserved.
3609
3610                                         for (i = 0; i <= num_objs; i++) {
3611                                                 for (j = 0; j <= Highest_object_index; j++) {
3612                                                         if (cur_objs[i].signature == Objects[j].signature) {
3613                                                                 memcpy(&(Objects[j].orient), &(cur_objs[i].orient), sizeof(vms_matrix));
3614                                                                 memcpy(&(Objects[j].pos), &(cur_objs[i].pos), sizeof(vms_vector));
3615                                                                 break;
3616                                                         }
3617                                                 }
3618                                         }
3619                                         d_free(cur_objs);
3620                                         d_recorded += nd_recorded_time;
3621                                         base_interpol_time = nd_playback_total - FrameTime;
3622                                 }
3623                         }
3624
3625                         d_play = nd_playback_total - base_interpol_time;
3626                         interpolate_frame(d_play, d_recorded);
3627                         return;
3628                 }
3629                 else {
3630                         //mprintf ((0, "*"));
3631                         if (newdemo_read_frame_information() == -1) {
3632                                 newdemo_stop_playback();
3633                                 return;
3634                         }
3635                         if (playback_style == SKIP_PLAYBACK) {
3636                                 //mprintf ((0, "."));
3637                                 while (nd_playback_total > nd_recorded_total) {
3638                                         if (newdemo_read_frame_information() == -1) {
3639                                                 newdemo_stop_playback();
3640                                                 return;
3641                                         }
3642                                 }
3643                         }
3644                 }
3645         }
3646 }
3647
3648 void newdemo_start_recording()
3649 {
3650 #ifdef WINDOWS
3651         Newdemo_size=GetFreeDiskSpace();
3652         mprintf((0, "Free space = %d\n", Newdemo_size));
3653 #else
3654         Newdemo_size = GetDiskFree();
3655 #endif
3656
3657         Newdemo_size -= 100000;
3658
3659         if ((Newdemo_size+100000) <  2000000000) {
3660                 if (((int)(Newdemo_size)) < 500000) {
3661 #ifndef MACINTOSH
3662                         nm_messagebox(NULL, 1, TXT_OK, TXT_DEMO_NO_SPACE);
3663 #else
3664                         nm_messagebox(NULL, 1, TXT_OK, "Not enough space on current\ndrive to start demo recording.");
3665 #endif
3666                         return;
3667                 }
3668         }
3669
3670         Newdemo_num_written = 0;
3671         Newdemo_no_space=0;
3672         Newdemo_state = ND_STATE_RECORDING;
3673         outfile = cfopen(DEMO_FILENAME, "wb");
3674
3675 #if !defined(MACINTOSH) && !defined(_WIN32_WCE)
3676         if (outfile == NULL && errno == ENOENT) {   //dir doesn't exist?
3677 #else
3678         if (outfile == NULL) {                      //dir doesn't exist and no errno on mac!
3679 #endif
3680                 cfile_mkdir(DEMO_DIR); //try making directory
3681                 outfile = cfopen(DEMO_FILENAME, "wb");
3682         }
3683
3684         if (outfile == NULL)
3685         {
3686                 nm_messagebox(NULL, 1, TXT_OK, "Cannot open demo temp file");
3687                 Newdemo_state = ND_STATE_NORMAL;
3688         }
3689         else
3690                 newdemo_record_start_demo();
3691
3692 }
3693
3694 char demoname_allowed_chars[] = "azAZ09__--";
3695 void newdemo_stop_recording()
3696 {
3697         newmenu_item m[6];
3698         int l, exit;
3699         static char filename[15] = "", *s;
3700         static ubyte tmpcnt = 0;
3701         ubyte cloaked = 0;
3702         char fullname[15+FILENAME_LEN] = DEMO_DIR;
3703         unsigned short byte_count = 0;
3704
3705         exit = 0;
3706
3707         nd_write_byte(ND_EVENT_EOF);
3708         nd_write_short(frame_bytes_written - 1);
3709         if (Game_mode & GM_MULTI) {
3710                 for (l = 0; l < N_players; l++) {
3711                         if (Players[l].flags & PLAYER_FLAGS_CLOAKED)
3712                                 cloaked |= (1 << l);
3713                 }
3714                 nd_write_byte(cloaked);
3715                 nd_write_byte(ND_EVENT_EOF);
3716         } else {
3717                 nd_write_short(ND_EVENT_EOF);
3718         }
3719         nd_write_short(ND_EVENT_EOF);
3720         nd_write_int(ND_EVENT_EOF);
3721
3722         byte_count += 10;       // from frame_bytes_written
3723
3724         nd_write_byte((sbyte)(f2ir(Players[Player_num].energy)));
3725         nd_write_byte((sbyte)(f2ir(Players[Player_num].shields)));
3726         nd_write_int(Players[Player_num].flags);        // be sure players flags are set
3727         nd_write_byte((sbyte)Primary_weapon);
3728         nd_write_byte((sbyte)Secondary_weapon);
3729         byte_count += 8;
3730
3731         for (l = 0; l < MAX_PRIMARY_WEAPONS; l++)
3732                 nd_write_short((short)Players[Player_num].primary_ammo[l]);
3733
3734         for (l = 0; l < MAX_SECONDARY_WEAPONS; l++)
3735                 nd_write_short((short)Players[Player_num].secondary_ammo[l]);
3736         byte_count += (sizeof(short) * (MAX_PRIMARY_WEAPONS + MAX_SECONDARY_WEAPONS));
3737
3738         nd_write_byte(Players[Player_num].laser_level);
3739         byte_count++;
3740
3741         if (Game_mode & GM_MULTI) {
3742                 nd_write_byte((sbyte)N_players);
3743                 byte_count++;
3744                 for (l = 0; l < N_players; l++) {
3745                         nd_write_string(Players[l].callsign);
3746                         byte_count += (strlen(Players[l].callsign) + 2);
3747                         nd_write_byte(Players[l].connected);
3748                         if (Game_mode & GM_MULTI_COOP) {
3749                                 nd_write_int(Players[l].score);
3750                                 byte_count += 5;
3751                         } else {
3752                                 nd_write_short((short)Players[l].net_killed_total);
3753                                 nd_write_short((short)Players[l].net_kills_total);
3754                                 byte_count += 5;
3755                         }
3756                 }
3757         } else {
3758                 nd_write_int(Players[Player_num].score);
3759                 byte_count += 4;
3760         }
3761         nd_write_short(byte_count);
3762
3763         nd_write_byte(Current_level_num);
3764         nd_write_byte(ND_EVENT_EOF);
3765
3766         l = cftell(outfile);
3767         cfclose(outfile);
3768         outfile = NULL;
3769         Newdemo_state = ND_STATE_NORMAL;
3770         gr_palette_load( gr_palette );
3771
3772         if (filename[0] != '\0') {
3773                 int num, i = strlen(filename) - 1;
3774                 char newfile[15];
3775
3776                 while (isdigit(filename[i])) {
3777                         i--;
3778                         if (i == -1)
3779                                 break;
3780                 }
3781                 i++;
3782                 num = atoi(&(filename[i]));
3783                 num++;
3784                 filename[i] = '\0';
3785                 sprintf (newfile, "%s%d", filename, num);
3786                 strncpy(filename, newfile, 8);
3787                 filename[8] = '\0';
3788         }
3789
3790 try_again:
3791         ;
3792
3793         Newmenu_allowed_chars = demoname_allowed_chars;
3794         if (!Newdemo_no_space) {
3795                 m[0].type=NM_TYPE_INPUT; m[0].text_len = 8; m[0].text = filename;
3796                 exit = newmenu_do( NULL, TXT_SAVE_DEMO_AS, 1, &(m[0]), NULL );
3797         } else if (Newdemo_no_space == 1) {
3798                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_BAD;
3799                 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3800                 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3801         } else if (Newdemo_no_space == 2) {
3802                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_NOSPACE;
3803                 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3804                 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3805         }
3806         Newmenu_allowed_chars = NULL;
3807
3808         if (exit == -2) {                   // got bumped out from network menu
3809                 char save_file[7+FILENAME_LEN];
3810
3811                 if (filename[0] != '\0') {
3812                         strcpy(save_file, DEMO_DIR);
3813                         strcat(save_file, filename);
3814                         strcat(save_file, ".dem");
3815                 } else
3816                         sprintf (save_file, "%stmp%d.dem", DEMO_DIR, tmpcnt++);
3817                 cfile_delete(save_file);
3818                 cfile_rename(DEMO_FILENAME, save_file);
3819                 return;
3820         }
3821         if (exit == -1) {               // pressed ESC
3822                 cfile_delete(DEMO_FILENAME);      // might as well remove the file
3823                 return;                     // return without doing anything
3824         }
3825
3826         if (filename[0]==0) //null string
3827                 goto try_again;
3828
3829         //check to make sure name is ok
3830         for (s=filename;*s;s++)
3831                 if (!isalnum(*s) && *s!='_') {
3832                         nm_messagebox1(NULL, NULL,1,TXT_CONTINUE, TXT_DEMO_USE_LETTERS);
3833                         goto try_again;
3834                 }
3835
3836         if (Newdemo_no_space)
3837                 strcat(fullname, m[1].text);
3838         else
3839                 strcat(fullname, m[0].text);
3840         strcat(fullname, ".dem");
3841         cfile_delete(fullname);
3842         cfile_rename(DEMO_FILENAME, fullname);
3843 }
3844
3845
3846 extern char AltHogDir[64];
3847 extern char AltHogdir_initialized;
3848
3849 //returns the number of demo files on the disk
3850 int newdemo_count_demos()
3851 {
3852         FILEFINDSTRUCT find;
3853         int NumFiles=0;
3854
3855         if( !FileFindFirst( DEMO_DIR "*.dem", &find ) ) {
3856                 do {
3857                         NumFiles++;
3858                 } while( !FileFindNext( &find ) );
3859                 FileFindClose();
3860         }
3861
3862         if ( AltHogdir_initialized ) {
3863                 char search_name[PATH_MAX + 5];
3864                 strcpy(search_name, AltHogDir);
3865                 strcat(search_name, "/" DEMO_DIR "*.dem");
3866                 if( !FileFindFirst( search_name, &find ) ) {
3867                         do {
3868                                 NumFiles++;
3869                         } while( !FileFindNext( &find ) );
3870                         FileFindClose();
3871                 }
3872         }
3873
3874         return NumFiles;
3875 }
3876
3877 void newdemo_start_playback(char * filename)
3878 {
3879         FILEFINDSTRUCT find;
3880         int rnd_demo = 0;
3881         char filename2[PATH_MAX+FILENAME_LEN] = DEMO_DIR;
3882
3883 #ifdef NETWORK
3884         change_playernum_to(0);
3885 #endif
3886         First_time_playback=1;
3887         JasonPlaybackTotal=0;
3888
3889         if (filename==NULL) {
3890                 // Randomly pick a filename
3891                 int NumFiles = 0, RandFileNum;
3892                 rnd_demo = 1;
3893
3894                 NumFiles = newdemo_count_demos();
3895
3896                 if ( NumFiles == 0 ) {
3897                         return;     // No files found!
3898                 }
3899                 RandFileNum = d_rand() % NumFiles;
3900                 NumFiles = 0;
3901                 if( !FileFindFirst( DEMO_DIR "*.dem", &find ) ) {
3902                         do {
3903                                 if ( NumFiles==RandFileNum ) {
3904                                         filename = (char *)&find.name;
3905                                         break;
3906                                 }
3907                                 NumFiles++;
3908                         } while( !FileFindNext( &find ) );
3909                         FileFindClose();
3910                 }
3911
3912                 if ( filename == NULL && AltHogdir_initialized ) {
3913                         char search_name[PATH_MAX + 5];
3914                         strcpy(search_name, AltHogDir);
3915                         strcat(search_name, "/" DEMO_DIR "*.dem");
3916                         if( !FileFindFirst( search_name, &find ) ) {
3917                                 do {
3918                                         if ( NumFiles==RandFileNum ) {
3919                                                 filename = (char *)&find.name;
3920                                                 break;
3921                                         }
3922                                         NumFiles++;
3923                                 } while( !FileFindNext( &find ) );
3924                                 FileFindClose();
3925                         }
3926                 }
3927
3928                 if ( filename==NULL) return;
3929         }
3930
3931         if (!filename)
3932                 return;
3933
3934         strcat(filename2,filename);
3935
3936         infile = cfopen(filename2, "rb");
3937
3938         if (infile==NULL) {
3939                 mprintf( (0, "Error reading '%s'\n", filename ));
3940                 return;
3941         }
3942
3943         nd_bad_read = 0;
3944 #ifdef NETWORK
3945         change_playernum_to(0);                 // force playernum to 0
3946 #endif
3947         strncpy(nd_save_callsign, Players[Player_num].callsign, CALLSIGN_LEN);
3948         Viewer = ConsoleObject = &Objects[0];   // play properly as if console player
3949         if (newdemo_read_demo_start(rnd_demo)) {
3950                 cfclose(infile);
3951                 return;
3952         }
3953
3954         Game_mode = GM_NORMAL;
3955         Newdemo_state = ND_STATE_PLAYBACK;
3956         Newdemo_vcr_state = ND_STATE_PLAYBACK;
3957         Newdemo_old_cockpit = Cockpit_mode;
3958         Newdemo_size = cfilelength(infile);
3959         nd_bad_read = 0;
3960         Newdemo_at_eof = 0;
3961         NewdemoFrameCount = 0;
3962         Newdemo_players_cloaked = 0;
3963         playback_style = NORMAL_PLAYBACK;
3964         Function_mode = FMODE_GAME;
3965         Cockpit_3d_view[0] = CV_NONE;       //turn off 3d views on cockpit
3966         Cockpit_3d_view[1] = CV_NONE;       //turn off 3d views on cockpit
3967         newdemo_playback_one_frame();       // this one loads new level
3968         newdemo_playback_one_frame();       // get all of the objects to renderb game
3969 }
3970
3971 void newdemo_stop_playback()
3972 {
3973         cfclose(infile);
3974         Newdemo_state = ND_STATE_NORMAL;
3975 #ifdef NETWORK
3976         change_playernum_to(0);             //this is reality
3977 #endif
3978         strncpy(Players[Player_num].callsign, nd_save_callsign, CALLSIGN_LEN);
3979         Cockpit_mode = Newdemo_old_cockpit;
3980         Game_mode = GM_GAME_OVER;
3981         Function_mode = FMODE_MENU;
3982         longjmp(LeaveGame,0);               // Exit game loop
3983 }
3984
3985
3986 #ifndef NDEBUG
3987
3988 #define BUF_SIZE 16384
3989
3990 void newdemo_strip_frames(char *outname, int bytes_to_strip)
3991 {
3992         CFILE *outfile;
3993         char *buf;
3994         int total_size, bytes_done, read_elems, bytes_back;
3995         int trailer_start, loc1, loc2, stop_loc, bytes_to_read;
3996         short last_frame_length;
3997
3998         bytes_done = 0;
3999         total_size = cfilelength(infile);
4000         outfile = cfopen(outname, "wb");
4001         if (outfile == NULL) {
4002                 newmenu_item m[1];
4003
4004                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't open output file";
4005                 newmenu_do( NULL, NULL, 1, m, NULL );
4006                 newdemo_stop_playback();
4007                 return;
4008         }
4009         buf = d_malloc(BUF_SIZE);
4010         if (buf == NULL) {
4011                 newmenu_item m[1];
4012
4013                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't malloc output buffer";
4014                 newmenu_do( NULL, NULL, 1, m, NULL );
4015                 cfclose(outfile);
4016                 newdemo_stop_playback();
4017                 return;
4018         }
4019         newdemo_goto_end();
4020         trailer_start = cftell(infile);
4021         cfseek(infile, 11, SEEK_CUR);
4022         bytes_back = 0;
4023         while (bytes_back < bytes_to_strip) {
4024                 loc1 = cftell(infile);
4025                 //cfseek(infile, -10, SEEK_CUR);
4026                 //nd_read_short(&last_frame_length);
4027                 //cfseek(infile, 8 - last_frame_length, SEEK_CUR);
4028                 newdemo_back_frames(1);
4029                 loc2 = cftell(infile);
4030                 bytes_back += (loc1 - loc2);
4031         }
4032         cfseek(infile, -10, SEEK_CUR);
4033         nd_read_short(&last_frame_length);
4034         cfseek(infile, -3, SEEK_CUR);
4035         stop_loc = cftell(infile);
4036         cfseek(infile, 0, SEEK_SET);
4037         while (stop_loc > 0) {
4038                 if (stop_loc < BUF_SIZE)
4039                         bytes_to_read = stop_loc;
4040                 else
4041                         bytes_to_read = BUF_SIZE;
4042                 read_elems = cfread(buf, 1, bytes_to_read, infile);
4043                 cfwrite(buf, 1, read_elems, outfile);
4044                 stop_loc -= read_elems;
4045         }
4046         stop_loc = cftell(outfile);
4047         cfseek(infile, trailer_start, SEEK_SET);
4048         while ((read_elems = cfread(buf, 1, BUF_SIZE, infile)) != 0)
4049                 cfwrite(buf, 1, read_elems, outfile);
4050         cfseek(outfile, stop_loc, SEEK_SET);
4051         cfseek(outfile, 1, SEEK_CUR);
4052         cfwrite(&last_frame_length, 2, 1, outfile);
4053         cfclose(outfile);
4054         newdemo_stop_playback();
4055
4056 }
4057
4058 #endif
4059
4060 object DemoRightExtra,DemoLeftExtra;
4061 ubyte DemoDoRight=0,DemoDoLeft=0;
4062
4063 void nd_render_extras (ubyte which,object *obj)
4064 {
4065         ubyte w=which>>4;
4066         ubyte type=which&15;
4067
4068         if (which==255)
4069         {
4070                 Int3(); // how'd we get here?
4071                 do_cockpit_window_view(w,NULL,0,WBU_WEAPON,NULL);
4072                 return;
4073         }
4074
4075         if (w)
4076         {
4077                 memcpy (&DemoRightExtra,obj,sizeof(object));  DemoDoRight=type;
4078         }
4079         else
4080         {
4081                 memcpy (&DemoLeftExtra,obj,sizeof(object)); DemoDoLeft=type;
4082         }
4083
4084 }
4085
4086 void DoJasonInterpolate (fix recorded_time)
4087 {
4088         fix the_delay;
4089
4090         JasonPlaybackTotal+=FrameTime;
4091
4092         if (!First_time_playback)
4093         {
4094                 // get the difference between the recorded time and the playback time
4095                 the_delay=(recorded_time - FrameTime);
4096                 //mprintf ((0,"The delay=%d\n", f2i(the_delay)));
4097                 if (the_delay >= f0_0)
4098                 {
4099                         stop_time();
4100                         timer_delay(the_delay);
4101                         start_time();
4102                 }
4103                 else
4104                 {
4105                         while (JasonPlaybackTotal > nd_recorded_total)
4106                                 if (newdemo_read_frame_information() == -1)
4107                                 {
4108                                         newdemo_stop_playback();
4109                                         return;
4110                                 }
4111
4112                         //the_delay = nd_recorded_total - JasonPlaybackTotal;
4113                         //if (the_delay > f0_0)
4114                         //      timer_delay(the_delay);
4115                 }
4116
4117         }
4118
4119         First_time_playback=0;
4120 }
4121
4122 #ifdef MACINTOSH
4123 #pragma global_optimizer reset
4124 #endif
4125