FS2_Open
Open source remastering of the Freespace 2 engine
animplay.cpp
Go to the documentation of this file.
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 the
6  * source.
7  *
8 */
9 
10 
11 
12 #include "anim/animplay.h"
13 #include "anim/packunpack.h"
14 #include "bmpman/bmpman.h"
15 #include "cfile/cfile.h"
16 #include "cmdline/cmdline.h"
17 #include "globalincs/linklist.h"
18 #include "graphics/2d.h"
19 #include "io/timer.h"
20 #include "pcxutils/pcxutils.h"
21 #include "render/3d.h"
22 
23 
24 
25 static color Color_xparent;
26 
27 anim *first_anim = NULL;
30 
31 #define MAX_ANIM_INSTANCES 25
33 
36 
38 
39 int Anim_ignore_frametime=0; // flag used to ignore frametime... useful when need to avoid saturated frametimes
40 
45 void anim_init()
46 {
47  int i;
48 
49  if ( Anim_inited == TRUE )
50  return;
51 
52  list_init( &anim_free_list );
53  list_init( &anim_render_list );
54 
55  // Link all anim render slots into the free list
56  for (i=1; i < MAX_ANIM_INSTANCES; i++) {
57  list_append(&anim_free_list, &anim_render_instance[i]);
58  }
59 
60  Anim_paused = 0;
61  Anim_inited = TRUE;
62 }
63 
67 void anim_render_all(int screen_id, float frametime)
68 {
71 
72  A = GET_FIRST(&anim_render_list);
73  while( A !=END_OF_LIST(&anim_render_list) ) {
74  temp = GET_NEXT(A);
75  if ( A->screen_id == screen_id ) {
76  if ( Anim_ignore_frametime ) {
77  frametime = 0.0f;
79  }
80  if ( anim_show_next_frame(A, frametime) == -1 ) {
81  A->data = NULL;
83  }
84  }
85  A = temp;
86  }
87 }
88 
93 void anim_render_one(int screen_id, anim_instance *ani, float frametime)
94 {
95  // make sure this guy's screen id matches the passed one
96  if(screen_id != ani->screen_id){
97  return;
98  }
99 
100  // otherwise render it
101  if ( Anim_ignore_frametime ) {
102  frametime = 0.0f;
104  }
105  if ( anim_show_next_frame(ani, frametime) == -1 ) {
106  ani->data = NULL;
108  }
109 }
110 
111 MONITOR(NumANIPlayed)
112 
113 
117 void anim_play_init(anim_play_struct *aps, anim *a_info, int x, int y, int base_w, int base_h)
118 {
119  aps->anim_info = a_info;
120  aps->x = x;
121  aps->y = y;
122  aps->base_w = base_w;
123  aps->base_h = base_h;
124  aps->start_at = 0;
125  aps->stop_at = a_info->total_frames - 1;
126  aps->screen_id = 0;
127  aps->world_pos = NULL;
128  aps->radius = 0.0f;
129  aps->framerate_independent = 0;
130  aps->color = NULL;
131  aps->skip_frames = 1;
132  aps->looped = 0;
133  aps->ping_pong = 0;
134 }
135 
144 {
145  Assert( aps->anim_info != NULL );
146  Assert( aps->start_at >= 0 );
147  Assert( aps->stop_at < aps->anim_info->total_frames );
148  Assert( !(aps->looped && aps->ping_pong) ); // shouldn't have these both set at once
149 
150  MONITOR_INC(NumANIPlayed, 1);
151 
152  anim_instance *instance;
153 
154  // Find next free anim instance slot on queue
155  instance = GET_FIRST(&anim_free_list);
156  Assert( instance != &anim_free_list ); // shouldn't have the dummy element
157 
158  // remove instance from the free list
159  list_remove( &anim_free_list, instance );
160 
161  // insert instance onto the end of anim_render_list
162  list_append( &anim_render_list, instance );
163 
164  aps->anim_info->instance_count++;
165  instance->frame_num = -1;
166  instance->last_frame_num = -99;
167  instance->parent = aps->anim_info;
168  instance->data = aps->anim_info->data;
169  if ( anim_instance_is_streamed(instance) ) {
170  instance->file_offset = instance->parent->file_offset;
171  }
172  instance->frame = (ubyte *) vm_malloc(instance->parent->width * instance->parent->height * 2);
173  Assert( instance->frame != NULL );
174  memset( instance->frame, 0, instance->parent->width * instance->parent->height * 2 );
175  instance->time_elapsed = 0.0f;
176  instance->stop_at = aps->stop_at;
177  instance->x = aps->x;
178  instance->y = aps->y;
179  instance->world_pos = aps->world_pos;
180  instance->radius = aps->radius;
182  instance->last_bitmap = -1;
183  instance->stop_now = FALSE;
184  instance->screen_id = aps->screen_id;
185  instance->aa_color = aps->color;
186  instance->skip_frames = aps->skip_frames;
187  instance->looped = aps->looped;
188  instance->ping_pong = aps->ping_pong;
189  instance->direction = ANIM_DIRECT_FORWARD;
190  instance->paused = 0;
191  instance->loop_count = 0;
192  if ( aps->color == NULL ){
193  instance->xlate_pal = 1;
194  } else {
195  instance->xlate_pal = 0;
196  }
197 
198  if(aps->base_w < 0 || aps->base_h < 0) {
201  } else {
202  instance->base_w = aps->base_w;
203  instance->base_h = aps->base_h;
204  }
205 
206  // determining the start_at frame is more complicated, since it must be a key-frame.
207  // Futhermore, need to subtract 1 from key-frame number, since frame number is always
208  // incremented the first time anim_show_next_frame() is called
209 
210  instance->start_at = aps->start_at;
211 
212  if ( aps->start_at > 0 ) {
213  key_frame *keyp;
214  int idx;
215  int key = 0;
216  int offset = 0;
217  int frame_num = aps->start_at;
218 
219  keyp = instance->parent->keys;
220  idx = 0;
221  while (idx < instance->parent->num_keys) {
222  if (key == frame_num)
223  break;
224 
225  key = keyp[idx].frame_num - 1;
226  offset = keyp[idx].offset;
227 
228  idx++;
229  }
230 
231  if (key > instance->frame_num) { // best key is closer than current position
232  instance->frame_num = key;
233  if ( anim_instance_is_streamed(instance) ) {
234  instance->file_offset = instance->parent->file_offset + offset;
235  } else {
236  instance->data = instance->parent->data + offset;
237  }
238 
239  }
240 
241  instance->frame_num--; // required
242  }
243 
244  return instance;
245 }
246 
254 int anim_show_next_frame(anim_instance *instance, float frametime)
255 {
256  int bitmap_id, bitmap_flags=0, new_frame_num, frame_diff=0, i, n_frames=0,frame_save;
257  float percent_through, time;
258  vertex image_vertex;
259  int aabitmap = 0;
260  int bpp = 16;
261 
262  Assert( instance != NULL );
263 
264  instance->time_elapsed += frametime;
265 
266  // Advance to the next frame, if we determine enough time has elapsed.
267  if(instance->direction == ANIM_DIRECT_FORWARD)
268  n_frames = instance->stop_at - instance->start_at + 1;
269  else if(instance->direction == ANIM_DIRECT_REVERSE)
270  n_frames = instance->start_at - instance->stop_at + 1;
271 
272  time = n_frames / i2fl(instance->parent->fps);
273 
274  percent_through = instance->time_elapsed / time;
275 
276  if(instance->direction == ANIM_DIRECT_FORWARD)
277  new_frame_num = instance->start_at - 1 + fl2i(percent_through * n_frames + 0.5f);
278  else
279  new_frame_num = instance->start_at - 1 - fl2i(percent_through * n_frames + 0.5f);
280 
281  frame_save = instance->frame_num;
282 
283  // If framerate independent, use the new_frame_num... unless instance->skip_frames is
284  // FALSE, then only advance a maximum of one frame (this is needed since some big animations
285  // should just play slower rather than taking the hit of decompressing multiple frames and
286  // creating an even greater slowdown
287  if (instance->framerate_independent) {
288  if(instance->direction == ANIM_DIRECT_FORWARD){
289  if ( new_frame_num > instance->last_frame_num) {
290  if ( instance->skip_frames )
291  instance->frame_num = new_frame_num;
292  else
293  instance->frame_num++;
294  }
295  } else if(instance->direction == ANIM_DIRECT_REVERSE){
296  if( new_frame_num < instance->last_frame_num) {
297  if ( instance->skip_frames )
298  instance->frame_num = new_frame_num;
299  else
300  instance->frame_num--;
301  }
302  }
303  }
304  else {
305  if(instance->direction == ANIM_DIRECT_FORWARD){
306  if ( new_frame_num > instance->last_frame_num) {
307  instance->frame_num++;
308  }
309  } else if(instance->direction == ANIM_DIRECT_REVERSE){
310  if ( new_frame_num < instance->last_frame_num) {
311  instance->frame_num--;
312  }
313  }
314  }
315 
316  if(instance->direction == ANIM_DIRECT_FORWARD){
317  if ( instance->frame_num < instance->start_at ) {
318  instance->frame_num = instance->start_at;
319  }
320  } else if(instance->direction == ANIM_DIRECT_REVERSE){
321  if ( instance->frame_num > instance->start_at ) {
322  instance->frame_num = instance->start_at;
323  }
324  }
325 
326  if ( instance->stop_now == TRUE ) {
327  return -1;
328  }
329 
330  // If past the last frame, clamp to the last frame and then set the stop_now flag in the
331  // anim instance. The next iteration, the animation will stop.
332  if(instance->direction == ANIM_DIRECT_FORWARD){
333  if (instance->frame_num >= instance->stop_at ) {
334  if (instance->looped) { // looped animations
335  instance->frame_num = instance->stop_at;
336  instance->time_elapsed = 0.0f;
337  } else if(instance->ping_pong) { // pingponged animations
338  instance->frame_num = instance->stop_at;
339  anim_reverse_direction(instance);
340  } else { // one-shot animations
341  instance->frame_num = instance->stop_at;
342  instance->last_frame_num = instance->frame_num;
343  instance->stop_now = TRUE;
344  }
345  }
346  } else if(instance->direction == ANIM_DIRECT_REVERSE){
347  if (instance->frame_num <= instance->stop_at ) {
348  if (instance->looped) { // looped animations
349  instance->frame_num = instance->stop_at;
350  instance->time_elapsed = 0.0f;
351  } else if(instance->ping_pong) { // pingponged animations
352  instance->frame_num = instance->stop_at;
353  anim_reverse_direction(instance);
354  } else { // one-shot animations
355  instance->frame_num = instance->stop_at+1;
356  instance->last_frame_num = instance->frame_num;
357  instance->stop_now = TRUE;
358  }
359  }
360  }
361 
362  if(instance->direction == ANIM_DIRECT_FORWARD){
363  if( instance->last_frame_num >= instance->start_at ) {
364  frame_diff = instance->frame_num - instance->last_frame_num;
365  } else {
366  frame_diff = 1;
367  }
368  } else if(instance->direction == ANIM_DIRECT_REVERSE){
369  if( instance->last_frame_num <= instance->start_at ) {
370  frame_diff = instance->last_frame_num - instance->frame_num;
371  } else {
372  frame_diff = 1;
373  }
374  }
375  Assert(frame_diff >= 0);
376  Assert( instance->frame_num >= 0 && instance->frame_num < instance->parent->total_frames );
377 
378  // if the anim is paused, ignore all the above changes and still display this frame
379  if(instance->paused || Anim_paused){
380  instance->frame_num = frame_save;
381  instance->time_elapsed -= frametime;
382  frame_diff = 0;
383  }
384 
385  if (instance->parent->flags & ANF_XPARENT){
386  bitmap_flags = 0;
387  }
388  bpp = 16;
389  if(instance->aa_color != NULL){
390  bitmap_flags |= BMP_AABITMAP;
391  aabitmap = 1;
392  bpp = 8;
393  }
394 
395  if ( frame_diff > 0 ) {
396  instance->last_frame_num = instance->frame_num;
397 
399  for ( i = 0; i < frame_diff; i++ ) {
401 
402  // if we're playing backwards, every frame must be a keyframe and we set the data ptr here
403  if(instance->direction == ANIM_DIRECT_REVERSE){
404  if ( anim_instance_is_streamed(instance) ) {
405  instance->file_offset = instance->parent->file_offset + instance->parent->keys[instance->frame_num-1].offset;
406  } else {
407  instance->data = instance->parent->data + instance->parent->keys[instance->frame_num-1].offset;
408  }
409  }
410 
411  ubyte *temp = NULL;
412  int temp_file_offset = -1;
413 
414  // if we're using bitmap polys
416 
417  if ( anim_instance_is_streamed(instance) ) {
418  if ( instance->xlate_pal ){
419  temp_file_offset = unpack_frame_from_file(instance, instance->frame, instance->parent->width*instance->parent->height, instance->parent->palette_translation, aabitmap, bpp);
420  } else {
421  temp_file_offset = unpack_frame_from_file(instance, instance->frame, instance->parent->width*instance->parent->height, NULL, aabitmap, bpp);
422  }
423  } else {
424  if ( instance->xlate_pal ){
425  temp = unpack_frame(instance, instance->data, instance->frame, instance->parent->width*instance->parent->height, instance->parent->palette_translation, aabitmap, bpp);
426  } else {
427  temp = unpack_frame(instance, instance->data, instance->frame, instance->parent->width*instance->parent->height, NULL, aabitmap, bpp);
428  }
429  }
430 
431  // always go back to screen format
433 
434  // see if we had an error during decode (corrupted anim stream)
435  if ( (temp == NULL) && (temp_file_offset < 0) ) {
436  mprintf(("ANI: Fatal ERROR at frame %i!! Aborting playback of \"%s\"...\n", instance->frame_num, instance->parent->name));
437 
438  // return -1 to end all playing of this anim instanc
439  return -1;
440  }
441 
442  if(instance->direction == ANIM_DIRECT_FORWARD){
443  if ( anim_instance_is_streamed(instance) ) {
444  instance->file_offset = temp_file_offset;
445  } else {
446  instance->data = temp;
447  }
448  }
449  }
451  }
452  else {
453  t2=t1=0;
454  }
455 
456  // this only happens when the anim is being looped, we need to reset the last_frame_num
457  if ( (instance->time_elapsed == 0) && (instance->looped) ) {
458  instance->last_frame_num = -1;
459  instance->frame_num = -1;
460  instance->data = instance->parent->data;
461  instance->file_offset = instance->parent->file_offset;
462  instance->loop_count++;
463  }
464 
466  if ( frame_diff == 0 && instance->last_bitmap != -1 ) {
467  bitmap_id = instance->last_bitmap;
468  }
469  else {
470  if ( instance->last_bitmap != -1 ){
471  bm_release(instance->last_bitmap);
472  }
473  bitmap_id = bm_create(bpp, instance->parent->width, instance->parent->height, instance->frame, bitmap_flags);
474  }
475 
476  if ( bitmap_id == -1 ) {
477  // anim has finsished playing, free the instance frame data
478  anim_release_render_instance(instance);
479  return -1;
480 
481  // NOTE: there is no need to free the instance, since it was pre-allocated as
482  // part of the anim_free_list
483  }
484  else {
485  gr_set_bitmap(bitmap_id);
486 
487  // determine x,y to display the bitmap at
488  if ( instance->world_pos == NULL ) {
489  int old_max_w_unscaled = gr_screen.max_w_unscaled;
490  int old_max_h_unscaled = gr_screen.max_h_unscaled;
491  int old_max_w_unscaled_zoomed = gr_screen.max_w_unscaled_zoomed;
492  int old_max_h_unscaled_zoomed = gr_screen.max_h_unscaled_zoomed;
493  gr_set_screen_scale(instance->base_w, instance->base_h);
494  gr_set_clip(0, 0, instance->base_w, instance->base_h, GR_RESIZE_MENU);
495  if ( instance->aa_color == NULL ) {
496  gr_bitmap(instance->x, instance->y, GR_RESIZE_MENU_NO_OFFSET);
497  }
498  else {
499  gr_set_color_fast( (color*)instance->aa_color );
500  gr_aabitmap(instance->x, instance->y, GR_RESIZE_MENU_NO_OFFSET);
501  }
502  gr_set_screen_scale(old_max_w_unscaled, old_max_h_unscaled, old_max_w_unscaled_zoomed, old_max_h_unscaled_zoomed);
503  gr_reset_clip();
504  }
505  else {
506  g3_rotate_vertex(&image_vertex,instance->world_pos);
507  Assert(instance->radius != 0.0f);
508  g3_draw_bitmap(&image_vertex, 0, instance->radius*1.5f, TMAP_FLAG_TEXTURED | TMAP_HTL_2D);
509  }
510 
511  instance->last_bitmap = bitmap_id;
512  }
513 
515 
516  return 0;
517 }
518 
523 {
524  Assert(instance != NULL);
525 
526  if ( anim_playing(instance) ) {
528  }
529  return 0;
530 }
531 
539 {
540  Assert( instance != NULL );
541 
542  if (instance->frame != NULL)
543  vm_free(instance->frame);
544 
545  instance->frame = NULL;
546  instance->parent->instance_count--;
547 
548  if ( instance->last_bitmap != -1 ) {
549  bm_release(instance->last_bitmap);
550  instance->last_bitmap = -1;
551  }
552 
553  // remove instance from anim_render_list
554  list_remove( &anim_render_list, instance );
555 
556  // insert instance into the anim_free_list
557  list_append( &anim_free_list, instance );
558 }
559 
567 void anim_release_all_instances(int screen_id)
568 {
569  anim_instance* A;
571 
572  if ( Anim_inited == FALSE )
573  return;
574 
575  A = GET_FIRST(&anim_render_list);
576  while( A !=END_OF_LIST(&anim_render_list) ) {
577  temp = GET_NEXT(A);
578  if ( A->screen_id == screen_id || screen_id == 0 ) {
580  }
581  A = temp;
582  }
583 }
584 
585 // -----------------------------------------------------------------------------
586 // anim_read_header()
587 //
588 // Read the header of a .ani file. Below is the format of a .ani header
589 //
590 // #bytes | description
591 // 2 | obsolete, kept for compatibility with old versions
592 // 2 | version number
593 // 2 | fps
594 // 1 | transparent red value
595 // 1 | transparent green value
596 // 1 | transparent blue value
597 // 2 | width
598 // 2 | height
599 // 2 | number of frames
600 // 1 | packer code
601 // 763 | palette
602 // 2 | number of key frames
603 // 2 | key frame number } repeats
604 // 4 | key frame offset } repeats
605 // 4 | compressed data length
606 //
608 {
609  ptr->width = cfread_short(fp);
610  // If first 2 bytes are zero, this means we are using a new format, which includes
611  // a version, and fps values. This is only done since a version number was not included
612  // in the original header.
613 
614  // default
615  Color_xparent.red = 0;
616  Color_xparent.green = 255;
617  Color_xparent.blue = 0;
618 
619  if ( ptr->width == 0 ) {
620  ptr->version = cfread_short(fp);
621  ptr->fps = cfread_short(fp);
622 
623  // version 2 added a custom transparency color
624  if ( ptr->version >= 2 ) {
625  cfread(&Color_xparent.red, 1, 1, fp);
626  cfread(&Color_xparent.green, 1, 1, fp);
627  cfread(&Color_xparent.blue, 1, 1, fp);
628  }
629 
630  ptr->width = cfread_short(fp);
631  }
632  else {
633  ptr->version = 0;
634  ptr->fps = 30;
635  }
636 
637  ptr->height = cfread_short(fp);
638 
639 #ifndef NDEBUG
640  // get size of ani compared to power of 2
641  int r, floor_pow;
642  r = ptr->height;
643  floor_pow = 0;
644 
645  while(r >= 2) {
646  r /= 2;
647  floor_pow++;
648  }
649 
650  int floor_size = (int) pow(2.0, floor_pow);
651  int diff = ptr->height - floor_size;
652  float waste = 100.0f * float((floor_size - diff))/(2.0f *(float)floor_size);
653 
654  if (diff != 0) {
655  if (ptr->height > 16) {
656  mprintf(("ANI %s with size %dx%d (%.1f%% wasted)\n", ptr->name, ptr->width, ptr->height, waste));
657  }
658  }
659 #endif
660 
661  ptr->total_frames = cfread_short(fp);
662  ptr->packer_code = cfread_ubyte(fp);
663  cfread(&ptr->palette, 256, 3, fp);
664  ptr->num_keys = cfread_short(fp);
665 
666  // store xparent colors
667  ptr->xparent_r = Color_xparent.red;
668  ptr->xparent_g = Color_xparent.green;
669  ptr->xparent_b = Color_xparent.blue;
670 
671  if(ptr->total_frames == ptr->num_keys){
672  ptr->flags |= ANF_ALL_KEYFRAMES;
673  }
674 }
675 
687 anim *anim_load(char *real_filename, int cf_dir_type, int file_mapped)
688 {
689  anim *ptr;
690  CFILE *fp;
691  int count,idx;
692  char name[_MAX_PATH];
693 
694  Assert( real_filename != NULL );
695 
696  strcpy_s( name, real_filename );
697  char *p = strchr( name, '.' );
698  if ( p ) {
699  *p = 0;
700  }
701  strcat_s( name, ".ani" );
702 
703  ptr = first_anim;
704  while (ptr) {
705  if (!stricmp(name, ptr->name))
706  break;
707 
708  ptr = ptr->next;
709  }
710 
711  if (!ptr) {
712  fp = cfopen(name, "rb", CFILE_NORMAL, cf_dir_type);
713  if ( !fp )
714  return NULL;
715 
716  ptr = (anim *) vm_malloc(sizeof(anim));
717  Assert(ptr);
718 
719  ptr->flags = 0;
720  ptr->next = first_anim;
721  first_anim = ptr;
722  Assert(strlen(name) < _MAX_PATH - 1);
723  strcpy_s(ptr->name, name);
724  ptr->instance_count = 0;
725  ptr->width = 0;
726  ptr->height = 0;
727  ptr->total_frames = 0;
728  ptr->keys = NULL;
729  ptr->ref_count=0;
730 
731  anim_read_header(ptr, fp);
732 
733  if(ptr->num_keys > 0){
734  ptr->keys = (key_frame*)vm_malloc(sizeof(key_frame) * ptr->num_keys);
735  Assert(ptr->keys != NULL);
736  }
737 
738  // store how long the anim should take on playback (in seconds)
739  ptr->time = i2fl(ptr->total_frames)/ptr->fps;
740 
741  for(idx=0;idx<ptr->num_keys;idx++){
742  ptr->keys[idx].frame_num = 0;
743  cfread(&ptr->keys[idx].frame_num, 2, 1, fp);
744  cfread(&ptr->keys[idx].offset, 4, 1, fp);
745  ptr->keys[idx].frame_num = INTEL_INT( ptr->keys[idx].frame_num ); //-V570
746  ptr->keys[idx].offset = INTEL_INT( ptr->keys[idx].offset ); //-V570
747  }
748 
749  cfread(&count, 4, 1, fp); // size of compressed data
750  count = INTEL_INT( count );
751 
752  ptr->cfile_ptr = NULL;
753 
754  if ( file_mapped == PAGE_FROM_MEM) {
755  // Try mapping the file to memory
756  ptr->flags |= ANF_MEM_MAPPED;
757  ptr->cfile_ptr = cfopen(name, "rb", CFILE_MEMORY_MAPPED, cf_dir_type);
758  }
759 
760  // couldn't memory-map file... must be in a packfile, so stream manually
761  if ( file_mapped && !ptr->cfile_ptr ) {
762  ptr->flags &= ~ANF_MEM_MAPPED;
763  ptr->flags |= ANF_STREAMED;
764  ptr->cfile_ptr = cfopen(name, "rb", CFILE_NORMAL, cf_dir_type);
765  }
766 
767  ptr->cache = NULL;
768 
769  // If it opened properly as mem-mapped (or streamed)
770  if (ptr->cfile_ptr != NULL) {
771  // VERY IMPORTANT STEP
772  // Set the data pointer to the compressed data (which is not at the start of the
773  // file). Use ftell() to find out how far we've already parsed into the file
774  //
775  int offset;
776  offset = cftell(fp);
777  ptr->file_offset = offset;
778  if ( ptr->flags & ANF_STREAMED ) {
779  ptr->data = NULL;
780  ptr->cache_file_offset = ptr->file_offset;
782  Assert(ptr->cache);
783  cfseek(ptr->cfile_ptr, offset, CF_SEEK_SET);
785  } else {
786  ptr->data = (ubyte*)cf_returndata(ptr->cfile_ptr) + offset;
787  }
788  } else {
789  // Not a memory mapped file (or streamed)
790  ptr->flags &= ~ANF_MEM_MAPPED;
791  ptr->flags &= ~ANF_STREAMED;
792  ptr->data = (ubyte *) vm_malloc(count);
793  ptr->file_offset = -1;
794  cfread(ptr->data, count, 1, fp);
795  }
796 
797  cfclose(fp);
798 
799  // store screen signature, so we can tell if palette changes
801 
802  anim_set_palette(ptr);
803  }
804 
805  ptr->ref_count++;
806  return ptr;
807 }
808 
813 int anim_free(anim *ptr)
814 {
815  Assert ( ptr != NULL );
816  anim *list, **prev_anim;
817 
818  list = first_anim;
819  prev_anim = &first_anim;
820  while (list && (list != ptr)) {
821  prev_anim = &list->next;
822  list = list->next;
823  }
824 
825  if ( !list )
826  return -2;
827 
828  // only free when ref_count is 0
829  ptr->ref_count--;
830  if ( ptr->ref_count > 0 )
831  return -1;
832 
833  // only free if there are no playing instances
834  if ( ptr->instance_count > 0 )
835  return -1;
836 
837  if(ptr->keys != NULL){
838  vm_free(ptr->keys);
839  ptr->keys = NULL;
840  }
841 
842  if ( ptr->flags & (ANF_MEM_MAPPED|ANF_STREAMED) ) {
843  cfclose(ptr->cfile_ptr);
844  if (ptr->cache != NULL) {
845  vm_free(ptr->cache);
846  ptr->cache = NULL;
847  }
848  }
849  else {
850  Assert(ptr->data);
851  if (ptr->data != NULL) {
852  vm_free(ptr->data);
853  ptr->data = NULL;
854  }
855  }
856 
857  *prev_anim = ptr->next;
858  vm_free(ptr);
859  return 0;
860 }
861 
862 
867 {
868  Assert(ai != NULL);
869  if ( ai->frame == NULL )
870  return 0;
871  else
872  return 1;
873 }
874 
875 
881 {
882 }
883 
888 {
890 }
891 
899 {
900  anim *source_anim;
901  anim_instance *ai;
902  char root_name[256], pcxname[256];
903  char buf[64];
904  int i,j;
905  ubyte **row_data;
906 
907  strcpy_s(root_name, filename);
908  root_name[strlen(filename)-4] = 0;
909 
910  source_anim = anim_load(filename);
911  if ( source_anim == NULL )
912  return -1;
913 
914  ai = init_anim_instance(source_anim, 16);
915 
916  row_data = (ubyte**)vm_malloc((source_anim->height+1) * 4);
917 
918  for ( i = 0; i < source_anim->total_frames; i++ ) {
919  anim_get_next_raw_buffer(ai, 0, 0, 16);
920  strcpy_s(pcxname, root_name);
921  sprintf(buf,"%04d",i);
922  strcat_s(pcxname, buf);
923 
924  for ( j = 0; j < source_anim->height; j++ ) {
925  row_data[j] = &ai->frame[j*source_anim->width];
926  }
927 
928 
929  pcx_write_bitmap( pcxname,
930  source_anim->width,
931  source_anim->height,
932  row_data,
933  source_anim->palette);
934 
935  printf(".");
936 
937  }
938  printf("\n");
939  vm_free(row_data);
940  return 0;
941 }
942 
947 void anim_display_info(char *real_filename)
948 {
949  CFILE *fp;
950  anim A;
951  float percent;
952  int i, uncompressed, compressed, *key_frame_nums=NULL, tmp;
954 
955  strcpy_s( filename, real_filename );
956  char *p = strchr( filename, '.' );
957  if ( p ) {
958  *p = 0;
959  }
960  strcat_s( filename, ".ani" );
961 
962  fp = cfopen(filename, "rb");
963  if ( !fp ) {
964  printf("Fatal error opening %s", filename);
965  return;
966  }
967 
968  anim_read_header(&A, fp);
969  // read the keyframe frame nums and offsets
970  key_frame_nums = (int*)vm_malloc(sizeof(int)*A.num_keys);
971  Assert(key_frame_nums != NULL);
972  if (key_frame_nums == NULL)
973  return;
974 
975  for ( i = 0; i < A.num_keys; i++ ) {
976  key_frame_nums[i] = 0;
977  cfread(&key_frame_nums[i], 2, 1, fp);
978  cfread(&tmp, 4, 1, fp);
979  }
980 
981  cfread(&compressed, 4, 1, fp);
982 
983  uncompressed = A.width * A.height * A.total_frames; // 8 bits per pixel
984  percent = i2fl(compressed) / uncompressed * 100.0f;
985 
986  printf("%% of uncompressed size: %.0f%%\n", percent);
987  printf("Width: %d\n", A.width);
988  printf("Height: %d\n", A.height);
989  printf("Total Frames: %d\n", A.total_frames);
990 
991 #ifndef NDEBUG
992  printf("Key Frames: %d\n", A.num_keys);
993  if ( A.num_keys > 1 && (A.total_frames != A.num_keys) ) {
994  printf("key list: (");
995  for ( i = 0; i < A.num_keys; i++ ) {
996  if ( i < A.num_keys-1 )
997  printf("%d, ", key_frame_nums[i]);
998  else
999  printf("%d)\n", key_frame_nums[i]);
1000  }
1001  }
1002 #endif
1003 
1004  printf("FPS: %d\n", A.fps);
1005 
1006 #ifndef NDEBUG
1007  printf("Transparent RGB: (%u,%u,%u)\n", A.xparent_r, A.xparent_g, A.xparent_b);
1008 #endif
1009 
1010  printf("ac version: %d\n", A.version);
1011 
1012  if ( key_frame_nums != NULL ) {
1013  vm_free(key_frame_nums);
1014  }
1015 
1016  cfclose(fp);
1017 }
1018 
1020 {
1021  int temp;
1022 
1023  // you're not allowed to call anim_reverse_direction(...) unless every frame is a keyframe!!!!
1024  // The God of Delta-RLE demands it be thus.
1025  Assertion( ai->parent->flags & ANF_ALL_KEYFRAMES, "Ani was set to play backwards. In order to enable this, all frames of the animation MUST be keyframes.");
1026 
1027  // flip the animation direction
1028  if(ai->direction == ANIM_DIRECT_FORWARD){
1030  } else if(ai->direction == ANIM_DIRECT_REVERSE){
1032  }
1033 
1034  // flip frame_num and last_frame_num
1035  temp = ai->frame_num;
1036  ai->frame_num = ai->last_frame_num;
1037  ai->last_frame_num = temp;
1038 
1039  // flip the start and stop at frames
1040  temp = ai->stop_at;
1041  ai->stop_at = ai->start_at;
1042  ai->start_at = temp;
1043 
1044  // make sure to sync up the time correctly
1045  if(ai->direction == ANIM_DIRECT_FORWARD){
1046  ai->time_elapsed = ((float)ai->frame_num - (float)ai->start_at - 1.0f) / (float)ai->parent->fps;
1047  } else if(ai->direction == ANIM_DIRECT_REVERSE) {
1048  ai->time_elapsed = ((float)ai->start_at - (float)ai->frame_num - 1.0f) / (float)ai->parent->fps;
1049  }
1050 }
1051 
1053 {
1054  ai->paused = 1;
1055 }
1056 
1058 {
1059  ai->paused = 0;
1060 }
1061 
1063 {
1065 }
1066 
1068 {
1069  Assert(ai);
1070  return ( ai->parent->flags & ANF_STREAMED );
1071 }
1072 
1074 {
1075  int absolute_offset;
1076  anim *parent;
1077 
1078  Assert(ai);
1079  Assert(ai->parent->cfile_ptr);
1080  Assert(ai->parent->flags & ANF_STREAMED);
1081 
1082  parent = ai->parent;
1083  absolute_offset = ai->file_offset + offset;
1084 
1085  // maybe in cache?
1086  int cache_offset;
1087  cache_offset = absolute_offset - parent->cache_file_offset;
1088  if ( (cache_offset >= 0) && (cache_offset < ANI_STREAM_CACHE_SIZE) ) {
1089  return parent->cache[cache_offset];
1090  } else {
1091  // fill cache
1092  cfseek(parent->cfile_ptr, absolute_offset, CF_SEEK_SET);
1093  cfread(parent->cache, ANI_STREAM_CACHE_SIZE, 1, parent->cfile_ptr);
1094  parent->cache_file_offset = absolute_offset;
1095  return parent->cache[0];
1096  }
1097 }
vec3d * world_pos
Definition: animplay.h:32
#define MAX_FILENAME_LEN
Definition: pstypes.h:324
void * color
Definition: animplay.h:35
int unpack_frame_from_file(anim_instance *ai, ubyte *frame, int size, ubyte *pal_translate, int aabitmap, int bpp)
Unpack frame from file.
Definition: packunpack.cpp:858
int height
Definition: packunpack.h:44
#define CFILE_NORMAL
Definition: cfile.h:89
int i
Definition: multi_pxo.cpp:466
int anim_playing(anim_instance *ai)
Return if an anim is playing or not.
Definition: animplay.cpp:866
#define vm_free(ptr)
Definition: pstypes.h:548
int last_frame_num
Definition: packunpack.h:78
int framerate_independent
Definition: packunpack.h:85
int anim_write_frames_out(char *filename)
Write the frames of a .ani file out to disk as .pcx files.
Definition: animplay.cpp:898
int instance_count
Definition: packunpack.h:46
#define GR_RESIZE_MENU_NO_OFFSET
Definition: 2d.h:686
int offset
Definition: packunpack.h:31
CFILE * cfile_ptr
Definition: packunpack.h:54
anim * next
Definition: packunpack.h:40
int num_keys
Definition: packunpack.h:49
anim_instance anim_free_list
Definition: animplay.cpp:28
#define GR_RESIZE_MENU
Definition: 2d.h:684
int cfread(void *buf, int elsize, int nelem, CFILE *fp)
int Anim_paused
Definition: animplay.cpp:34
void anim_level_close()
Called after the end of a mission to clean up any mission dependent anim data.
Definition: animplay.cpp:887
Assert(pm!=NULL)
ubyte * frame
Definition: packunpack.h:81
#define mprintf(args)
Definition: pstypes.h:238
void anim_set_palette(anim *ptr)
Set animation palette.
Definition: 2d.h:95
#define _MAX_PATH
Definition: config.h:221
int max_h_unscaled
Definition: 2d.h:361
#define CFILE_MEMORY_MAPPED
Definition: cfile.h:90
int total_frames
Definition: packunpack.h:45
GLclampf f
Definition: Glext.h:7097
void * aa_color
Definition: packunpack.h:91
#define TRUE
Definition: pstypes.h:399
ubyte xparent_g
Definition: packunpack.h:58
Definition: cfile.h:28
#define Assertion(expr, msg,...)
Definition: clang.h:41
#define CF_SEEK_SET
Definition: cfile.h:24
fix t2
Definition: animplay.cpp:37
int flags
Definition: packunpack.h:60
void gr_set_screen_scale(int w, int h, int zoom_w, int zoom_h, int max_w, int max_h, int center_w, int center_h, bool force_stretch)
Definition: 2d.cpp:104
#define ANF_ALL_KEYFRAMES
Definition: packunpack.h:37
#define ANIM_DIRECT_FORWARD
Definition: packunpack.h:68
void gr_set_color_fast(color *dst)
Definition: 2d.cpp:1197
ubyte blue
Definition: 2d.h:102
int key
void gr_set_bitmap(int bitmap_num, int alphablend_mode, int bitblt_mode, float alpha)
Definition: 2d.cpp:2105
ubyte palette_translation[256]
Definition: packunpack.h:52
int max_w_unscaled
Definition: 2d.h:361
unsigned char anim_instance_get_byte(anim_instance *ai, int offset)
Definition: animplay.cpp:1073
void anim_release_all_instances(int screen_id)
Free all anim instances that are on the anim_render_list.
Definition: animplay.cpp:567
#define TMAP_HTL_2D
Definition: tmapper.h:64
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: Glext.h:7308
int bm_release(int handle, int clear_render_targets)
Frees both a bitmap's data and it's associated slot.
Definition: bmpman.cpp:2603
void BM_SELECT_TEX_FORMAT()
Sets bm_set_components and bm_get_components to reference texture format functions.
Definition: bmpman.cpp:2745
#define BMP_AABITMAP
antialiased bitmap
Definition: bmpman.h:52
int cache_file_offset
Definition: packunpack.h:63
ubyte green
Definition: 2d.h:101
char name[MAX_PATH_LEN]
Definition: packunpack.h:41
uint signature
Definition: 2d.h:359
__inline void gr_set_clip(int x, int y, int w, int h, int resize_mode=GR_RESIZE_FULL)
Definition: 2d.h:741
key_frame * keys
Definition: packunpack.h:50
ubyte xparent_r
Definition: packunpack.h:57
typedef int(SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version *)
#define gr_reset_clip
Definition: 2d.h:745
anim_instance anim_render_list
Definition: animplay.cpp:29
GLintptr offset
Definition: Glext.h:5497
ubyte packer_code
Definition: packunpack.h:42
void anim_unpause(anim_instance *ai)
Definition: animplay.cpp:1057
GLdouble GLdouble GLdouble r
Definition: Glext.h:5337
#define cfopen(...)
Definition: cfile.h:134
#define ANF_MEM_MAPPED
Definition: packunpack.h:34
ubyte * data
Definition: packunpack.h:80
char * filename
void anim_release_render_instance(anim_instance *instance)
Free a particular animation instance that is on the anim_render_list. Do not call this function to fr...
Definition: animplay.cpp:538
void anim_level_init()
Called at the beginning of a mission to initialize any mission dependent anim data.
Definition: animplay.cpp:880
sprintf(buf,"(%f,%f,%f)", v3->xyz.x, v3->xyz.y, v3->xyz.z)
ubyte xparent_b
Definition: packunpack.h:59
ubyte red
Definition: 2d.h:100
int anim_free(anim *ptr)
Free an animation that was loaded with anim_load().
Definition: animplay.cpp:813
ubyte g3_rotate_vertex(vertex *dest, const vec3d *src)
Definition: 3dmath.cpp:97
int width
Definition: packunpack.h:43
int frame_num
Definition: packunpack.h:30
int Anim_ignore_frametime
Definition: animplay.cpp:39
#define MONITOR(function_name)
Definition: pstypes.h:454
short cfread_short(CFILE *file, int ver, short deflt)
Definition: cfile.cpp:1192
void anim_init()
Initialise animation.
Definition: animplay.cpp:45
void anim_render_all(int screen_id, float frametime)
Display the frames for the currently playing anims.
Definition: animplay.cpp:67
ubyte * unpack_frame(anim_instance *ai, ubyte *ptr, ubyte *frame, int size, ubyte *pal_translate, int aabitmap, int bpp)
Unpack frame.
Definition: packunpack.cpp:669
int idx
Definition: multiui.cpp:761
cfbp fp
Definition: cfile.cpp:1065
GLint GLint GLint GLint GLint x
Definition: Glext.h:5182
long fix
Definition: pstypes.h:54
unsigned char ubyte
Definition: pstypes.h:62
#define MONITOR_INC(function_name, inc)
Definition: pstypes.h:457
int bm_create(int bpp, int w, int h, void *data, int flags)
Definition: bmpman.cpp:469
anim * anim_info
Definition: animplay.h:24
void anim_ignore_next_frametime()
Definition: animplay.cpp:1062
anim_instance * anim_play(anim_play_struct *aps)
Will add an anim instance to the anim_render_list. This will cause the anim to be played at the x...
Definition: animplay.cpp:143
int ref_count
Definition: packunpack.h:47
#define MAX_ANIM_INSTANCES
Definition: animplay.cpp:31
#define vm_malloc(size)
Definition: pstypes.h:547
ubyte cfread_ubyte(CFILE *file, int ver, ubyte deflt)
Definition: cfile.cpp:1220
typedef void(APIENTRY *PFNGLARRAYELEMENTEXTPROC)(GLint i)
#define INTEL_INT(x)
Definition: pstypes.h:388
GLuint const GLchar * name
Definition: Glext.h:5608
float radius
Definition: packunpack.h:76
int max_w_unscaled_zoomed
Definition: 2d.h:362
fix t1
Definition: animplay.cpp:37
int fps
Definition: packunpack.h:56
int cftell(CFILE *fp)
int Anim_inited
Global variable to pause the playing back of anims.
Definition: animplay.cpp:35
typedef float(SCP_EXT_CALLCONV *SCPTRACKIR_PFFLOATVOID)()
void anim_read_header(anim *ptr, CFILE *fp)
Definition: animplay.cpp:607
void anim_play_init(anim_play_struct *aps, anim *a_info, int x, int y, int base_w, int base_h)
Setup an anim_play_struct for passing into anim_play().
Definition: animplay.cpp:117
#define strcat_s(...)
Definition: safe_strings.h:68
ubyte * anim_get_next_raw_buffer(anim_instance *inst, int xlate_pal, int aabitmap, int bpp)
Definition: packunpack.cpp:127
#define ANIM_DIRECT_REVERSE
Definition: packunpack.h:69
int framerate_independent
Definition: animplay.h:34
#define fl2i(fl)
Definition: floating.h:33
fix timer_get_fixed_seconds()
Definition: timer.cpp:116
float time
Definition: packunpack.h:48
#define ANF_XPARENT
Definition: packunpack.h:36
screen gr_screen
Definition: 2d.cpp:46
float time_elapsed
Definition: packunpack.h:82
int file_offset
Definition: packunpack.h:62
ubyte * data
Definition: packunpack.h:53
void anim_reverse_direction(anim_instance *ai)
Definition: animplay.cpp:1019
GLfloat GLfloat p
Definition: Glext.h:8373
__inline void gr_aabitmap(int x, int y, int resize_mode=GR_RESIZE_FULL, bool mirror=false)
Definition: 2d.h:750
#define TMAP_FLAG_TEXTURED
Definition: tmapper.h:36
#define ANF_STREAMED
Definition: packunpack.h:35
int anim_instance_is_streamed(anim_instance *ai)
Definition: animplay.cpp:1067
int g3_draw_bitmap(vertex *pos, int orient, float radius, uint tmap_flags, float depth=0.0f)
Definition: 3ddraw.cpp:649
int anim_show_next_frame(anim_instance *instance, float frametime)
This function is called to blit the next frame of an anim instance to the screen. This is normally ca...
Definition: animplay.cpp:254
#define ANI_STREAM_CACHE_SIZE
Definition: packunpack.h:19
uint screen_sig
Definition: packunpack.h:61
#define i2fl(i)
Definition: floating.h:32
void anim_display_info(char *real_filename)
Display information and statistics about a .ani file.
Definition: animplay.cpp:947
GLint GLsizei count
Definition: Gl.h:1491
void gr_bitmap(int _x, int _y, int resize_mode)
Definition: 2d.cpp:1303
matrix * A
Definition: lua.cpp:444
anim_instance * init_anim_instance(anim *ptr, int bpp)
Definition: packunpack.cpp:28
void * cf_returndata(CFILE *cfile)
Definition: cfile.cpp:1110
int temp
Definition: lua.cpp:4996
int version
Definition: packunpack.h:55
void anim_render_one(int screen_id, anim_instance *ani, float frametime)
Display the frames for the passed animation.
Definition: animplay.cpp:93
int cfclose(CFILE *cfile)
Definition: cfile.cpp:895
anim_instance anim_render_instance[MAX_ANIM_INSTANCES]
Definition: animplay.cpp:32
ubyte * cache
Definition: packunpack.h:64
int pcx_write_bitmap(const char *real_filename, int w, int h, ubyte **row_ptrs, ubyte *palette)
Definition: pcxutils.cpp:455
#define FALSE
Definition: pstypes.h:400
void anim_pause(anim_instance *ai)
Definition: animplay.cpp:1052
#define stricmp(s1, s2)
Definition: config.h:271
vec3d * world_pos
Definition: packunpack.h:75
int anim_stop_playing(anim_instance *instance)
Stop an anim instance that is on the anim_render_list from playing.
Definition: animplay.cpp:522
int max_h_unscaled_zoomed
Definition: 2d.h:362
anim * anim_load(char *real_filename, int cf_dir_type, int file_mapped)
Load an animation. This stores the compressed data, which instances of the animation can reference...
Definition: animplay.cpp:687
GLint y
Definition: Gl.h:1505
anim * first_anim
Definition: animplay.cpp:27
anim * parent
Definition: packunpack.h:79
ubyte palette[768]
Definition: packunpack.h:51
#define strcpy_s(...)
Definition: safe_strings.h:67
void BM_SELECT_SCREEN_FORMAT()
Sets bm_set_components and bm_get_components to reference screen format functions.
Definition: bmpman.cpp:2733
int cfseek(CFILE *fp, int offset, int where)
void anim_check_for_palette_change(anim_instance *instance)
Definition: packunpack.cpp:21