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