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