]> icculus.org git repositories - taylor/freespace2.git/blob - src/anim/animplay.cpp
byte-swapping changes for bigendian systems
[taylor/freespace2.git] / src / anim / animplay.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Anim/AnimPlay.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * C module for playing back anim files
16  *
17  * $Log$
18  * Revision 1.4  2004/06/11 00:27:06  tigital
19  * byte-swapping changes for bigendian systems
20  *
21  * Revision 1.3  2002/06/09 04:41:15  relnev
22  * added copyright header
23  *
24  * Revision 1.2  2002/05/07 03:16:43  theoddone33
25  * The Great Newline Fix
26  *
27  * Revision 1.1.1.1  2002/05/03 03:28:08  root
28  * Initial import.
29  *
30  * 
31  * 9     9/13/99 11:26p Andsager
32  * Add debug code to check for poorly sized anis
33  * 
34  * 8     8/26/99 9:45a Dave
35  * First pass at easter eggs and cheats.
36  * 
37  * 7     7/16/99 1:49p Dave
38  * 8 bit aabitmaps. yay.
39  * 
40  * 6     7/13/99 1:15p Dave
41  * 32 bit support. Whee!
42  * 
43  * 5     6/10/99 10:34a Dave
44  * Removed unnecessary assert.
45  * 
46  * 4     11/30/98 1:07p Dave
47  * 16 bit conversion, first run.
48  * 
49  * 3     10/22/98 6:14p Dave
50  * Optimized some #includes in Anim folder. Put in the beginnings of
51  * parse/localization support for externalized strings and tstrings.tbl
52  * 
53  * 2     10/07/98 10:52a Dave
54  * Initial checkin.
55  * 
56  * 1     10/07/98 10:48a Dave
57  * 
58  * 38    6/23/98 4:18p Hoffoss
59  * Fixed some bugs with AC release build.
60  * 
61  * 37    5/18/98 5:59p Hoffoss
62  * Made command briefing advanced now once the speech stops and animation
63  * has fully played once, whichever is longer.
64  * 
65  * 36    5/14/98 6:29p Hoffoss
66  * Fixed some warnings a release rebuild all turned up.
67  * 
68  * 35    5/07/98 3:11a Lawrance
69  * Implement custom streaming code
70  * 
71  * 34    4/27/98 3:36p Dave
72  * 
73  * 33    3/25/98 8:43p Hoffoss
74  * Changed anim_play() to not be so damn complex when you try and call it.
75  * 
76  * 32    2/05/98 9:21p John
77  * Some new Direct3D code.   Added code to monitor a ton of stuff in the
78  * game.
79  * 
80  * 31    1/19/98 11:37p Lawrance
81  * Fixing Optimization build warnings
82  * 
83  * 30    1/19/98 3:09p Hoffoss
84  * Only free an anim instance if it is actually playing.
85  * 
86  * 29    1/14/98 6:43p Lawrance
87  * Add ref_count to anim struct, so we don't free multiple times
88  * 
89  * 28    12/30/97 6:44p John
90  * Made g3_Draw_bitmap functions account for aspect of bitmap.
91  * 
92  * 27    12/27/97 2:35p John
93  * Restructed some code so that if the memory-mapped file open fails, it
94  * will still read it the old-fashioned, non-memory mapped way.
95  * 
96  * 26    12/24/97 9:10p Lawrance
97  * take out some debugging statements
98  * 
99  * 25    12/24/97 8:57p Lawrance
100  * Added anim_ignore_next_frametime()
101  * 
102  * 24    12/07/97 2:06p Dave
103  * Removed some debug frame-checking code.
104  * 
105  * 23    12/06/97 2:55p Dave
106  * 
107  * 22    11/29/97 2:05p John
108  * made g3_draw_bitmap and g3_draw_rotated bitmap take w&h, not w/2 & h/2,
109  * like they used to incorrectly assume.   Added code to model to read in
110  * thruster radius's.
111  * 
112  * 21    11/20/97 5:36p Dave
113  * Hooked in a bunch of main hall changes (including sound). Made it
114  * possible to reposition (rewind/ffwd) 
115  * sound buffer pointers. Fixed animation direction change framerate
116  * problem.
117  * 
118  * 20    11/20/97 4:33p Sandeep
119  * ALAN: ensure instance->paused gets initialized
120  * 
121  * 19    11/20/97 4:12p Lawrance
122  * when paused, don't increment time_elapsed
123  * 
124  * 18    11/19/97 8:28p Dave
125  * Hooked in Main Hall screen. Put in Anim support for ping ponging
126  * animations as well as general reversal of anim direction.
127  * 
128  * 17    9/11/97 4:17p Allender
129  * use _MAX_PATH instead of MAX_FILENAME_LENGTH for anim_play since it is
130  * called from MoviePlayer with full path
131  * 
132  * 16    9/11/97 10:49a Lawrance
133  * improve anim_show_next_frame()
134  * 
135  * 15    9/10/97 4:59p Lawrance
136  * improve comments to anim_play() function
137  * 
138  * 14    9/03/97 4:19p John
139  * changed bmpman to only accept ani and pcx's.  made passing .pcx or .ani
140  * to bm_load functions not needed.   Made bmpman keep track of palettes
141  * for bitmaps not mapped into game palettes.
142  * 
143  * 13    8/30/97 2:11p Lawrance
144  * allow animations to loop
145  * 
146  * 12    8/25/97 11:13p Lawrance
147  * support framerate independent playback with the option of now advancing
148  * more than one frame at a time
149  * 
150  * 11    8/22/97 8:20a Lawrance
151  * display scrolling text properly, with indicator that text exitsts to be
152  * scrolled
153  * 
154  * 10    8/21/97 5:11p Lawrance
155  * frame numbering for ANI's now is from 0 -> total_frames-1.
156  * 
157  * 9     8/19/97 9:30a Lawrance
158  * print out reason if file doesn't open right
159  * 
160  * 8     7/28/97 10:42p Lawrance
161  * re-did interface to unpack_frame() to make more general
162  * 
163  * 7     7/21/97 11:41a Lawrance
164  * make playback time of .ani files keyed of frametime
165  * 
166  * 6     7/20/97 6:57p Lawrance
167  * supporting new RLE format
168  * 
169  * 5     7/11/97 11:54a John
170  * added rotated 3d bitmaps.
171  * 
172  * 4     6/27/97 4:36p Lawrance
173  * update pal translation table when gr_screen.signature changes
174  * 
175  * 3     6/26/97 3:00p Lawrance
176  * fix bug with playing 3d anims
177  * 
178  * 2     6/26/97 12:12a Lawrance
179  * supporting anti-aliased bitmap animations
180  * 
181  * 1     6/23/97 5:09p Lawrance
182  * 
183  * 24    6/03/97 5:53p Lawrance
184  * don't unload bitmap after bm_create
185  * 
186  * $NoKeywords: $
187  */
188
189 #include "animplay.h"
190 #include "linklist.h"
191 #include "timer.h"
192 #include "bmpman.h"
193 #include "2d.h"
194 #include "3d.h"
195 #include "grinternal.h"
196 #include "pcxutils.h"
197 #include "packunpack.h"
198 #include "cfile.h"
199
200 static color Color_xparent;
201
202 anim *first_anim = NULL;
203 anim_instance anim_free_list;
204 anim_instance anim_render_list;
205
206 #define MAX_ANIM_INSTANCES 25
207 anim_instance anim_render_instance[MAX_ANIM_INSTANCES];
208
209 int Anim_paused;        // global variable to pause the playing back of anims
210 int Anim_inited = FALSE;
211
212 fix t1,t2;
213
214 int Anim_ignore_frametime=0;    // flag used to ignore frametime... useful when need to avoid saturated frametimes
215
216 // -------------------------------------------------------------------------------------------------
217 // anim_init() will queue all the anim_render_instance[] elements onto the anim_free_list
218 //
219 void anim_init()
220 {
221         int i;
222
223         if ( Anim_inited == TRUE )
224                 return;
225
226         list_init( &anim_free_list );
227         list_init( &anim_render_list );
228
229         // Link all anim render slots into the free list
230         for (i=1; i < MAX_ANIM_INSTANCES; i++)  {
231                 list_append(&anim_free_list, &anim_render_instance[i]);
232         }
233         
234         Anim_paused = 0;
235         Anim_inited = TRUE;
236 }
237
238 // -------------------------------------------------------------------------------------------------
239 // anim_render_all() will display the frames for the currently playing anims
240 //
241 void anim_render_all(int screen_id, float frametime)
242 {
243         anim_instance* A;
244         anim_instance* temp;
245
246         A = GET_FIRST(&anim_render_list);
247         while( A !=END_OF_LIST(&anim_render_list) )     {
248                 temp = GET_NEXT(A);
249                 if ( A->screen_id == screen_id ) {
250                         if ( Anim_ignore_frametime ) {
251                                 frametime = 0.0f;
252                                 Anim_ignore_frametime=0;
253                         }
254                         if ( anim_show_next_frame(A, frametime) == -1 ) {
255                                 A->data = NULL;
256                                 anim_release_render_instance(A);        
257                         }
258                 }
259                 A = temp;
260         }
261 }
262
263 // -------------------------------------------------------------------------------------------------
264 // anim_render_one() will display the frames for the passed animation, it will ignore animations which
265 // do not have the same id as the passed screen_id
266 //
267 void anim_render_one(int screen_id, anim_instance *ani, float frametime)
268 {
269         // make sure this guy's screen id matches the passed one
270         if(screen_id != ani->screen_id){
271                 return;
272         }
273
274         // otherwise render it  
275         if ( Anim_ignore_frametime ) {
276                 frametime = 0.0f;
277                 Anim_ignore_frametime=0;
278         }
279         if ( anim_show_next_frame(ani, frametime) == -1 ) {
280                 ani->data = NULL;
281                 anim_release_render_instance(ani);      
282         }       
283 }
284
285 MONITOR(NumANIPlayed);
286
287 // Setup an anim_play_struct for passing into anim_play().  Will fill in default values, which you
288 // can then change before calling anim_play().
289 //
290 void anim_play_init(anim_play_struct *aps, anim *a_info, int x, int y)
291 {
292         aps->anim_info = a_info;
293         aps->x = x;
294         aps->y = y;
295         aps->start_at = 0;
296         aps->stop_at = a_info->total_frames - 1;
297         aps->screen_id = 0;
298         aps->world_pos = NULL;
299         aps->radius = 0.0f;
300         aps->framerate_independent = 0;
301         aps->color = NULL;
302         aps->skip_frames = 1;
303         aps->looped = 0;
304         aps->ping_pong = 0;
305 }
306
307 // -------------------------------------------------------------------------------------------------
308 // anim_play() will add an anim instance to the anim_render_list.  This will cause the
309 // anim to be played at the x,y position specified in the parameter list.
310 //
311 // input:
312 //
313 //              anim_info       =>      the compressed animation that we should make an instance from
314 //              x                               =>      x position of animation to play at (top left corner)
315 //              y                               =>      y position of animation to play at ( top left corner)
316 //              start_at                =>      frame number to start at (note: numbering is from 0->num_frames-1)
317 //              stop_at         =>      frame number to stop at (note: numbering is from 0->num_frames-1)
318 //              screen_id       =>      OPTIONAL (default value 0): screen signature so animation only plays when
319 //                                                      anim_render_all() called with that same signature
320 //              world_pos       =>      OPTIONAL (default value NULL): only give a world pos when you want to
321 //                                                      play the animation at a 3D location.  You must specify radius when
322 //                                                      this is non-null.
323 //              radius          =>      OPTIONAL (default value 0): only needed when the animation is playing
324 //                                                      as a 3D animation (this is only when world_pos in not NULL).
325 //              fi                              =>      OPTIONAL (default value 0): framerate indepentdent flag, when set TRUE
326 //                                                      the animation will skip frames if necessary to maintain the fps value
327 //                                                      associated with the animation
328 //              color                   =>      OPTIONAL (default value NULL) address of an alpha color struct.  Only
329 //                                                      required when the animation should be drawn with an alpha color.
330 //              skip_frames     => OPTIONAL (default value 1) should anim skip frames when doing framerate
331 //                   independent playback
332 //              looped          =>      OPTIONAL (default value 0) should anim play looped (ie forever)
333 //
334 // returns:
335 //
336 //              pointer to instance     => success
337 //              NULL                                            => if anim anim could not be played
338 //
339 anim_instance *anim_play(anim_play_struct *aps)
340 {
341         Assert( aps->anim_info != NULL );
342         Assert( aps->start_at >= 0 );
343         Assert( aps->stop_at < aps->anim_info->total_frames );
344         // Assert( aps->stop_at >= aps->start_at );
345         Assert( !(aps->looped && aps->ping_pong) );  // shouldn't have these both set at once
346
347         MONITOR_INC(NumANIPlayed, 1);
348         
349         // if (aps->ping_pong && !(aps->anim_info->flags & ANF_ALL_KEYFRAMES));
350
351         anim_instance *instance;
352
353         // Find next free anim instance slot on queue
354         instance = GET_FIRST(&anim_free_list);
355         Assert( instance != &anim_free_list );  // shouldn't have the dummy element
356
357         // remove instance from the free list
358         list_remove( &anim_free_list, instance );
359
360         // insert instance onto the end of anim_render_list
361         list_append( &anim_render_list, instance );
362
363         aps->anim_info->instance_count++;
364         instance->frame_num = -1;
365         instance->last_frame_num = -99;
366         instance->parent = aps->anim_info;
367         instance->data = aps->anim_info->data;
368         if ( anim_instance_is_streamed(instance) ) {
369                 instance->file_offset = instance->parent->file_offset;
370         }
371         instance->frame = (ubyte *) malloc(instance->parent->width * instance->parent->height * 2);
372         Assert( instance->frame != NULL );
373         instance->time_elapsed = 0.0f;
374         instance->stop_at = aps->stop_at;
375         instance->x = aps->x;
376         instance->y = aps->y;
377         instance->world_pos = aps->world_pos;
378         instance->radius = aps->radius;
379         instance->framerate_independent = aps->framerate_independent;
380         instance->last_bitmap = -1;
381         instance->stop_now = FALSE;
382         instance->screen_id = aps->screen_id;
383         instance->aa_color = aps->color;
384         instance->skip_frames = aps->skip_frames;
385         instance->looped = aps->looped;
386         instance->ping_pong = aps->ping_pong;
387         instance->direction = ANIM_DIRECT_FORWARD;
388         instance->paused = 0;
389         instance->loop_count = 0;
390         if ( aps->color == NULL ){
391                 instance->xlate_pal = 1;
392         } else {
393                 instance->xlate_pal = 0;
394         }
395
396         // determining the start_at frame is more complicated, since it must be a key-frame.
397         // Futhermore, need to subtract 1 from key-frame number, since frame number is always
398         // incremented the first time anim_show_next_frame() is called
399
400         instance->start_at = aps->start_at;
401
402         if ( aps->start_at > 0 ) {
403                 key_frame *keyp;
404                 int idx;
405                 int key = 0;
406                 int offset = 0;
407                 int frame_num = aps->start_at;
408
409                 keyp = instance->parent->keys;
410                 idx = 0;
411                 while (idx < instance->parent->num_keys) {
412                         if (key == frame_num)
413                                 break;
414
415                         key = keyp[idx].frame_num - 1;
416                         offset = keyp[idx].offset;
417
418                         idx++;
419                 }
420                 /*while (keyp) {
421                         if (( (keyp->frame_num-1) <= frame_num) && ( (keyp->frame_num-1) > key)) {  // find closest key
422                                 key = keyp->frame_num-1;
423                                 offset = keyp->offset;
424                                 if ( key == frame_num )
425                                         break;
426                         }
427
428                         keyp = keyp->next;
429                 }*/
430
431                 if (key > instance->frame_num) {  // best key is closer than current position
432                         instance->frame_num = key;
433                         if ( anim_instance_is_streamed(instance) ) {
434                                 instance->file_offset = instance->parent->file_offset + offset;
435                         } else {
436                                 instance->data = instance->parent->data + offset;
437                         }
438
439                 }
440
441                 instance->frame_num--;  // required
442         }
443
444         return instance;
445 }
446
447 // -----------------------------------------------------------------------------
448 //      anim_show_next_frame()
449 //
450 //      This function is called to blit the next frame of an anim instance to the 
451 // screen.  This is normally called by the anim_render_all() function.
452 //
453 //      input:  instance                =>              pointer to animation instance
454 //                              frametime       =>              time elapsed since last call, in seconds
455 //
456 int anim_show_next_frame(anim_instance *instance, float frametime)
457 {
458         int             bitmap_id, bitmap_flags=0, new_frame_num, frame_diff=0, i, n_frames=0,frame_save;
459         float           percent_through, decompress_time, render_time, time;
460         vertex  image_vertex;
461         int aabitmap = 0;
462         int bpp = 16;
463
464         Assert( instance != NULL );
465
466         instance->time_elapsed += frametime;
467
468         // Advance to the next frame, if we determine enough time has elapsed.
469         if(instance->direction == ANIM_DIRECT_FORWARD)
470                 n_frames = instance->stop_at - instance->start_at + 1;
471         else if(instance->direction == ANIM_DIRECT_REVERSE)
472                 n_frames = instance->start_at - instance->stop_at + 1;
473         time = n_frames / i2fl(instance->parent->fps);
474
475         percent_through = instance->time_elapsed / time;
476
477         if(instance->direction == ANIM_DIRECT_FORWARD)
478                 new_frame_num = instance->start_at - 1 + fl2i(percent_through * n_frames + 0.5f);
479         else
480                 new_frame_num = instance->start_at - 1 - fl2i(percent_through * n_frames + 0.5f);
481
482         frame_save = instance->frame_num;
483
484         // If framerate independent, use the new_frame_num... unless instance->skip_frames is
485         // FALSE, then only advance a maximum of one frame (this is needed since some big animations
486         // should just play slower rather than taking the hit of decompressing multiple frames and
487         // creating an even greater slowdown    
488         if (instance->framerate_independent) {
489                 if(instance->direction == ANIM_DIRECT_FORWARD){
490                         if ( new_frame_num > instance->last_frame_num) {
491                                 if ( instance->skip_frames )
492                                         instance->frame_num = new_frame_num;
493                                 else
494                                         instance->frame_num++;
495                         }
496                 } else if(instance->direction == ANIM_DIRECT_REVERSE){
497                         if( new_frame_num < instance->last_frame_num) {
498                                 if ( instance->skip_frames )
499                                         instance->frame_num = new_frame_num;
500                                 else
501                                         instance->frame_num--;
502                         }
503                 }
504         }
505         else {                  
506                 if(instance->direction == ANIM_DIRECT_FORWARD){
507                         if ( new_frame_num > instance->last_frame_num) {
508                                 instance->frame_num++;
509                         }
510                 } else if(instance->direction == ANIM_DIRECT_REVERSE){
511                         if ( new_frame_num < instance->last_frame_num) {
512                                 instance->frame_num--;
513                         }
514                 }                       
515         }
516
517         if(instance->direction == ANIM_DIRECT_FORWARD){
518                 if ( instance->frame_num < instance->start_at ) {
519                         instance->frame_num = instance->start_at;
520                 }       
521         } else if(instance->direction == ANIM_DIRECT_REVERSE){
522                 if ( instance->frame_num > instance->start_at ) {
523                         instance->frame_num = instance->start_at;
524                 }       
525         }
526
527         if ( instance->stop_now == TRUE ) {
528                 return -1;
529         }
530
531         // If past the last frame, clamp to the last frame and then set the stop_now flag in the
532         // anim instance.  The next iteration, the animation will stop.
533         if(instance->direction == ANIM_DIRECT_FORWARD){
534                 if (instance->frame_num >= instance->stop_at ) {
535                         if (instance->looped) {                                                                         // looped animations
536                                 instance->frame_num = instance->stop_at;
537                                 instance->time_elapsed = 0.0f;
538                         } else if(instance->ping_pong) {                                                        // pingponged animations
539                                 instance->frame_num = instance->stop_at;
540                                 // instance->time_elapsed = 0.0f;
541                                 anim_reverse_direction(instance);
542                         } else {                                                                                                                        // one-shot animations
543                                 instance->frame_num = instance->stop_at;
544                                 instance->last_frame_num = instance->frame_num;
545                                 instance->stop_now = TRUE;
546                         }
547                 }
548         } else if(instance->direction == ANIM_DIRECT_REVERSE){
549                 if (instance->frame_num <= instance->stop_at ) {
550                         if (instance->looped) {                                                                         // looped animations
551                                 instance->frame_num = instance->stop_at;
552                                 instance->time_elapsed = 0.0f;
553                         } else if(instance->ping_pong) {                                                        // pingponged animations
554                                 instance->frame_num = instance->stop_at;
555                                 // instance->time_elapsed = 0.0f;
556                                 anim_reverse_direction(instance);
557                         } else {                                                                                                                        // one-shot animations
558                                 instance->frame_num = instance->stop_at+1;
559                                 instance->last_frame_num = instance->frame_num;
560                                 instance->stop_now = TRUE;
561                         }
562                 }
563         }
564
565         if(instance->direction == ANIM_DIRECT_FORWARD){
566                 if( instance->last_frame_num >= instance->start_at ) {
567                         frame_diff = instance->frame_num - instance->last_frame_num;            
568                 } else {
569                         frame_diff = 1;
570                 }
571         } else if(instance->direction == ANIM_DIRECT_REVERSE){
572                 if( instance->last_frame_num <= instance->start_at ) {
573                         frame_diff = instance->last_frame_num - instance->frame_num;
574                 } else {
575                         frame_diff = 1;
576                 }
577         }               
578         Assert(frame_diff >= 0);
579         //      nprintf(("Alan","FRAME DIFF: %d\n",frame_diff));
580         Assert( instance->frame_num >= 0 && instance->frame_num < instance->parent->total_frames );
581
582         // if the anim is paused, ignore all the above changes and still display this frame
583         if(instance->paused || Anim_paused){
584                 instance->frame_num = frame_save;
585                 instance->time_elapsed -= frametime;
586                 frame_diff = 0;
587         }
588
589         if (instance->parent->flags & ANF_XPARENT){
590                 // bitmap_flags = BMP_XPARENT;
591                 bitmap_flags = 0;
592         } 
593         bpp = 16;
594         if(instance->aa_color != NULL){
595                 bitmap_flags |= BMP_AABITMAP;
596                 aabitmap = 1;
597                 bpp = 8;
598         }       
599
600         if ( frame_diff > 0 ) {
601                 instance->last_frame_num = instance->frame_num;         
602
603                 t1 = timer_get_fixed_seconds();
604                 for ( i = 0; i < frame_diff; i++ ) {
605                         anim_check_for_palette_change(instance);                        
606
607                         // if we're playing backwards, every frame must be a keyframe and we set the data ptr here
608                         if(instance->direction == ANIM_DIRECT_REVERSE){
609                                 if ( anim_instance_is_streamed(instance) ) {
610                                         instance->file_offset = instance->parent->file_offset + instance->parent->keys[instance->frame_num-1].offset;
611                                 } else {
612                                         instance->data = instance->parent->data + instance->parent->keys[instance->frame_num-1].offset;
613                                 }
614                         }
615
616                         ubyte *temp = NULL;
617                         int temp_file_offset = 0;                       
618
619                         // if we're using bitmap polys
620                         if(Gr_bitmap_poly){
621                                 BM_SELECT_TEX_FORMAT();
622                         }
623
624                         if ( anim_instance_is_streamed(instance) ) {
625                                 if ( instance->xlate_pal ){
626                                         temp_file_offset = unpack_frame_from_file(instance, instance->frame, instance->parent->width*instance->parent->height, instance->parent->palette_translation, aabitmap, bpp);
627                                 } else {
628                                         temp_file_offset = unpack_frame_from_file(instance, instance->frame, instance->parent->width*instance->parent->height, NULL, aabitmap, bpp);
629                                 }
630                         } else {
631                                 if ( instance->xlate_pal ){
632                                         temp = unpack_frame(instance, instance->data, instance->frame, instance->parent->width*instance->parent->height, instance->parent->palette_translation, aabitmap, bpp);
633                                 } else {
634                                         temp = unpack_frame(instance, instance->data, instance->frame, instance->parent->width*instance->parent->height, NULL, aabitmap, bpp);
635                                 }
636                         }
637
638                         // always go back to screen format
639                         BM_SELECT_SCREEN_FORMAT();
640
641                         if(instance->direction == ANIM_DIRECT_FORWARD){
642                                 if ( anim_instance_is_streamed(instance) ) {
643                                         instance->file_offset = temp_file_offset;
644                                 } else {
645                                         instance->data = temp;
646                                 }
647                         }                       
648                 }
649                 t2 = timer_get_fixed_seconds();
650         }
651         else {
652                 t2=t1=0;
653         }
654
655         // this only happens when the anim is being looped, we need to reset the last_frame_num
656         if ( (instance->time_elapsed == 0) && (instance->looped) ) {
657                 instance->last_frame_num = -1;
658                 instance->frame_num = -1;
659                 instance->data = instance->parent->data;
660                 instance->file_offset = instance->parent->file_offset;
661                 instance->loop_count++;
662         }
663                 
664         decompress_time = f2fl(t2-t1);
665
666         t1 = timer_get_fixed_seconds();
667         if ( frame_diff == 0 && instance->last_bitmap != -1 ) {
668                 bitmap_id = instance->last_bitmap;
669         }
670         else {
671                 if ( instance->last_bitmap != -1 ){
672                         bm_release(instance->last_bitmap);
673                 }
674                 bitmap_id = bm_create(16, instance->parent->width, instance->parent->height, instance->frame, bitmap_flags);
675         }
676         
677         if ( bitmap_id == -1 ) {
678                 // anim has finsished playing, free the instance frame data
679                 anim_release_render_instance(instance); 
680                 return -1;
681
682                 // NOTE: there is no need to free the instance, since it was pre-allocated as 
683                 //       part of the anim_free_list
684         }
685         else {
686                 gr_set_bitmap(bitmap_id);
687                 
688                 // determine x,y to display the bitmap at
689                 if ( instance->world_pos == NULL ) {
690                         if ( instance->aa_color == NULL ) {
691                                 gr_bitmap(instance->x, instance->y);
692                         }
693                         else {
694                                 gr_set_color_fast( (color*)instance->aa_color );
695                                 gr_aabitmap(instance->x, instance->y);
696                         }
697                 }
698                 else {
699                         g3_rotate_vertex(&image_vertex,instance->world_pos);
700                         Assert(instance->radius != 0.0f);
701                         g3_draw_bitmap(&image_vertex, 0, instance->radius*1.5f, TMAP_FLAG_TEXTURED );
702                 }
703
704                 //bm_release(bitmap_id);
705                 instance->last_bitmap = bitmap_id;
706         }
707
708         t2 = timer_get_fixed_seconds();
709         render_time = f2fl(t2-t1);
710
711 //      nprintf(("Alan","DECOMPRESS: %.3fms  RENDER: %.3fms\n", decompress_time*1000, render_time*1000));
712
713         return 0;
714 }
715
716 // -----------------------------------------------------------------------------
717 //      anim_stop_playing()
718 //
719 //      Stop an anim instance that is on the anim_render_list from playing
720 //
721 int anim_stop_playing(anim_instance* instance)
722 {
723         Assert(instance != NULL);
724
725         if ( anim_playing(instance) ) {
726                 anim_release_render_instance(instance);
727         }
728         return 0;
729 }
730
731 // -----------------------------------------------------------------------------
732 //      anim_release_render_instance()
733 //
734 //      Free a particular animation instance that is on the anim_render_list.  Do
735 // not call this function to free an animation instance in general (use 
736 // free_anim_instance() for that), only when you want to free an instance
737 // that is on the anim_render_list
738 //
739 void anim_release_render_instance(anim_instance* instance)
740 {
741         Assert( instance != NULL );
742         Assert(instance->frame);
743         free(instance->frame);
744         instance->frame = NULL;
745         instance->parent->instance_count--;
746
747         if ( instance->last_bitmap != -1 ) {
748                 bm_release(instance->last_bitmap); 
749                 instance->last_bitmap = -1;
750         }
751
752         // remove instance from anim_render_list
753         list_remove( &anim_render_list, instance );
754
755         // insert instance into the anim_free_list
756         list_append( &anim_free_list, instance );
757 }
758
759 // -----------------------------------------------------------------------------
760 //      anim_release_all_instances()
761 //
762 //      Free all anim instances that are on the anim_render_list.
763 //
764 //      input:  screen_id       =>              optional parameter that lets you only free a subset
765 //                                                                              of the anim instances.  A screen_id of 0 is the default
766 //                                                                              value, and this is used for animations that always play
767 //                                                                              when they are placed on the aim_render_list.
768 //
769 void anim_release_all_instances(int screen_id)
770 {
771         anim_instance* A;
772         anim_instance* temp;
773
774         if ( Anim_inited == FALSE )
775                 return;
776
777         A = GET_FIRST(&anim_render_list);
778         while( A !=END_OF_LIST(&anim_render_list) )     {
779                 temp = GET_NEXT(A);
780                 if ( A->screen_id == screen_id || screen_id == 0 ) {
781                         anim_release_render_instance(A);
782                 }
783                 A = temp;
784         }
785 }
786
787 // -----------------------------------------------------------------------------
788 //      anim_read_header()
789 //
790 // Read the header of a .ani file.  Below is the format of a .ani header
791 //
792 //      #bytes  |       description
793 //      2                       |       obsolete, kept for compatibility with old versions
794 //      2                       |       version number
795 //      2                       |       fps
796 //      1                       |       transparent red value
797 //      1                       |       transparent green value
798 //      1                       |       transparent blue value
799 //      2                       |       width
800 //      2                       |       height
801 //      2                       |       number of frames
802 //      2                       |       packer code
803 //      763                     |       palette
804 //      2                       |       number of key frames
805 //      2                       |       key frame number        }               repeats
806 //      4                       |       key frame offset        }               repeats
807 //      4                       |       compressed data length
808 //
809 void anim_read_header(anim *ptr, CFILE *fp)
810 {
811         ptr->width = cfread_short(fp);
812         // If first 2 bytes are zero, this means we are using a new format, which includes
813         // a version, and fps values.  This is only done since a version number was not included
814         // in the original header.
815
816         // default
817         Color_xparent.red = 0;
818         Color_xparent.green = 255;
819         Color_xparent.blue = 0;
820
821         if ( ptr->width == 0 ) {
822                 ptr->version = cfread_short(fp);
823                 ptr->fps = cfread_short(fp);
824
825                 // version 2 added a custom transparency color
826                 if ( ptr->version >= 2 ) {
827                         cfread(&Color_xparent.red, 1, 1, fp);
828                         cfread(&Color_xparent.green, 1, 1, fp);
829                         cfread(&Color_xparent.blue, 1, 1, fp);
830                 }
831
832                 ptr->width = cfread_short(fp);
833         }
834         else {
835                 ptr->version = 0;
836                 ptr->fps = 30;
837         }
838         
839         ptr->height = cfread_short(fp);
840
841 #ifndef NDEBUG
842         // get size of ani compared to power of 2
843         int r, floor_pow;
844         r = ptr->height;
845         floor_pow = 0;
846         
847         while(r >= 2) {
848                 r /= 2;
849                 floor_pow++;
850         }
851
852         int floor_size = (int) pow(2, floor_pow);
853         int diff = ptr->height - floor_size;
854         float waste = 100.0f * float((floor_size - diff))/(2.0f *(float)floor_size);
855
856         if (diff != 0) {
857                 if (ptr->height > 16) {
858                         mprintf(("ANI %s with size %dx%d (%.1f%% wasted)\n", ptr->name, ptr->height, ptr->height, waste));
859                 }
860         }
861 #endif
862
863         ptr->total_frames = cfread_short(fp);
864         cfread(&ptr->packer_code, 1, 1, fp);
865         ptr->packer_code = INTEL_SHORT(ptr->packer_code);
866         cfread(&ptr->palette, 256, 3, fp);
867         ptr->num_keys = cfread_short(fp);
868
869         // store xparent colors
870         ptr->xparent_r = Color_xparent.red;
871         ptr->xparent_g = Color_xparent.green;
872         ptr->xparent_b = Color_xparent.blue;
873
874         if(ptr->total_frames == ptr->num_keys){
875                 ptr->flags |= ANF_ALL_KEYFRAMES;
876         }
877 }
878
879 // -----------------------------------------------------------------------------
880 //      anim_load()
881 //
882 // Load an animation.  This stores the compressed data, which instances
883 // of the animation can reference.  Must be free'ed later with anim_free()
884 //
885 // input:       name                            =>              filename of animation
886 //                              file_mapped             =>              boolean, whether to use memory-mapped file or not.
887 //                                                                                      Memory-mapped files will page in the animation from disk
888 //                                                                                      as it is needed, but performance is not as good
889 //
890 //      returns:        pointer to anim that is loaded  => sucess
891 //                              NULL                                                                            =>      failure
892 //
893 anim *anim_load(char *real_filename, int file_mapped)
894 {
895         anim                    *ptr;
896         CFILE                   *fp;
897         int                     count,idx;
898         char name[_MAX_PATH];
899
900 //      file_mapped = 0;
901
902         Assert ( real_filename != NULL );
903
904         strcpy( name, real_filename );
905         char *p = strchr( name, '.' );
906         if ( p ) {
907                 *p = 0;
908         }
909         strcat( name, ".ani" );
910
911         ptr = first_anim;
912         while (ptr) {
913                 if (!stricmp(name, ptr->name))
914                         break;
915
916                 ptr = ptr->next;
917         }
918
919         if (!ptr) {
920                 fp = cfopen(name, "rb");
921                 if ( !fp )
922                         return NULL;
923
924                 ptr = (anim *) malloc(sizeof(anim));
925                 Assert(ptr);
926
927                 ptr->flags = 0;
928                 ptr->next = first_anim;
929                 first_anim = ptr;
930                 Assert(strlen(name) < _MAX_PATH - 1);
931                 strcpy(ptr->name, name);
932                 ptr->instance_count = 0;
933                 ptr->width = 0;
934                 ptr->height = 0;
935                 ptr->total_frames = 0;
936                 ptr->keys = NULL;
937                 ptr->ref_count=0;
938
939                 anim_read_header(ptr, fp);
940
941                 if(ptr->num_keys > 0){
942                         ptr->keys = (key_frame*)malloc(sizeof(key_frame) * ptr->num_keys);
943                         Assert(ptr->keys != NULL);
944                 }                       
945
946                 // store how long the anim should take on playback (in seconds)
947                 ptr->time = i2fl(ptr->total_frames)/ptr->fps;
948
949                 for(idx=0;idx<ptr->num_keys;idx++){
950                         ptr->keys[idx].frame_num = 0;
951                         cfread(&ptr->keys[idx].frame_num, 2, 1, fp);
952                         cfread(&ptr->keys[idx].offset, 4, 1, fp);
953             ptr->keys[idx].frame_num = INTEL_INT( ptr->keys[idx].frame_num );
954             ptr->keys[idx].offset = INTEL_INT( ptr->keys[idx].offset );
955                 }
956
957                 /*prev_keyp = &ptr->keys;
958                 count = ptr->num_keys;
959                 while (count--) {
960                         keyp = (key_frame *) malloc(sizeof(key_frame));
961                         keyp->next = *prev_keyp;
962                         *prev_keyp = keyp;
963                         prev_keyp = &keyp->next;
964
965                         keyp->frame_num = 0;
966                         cfread(&keyp->frame_num, 2, 1, fp);
967                         cfread(&keyp->offset, 4, 1, fp);
968                 }*/
969                 cfread(&count, 4, 1, fp);       // size of compressed data
970         count = INTEL_INT( count );
971
972                 ptr->cfile_ptr = NULL;
973
974                 if ( file_mapped ) {
975                         // Try mapping the file to memory 
976                         ptr->flags |= ANF_MEM_MAPPED;
977                         ptr->cfile_ptr = cfopen(name, "rb", CFILE_MEMORY_MAPPED);
978                 }
979
980                 // couldn't memory-map file... must be in a packfile, so stream manually
981                 if ( file_mapped && !ptr->cfile_ptr ) {
982                         ptr->flags &= ~ANF_MEM_MAPPED;
983                         ptr->flags |= ANF_STREAMED;
984                         ptr->cfile_ptr = cfopen(name, "rb");
985                 }
986
987                 ptr->cache = NULL;
988
989                 // If it opened properly as mem-mapped (or streamed)
990                 if (ptr->cfile_ptr != NULL)     {
991                         // VERY IMPORTANT STEP
992                         // Set the data pointer to the compressed data (which is not at the start of the
993                         // file).  Use ftell() to find out how far we've already parsed into the file
994                         //
995                         int offset;
996                         offset = cftell(fp);
997                         ptr->file_offset = offset;
998                         if ( ptr->flags & ANF_STREAMED ) {
999                                 ptr->data = NULL;
1000                                 ptr->cache_file_offset = ptr->file_offset;
1001                                 ptr->cache = (ubyte*)malloc(ANI_STREAM_CACHE_SIZE+2);
1002                                 Assert(ptr->cache);
1003                                 cfseek(ptr->cfile_ptr, offset, CF_SEEK_SET);
1004                                 cfread(ptr->cache, ANI_STREAM_CACHE_SIZE, 1, ptr->cfile_ptr);
1005                         } else {
1006                                 ptr->data = (ubyte*)cf_returndata(ptr->cfile_ptr) + offset;
1007                         }
1008                 } else {
1009                         // Not a memory mapped file (or streamed)
1010                         ptr->flags &= ~ANF_MEM_MAPPED;
1011                         ptr->flags &= ~ANF_STREAMED;
1012                         ptr->data = (ubyte *) malloc(count);
1013                         ptr->file_offset = -1;
1014                         cfread(ptr->data, count, 1, fp);
1015                 }
1016
1017                 cfclose(fp);
1018
1019                 // store screen signature, so we can tell if palette changes
1020                 ptr->screen_sig = gr_screen.signature;
1021
1022                 anim_set_palette(ptr);
1023         }
1024
1025         ptr->ref_count++;
1026         return ptr;
1027 }
1028
1029 // ---------------------------------------------------
1030 //      anim_free()
1031 //
1032 // Free an animation that was loaded with anim_load().  All instances
1033 // referencing this animation must be free'ed or get an assert.
1034 //
1035 int anim_free(anim *ptr)
1036 {
1037         Assert ( ptr != NULL );
1038         anim *list, **prev_anim;
1039
1040         list = first_anim;
1041         prev_anim = &first_anim;
1042         while (list && (list != ptr)) {
1043                 prev_anim = &list->next;
1044                 list = list->next;
1045         }
1046
1047         if ( !list )
1048                 return -1;
1049
1050         // only free when ref_count is 0
1051         ptr->ref_count--;
1052         if ( ptr->ref_count > 0 ) 
1053                 return -1;
1054
1055         // only free if there are no playing instances
1056         if ( ptr->instance_count > 0 )
1057                 return -1;
1058
1059         if(ptr->keys != NULL){
1060                 free(ptr->keys);
1061                 ptr->keys = NULL;
1062         }
1063
1064         if ( ptr->flags & (ANF_MEM_MAPPED|ANF_STREAMED) ) {
1065                 cfclose(ptr->cfile_ptr);
1066                 if ( ptr->cache ) {
1067                         free(ptr->cache);
1068                 }
1069         }
1070         else {
1071                 Assert(ptr->data);
1072                 free(ptr->data);
1073         }
1074
1075         *prev_anim = ptr->next;
1076         free(ptr);
1077         return 0;
1078 }
1079
1080
1081 // ---------------------------------------------------------------------
1082 // anim_playing()
1083 //
1084 // Return if an anim is playing or not.  
1085 //
1086 int anim_playing(anim_instance *ai)
1087 {
1088         Assert(ai != NULL);
1089         if ( ai->frame == NULL )
1090                 return 0;
1091         else 
1092                 return 1;
1093 }
1094
1095
1096 // ---------------------------------------------------------------------
1097 // anim_level_init()
1098 //
1099 // Called at the beginning of a mission to initialize any mission dependent
1100 // anim data.
1101 //
1102 void anim_level_init()
1103 {
1104 }
1105
1106 // ---------------------------------------------------------------------
1107 // anim_level_close()
1108 //
1109 // Called after the end of a mission to clean up any mission dependent 
1110 // anim data. 
1111 //
1112 void anim_level_close()
1113 {
1114         anim_release_all_instances();
1115 }
1116
1117 // ---------------------------------------------------
1118 //      anim_write_frames_out()
1119 //
1120 //      Write the frames of a .ani file out to disk as .pcx files.
1121 // Use naming convention: filename0000.pcx, filename0001.pcx etc.
1122 //
1123 // return:              0       =>              success
1124 //                                      -1      =>              failed
1125 //
1126 int anim_write_frames_out(char *filename)
1127 {
1128         anim                            *source_anim;
1129         anim_instance   *ai;
1130         char                            root_name[256], pcxname[256];
1131         char                            buf[64];
1132         int                             i,j;
1133         ubyte                           **row_data;
1134
1135         strcpy(root_name, filename);
1136         root_name[strlen(filename)-4] = 0;
1137
1138         source_anim = anim_load(filename);
1139         if ( source_anim == NULL ) 
1140                 return -1;
1141
1142         ai = init_anim_instance(source_anim, 16);
1143
1144         row_data = (ubyte**)malloc((source_anim->height+1) * 4);
1145
1146         for ( i = 0; i < source_anim->total_frames; i++ ) {
1147                 anim_get_next_raw_buffer(ai, 0, 0, 16);
1148                 strcpy(pcxname, root_name);
1149                 sprintf(buf,"%04d",i);
1150                 strcat(pcxname, buf);
1151
1152                 for ( j = 0; j < source_anim->height; j++ ) {
1153                         row_data[j] = &ai->frame[j*source_anim->width];
1154                 }
1155
1156
1157                 pcx_write_bitmap( pcxname,
1158                                                                 source_anim->width,
1159                                                                 source_anim->height,
1160                                                                 row_data,
1161                                                                 source_anim->palette);
1162
1163                 printf(".");
1164
1165         }
1166         printf("\n");
1167         free(row_data);
1168         return 0;
1169 }
1170
1171 // ---------------------------------------------------
1172 //      anim_display_info()
1173 //
1174 //      Display information and statistics about a .ani file.
1175 //      This is called when -i switch is on when running ac.exe
1176 //
1177 void anim_display_info(char *real_filename)
1178 {
1179         CFILE                           *fp;
1180         anim                            A;
1181         float                           percent;
1182         int                             i, uncompressed, compressed, *key_frame_nums=NULL, tmp;
1183         char filename[MAX_FILENAME_LEN];
1184
1185         strcpy( filename, real_filename );
1186         char *p = strchr( filename, '.' );
1187         if ( p ) {
1188                 *p = 0;
1189         }
1190         strcat( filename, ".ani" );
1191
1192         fp = cfopen(filename, "rb");
1193         if ( !fp ) {
1194                 printf("Fatal error opening %s", filename);
1195                 return;
1196         }
1197
1198         anim_read_header(&A, fp);
1199         // read the keyframe frame nums and offsets
1200         key_frame_nums = (int*)malloc(sizeof(int)*A.num_keys);
1201         Assert(key_frame_nums != NULL);
1202         for ( i = 0; i < A.num_keys; i++ ) {
1203                 key_frame_nums[i] = 0;
1204                 cfread(&key_frame_nums[i], 2, 1, fp);
1205                 cfread(&tmp, 4, 1, fp);
1206 //printf("key frame num: %d,%d\n", key_frame_nums[i],tmp);
1207         }
1208
1209         cfread(&compressed, 4, 1, fp);
1210
1211         uncompressed = A.width * A.height * A.total_frames;     // 8 bits per pixel
1212         percent = i2fl(compressed) / uncompressed * 100.0f;
1213
1214         printf("%% of uncompressed size:    %.0f%%\n", percent);
1215         printf("Width:                     %d\n", A.width);
1216         printf("Height:                    %d\n", A.height);
1217         printf("Total Frames:              %d\n", A.total_frames);
1218
1219 #ifndef NDEBUG
1220         printf("Key Frames:                %d\n", A.num_keys);
1221         if ( A.num_keys > 1 && (A.total_frames != A.num_keys) ) {
1222                 printf("key list: (");
1223                 for ( i = 0; i < A.num_keys; i++ ) {
1224                         if ( i < A.num_keys-1 ) 
1225                                 printf("%d, ", key_frame_nums[i]);
1226                         else
1227                                 printf("%d)\n", key_frame_nums[i]);
1228                 }
1229         }
1230 #endif
1231
1232         printf("FPS:                       %d\n", A.fps);
1233
1234 #ifndef NDEBUG
1235         printf("Transparent RGB:           (%d,%d,%d)\n", A.xparent_r, A.xparent_g, A.xparent_b); 
1236 #endif
1237
1238         printf("ac version:                %d\n", A.version);
1239
1240         if ( key_frame_nums != NULL ) {
1241                 free(key_frame_nums);
1242         }
1243
1244         cfclose(fp);
1245 }
1246
1247 void anim_reverse_direction(anim_instance *ai)
1248 {
1249         int temp;
1250
1251         if(!(ai->parent->flags & ANF_ALL_KEYFRAMES)){
1252                  // you're not allowed to call anim_reverse_direction(...) unless every frame is a keyframe!!!!
1253                  // The God of Delta-RLE demands it be thus.
1254                 Int3();
1255         }
1256                 
1257         // flip the animation direction
1258         if(ai->direction == ANIM_DIRECT_FORWARD){
1259                 ai->direction = ANIM_DIRECT_REVERSE;                            
1260         } else if(ai->direction == ANIM_DIRECT_REVERSE){
1261                 ai->direction = ANIM_DIRECT_FORWARD;
1262         }
1263
1264         // flip frame_num and last_frame_num
1265         temp = ai->frame_num;
1266         ai->frame_num = ai->last_frame_num;
1267         ai->last_frame_num = temp;
1268
1269         // flip the start and stop at frames
1270         temp = ai->stop_at;
1271         ai->stop_at = ai->start_at;
1272         ai->start_at = temp;
1273
1274         // make sure to sync up the time correctly 
1275         if(ai->direction == ANIM_DIRECT_FORWARD){
1276                 ai->time_elapsed = ((float)ai->frame_num - (float)ai->start_at - 1.0f) / (float)ai->parent->fps;        
1277         } else if(ai->direction == ANIM_DIRECT_REVERSE) {
1278                 ai->time_elapsed = ((float)ai->start_at - (float)ai->frame_num - 1.0f) / (float)ai->parent->fps;        
1279         }       
1280 }
1281
1282 void anim_pause(anim_instance *ai)
1283 {
1284         ai->paused = 1;
1285 }
1286
1287 void anim_unpause(anim_instance *ai)
1288 {
1289         ai->paused = 0;
1290 }
1291
1292 void anim_ignore_next_frametime()
1293 {
1294         Anim_ignore_frametime=1;
1295 }
1296
1297 int anim_instance_is_streamed(anim_instance *ai)
1298 {
1299         Assert(ai);
1300         return ( ai->parent->flags & ANF_STREAMED );
1301 }
1302
1303 unsigned char anim_instance_get_byte(anim_instance *ai, int offset)
1304 {
1305         int absolute_offset;
1306         anim *parent;
1307         
1308         Assert(ai);
1309         Assert(ai->parent->cfile_ptr);
1310         Assert(ai->parent->flags & ANF_STREAMED);
1311
1312         parent = ai->parent;
1313         absolute_offset = ai->file_offset + offset;
1314
1315         // maybe in cache?
1316         int cache_offset;
1317         cache_offset = absolute_offset - parent->cache_file_offset;
1318         if ( (cache_offset >= 0) && (cache_offset < ANI_STREAM_CACHE_SIZE) ) {
1319                 return parent->cache[cache_offset];
1320         } else {
1321                 // fill cache
1322                 cfseek(parent->cfile_ptr, absolute_offset, CF_SEEK_SET);
1323                 cfread(parent->cache, ANI_STREAM_CACHE_SIZE, 1, parent->cfile_ptr);
1324                 parent->cache_file_offset = absolute_offset;
1325                 return parent->cache[0];
1326         }
1327 }
1328