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