]> icculus.org git repositories - taylor/freespace2.git/blob - src/ac/convert.cpp
added copyright header
[taylor/freespace2.git] / src / ac / convert.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/AC/convert.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * C module for the conversion of standard animation files to our own ANIM format.
16  * This is where all the real code for this application is located really.
17  *
18  * $Log$
19  * Revision 1.2  2002/06/09 04:41:15  relnev
20  * added copyright header
21  *
22  * Revision 1.1.1.1  2002/05/03 03:28:11  root
23  * Initial import.
24  *
25  * 
26  * 2     10/23/98 6:03p Dave
27  * 
28  * 1     10/23/98 5:34p Dave
29  * 
30  * 3     10/22/98 6:14p Dave
31  * Optimized some #includes in Anim folder. Put in the beginnings of
32  * parse/localization support for externalized strings and tstrings.tbl
33  * 
34  * 2     10/07/98 10:52a Dave
35  * Initial checkin.
36  * 
37  * 1     10/07/98 10:48a Dave
38  * 
39  * 12    6/23/98 2:52p Hoffoss
40  * Changed code so AC compiles once again.
41  * 
42  * 11    7/20/97 6:57p Lawrance
43  * supporting new RLE format
44  * 
45  * 10    6/25/97 11:57a Lawrance
46  * forgot linefeed
47  * 
48  * 9     6/25/97 11:49a Lawrance
49  * don't assert if PCX read fails, print warning
50  * 
51  * 8     5/21/97 2:26p Lawrance
52  * bug fix: not using correct header size
53  * 
54  * 7     5/21/97 11:06a Lawrance
55  * enabling a user-defined transparent value
56  * 
57  * 6     5/19/97 3:21p Lawrance
58  * add fps parm, version num to anim header
59  * 
60  * 5     2/25/97 5:18p Lawrance
61  * force_key_frame now numbering from 1 for PCXs as well as AVIs
62  * 
63  * 4     2/20/97 1:47p Lawrance
64  * adding dots when making frames
65  * 
66  * 3     2/20/97 10:30a Hoffoss
67  * Added in support for forcing a key frame.
68  * 
69  * 2     2/19/97 9:26p Lawrance
70  * added force_key_frame global
71  * 
72  * 1     2/19/97 7:14p Lawrance
73  * 
74  * 14    2/19/97 4:21p Lawrance
75  * took out unnecessary #include BmpMan.h
76  * 
77  * 13    2/19/97 3:59p Lawrance
78  * using pcxutils to load bitmaps, not bmpman
79  * 
80  * 12    2/17/97 2:59p Lawrance
81  * integrating into game
82  * 
83  * 11    2/17/97 3:02p Hoffoss
84  * Added headers to files, and implemented key frame dialog stuff.
85  * 
86  * 10    2/14/97 10:38p Lawrance
87  * fixing bugs
88  * 
89  * 9     2/14/97 9:47p Hoffoss
90  * fixed bugs.
91  * 
92  * 8     2/14/97 8:44p Lawrance
93  * fixed bug with saving header
94  * 
95  * 7     2/14/97 8:04p Hoffoss
96  * Fixed bug.
97  * 
98  * 6     2/14/97 7:44p Lawrance
99  * fixed some bugs in loading an AVI
100  * 
101  * 5     2/14/97 7:33p Lawrance
102  * added convert_avi_to_anim() function
103  * 
104  * 4     2/14/97 5:38p Hoffoss
105  * Changes to get AnimCoverter project to compile and link.
106  * 
107  * 3     2/14/97 3:28p Hoffoss
108  * Wrote functions to save an anim file.
109  * 
110  * 2     2/13/97 5:55p Lawrance
111  * reading AVI / decompressing AVI functions in
112  *
113  * $NoKeywords: $
114  */
115
116 #pragma warning(disable: 4201)
117
118 #include <wtypes.h>
119 #include <vfw.h>                /* Video For Windows header file */
120 #include "pstypes.h"
121 #include "convert.h"
122 #include "pcxutils.h"
123 #include "animplay.h"
124 #include "packunpack.h"
125
126 #define AVI_STREAM_F_USED       ( 1 << 0 )
127
128 typedef struct AVI_STREAM_TYPE {
129         PAVISTREAM      pstream;
130         PAVIFILE                pfile; 
131         int                     num_frames;
132         int                     current_frame;
133         int                     w,h,bpp;
134         int                     min_compressed_buffer_size;
135         ubyte                   palette[768];
136         char                    filename[255];
137         //ubyte                 pal_translation[256];           // palette translation look-up table
138         int             flags;
139 }       AVI_STREAM_TYPE;
140
141 // Internal function prototypes
142 int     AVI_stream_open(char* filename);
143 void    AVI_stream_close();
144 int     AVI_stream_get_frame(ubyte* buffer, int frame_number);
145 void    AVI_decompress_RLE8(ubyte* src, ubyte* dest, int w, int h);
146
147 // Global to file
148 static          AVI_STREAM_TYPE AVI_stream;     
149 static int      AVI_stream_inited = 0;
150
151 // Globals 
152 char    *anim_save_filename;
153 ubyte *cur_frame, *last_frame;
154 ubyte   anim_buffer[ANIM_BUFFER_MAX];
155 int     key_frame_rate;
156 int     force_key_frame;
157 int     total_key_frames;
158 int     anim_offset;
159 int     cur_frame_num;
160 int     Default_fps;
161 int     Use_custom_xparent_color;
162 rgb_triple      Xparent_color;
163
164 int     Compression_type;               // what kind of RLE compression is going to be used 
165 int     Key_frame_compression, Regular_frame_compression;
166
167 key_frame *first_frame;
168 FILE *anim_fp = NULL;
169 anim Anim;
170
171 // AVI_stream_init() is called only to clear USED flag of the AVI_stream structure
172 // and reset the current frame.
173 //
174 //      This does not need to be called explicity, since it will be called by AVI_stream_open()
175 // anyways.
176 //
177 void AVI_stream_init()
178 {
179         AVI_stream.flags &= ~AVI_STREAM_F_USED;
180         AVI_stream.current_frame = 0;
181         AVI_stream_inited = 1;
182
183         AVIFileInit();                                          // opens AVIFile library 
184 }
185
186 // AVI_stream_open() will open the AVI file and prepare it for reading, but will not 
187 // store any of the frame data. 
188 //
189 //      returns:   0 ==> success
190 //           !0 ==> could not open the AVI stream
191 //
192 // The filename is expected to be an absolute pathname (or file in the current working directory)
193 //
194 int AVI_stream_open(char* filename)
195 {
196         if ( !AVI_stream_inited )
197                 AVI_stream_init();
198
199         int                             hr; 
200         PAVIFILE                        pfile; 
201         PAVISTREAM              pstream;
202         AVISTREAMINFO   avi_stream_info;
203
204         Assert( !(AVI_stream.flags & AVI_STREAM_F_USED) );
205
206         // Open the AVI file
207         hr = AVIFileOpen(&pfile, filename, OF_SHARE_DENY_WRITE, 0); 
208         if (hr != 0){ 
209 //              nprintf(("Warning", "AVI ==> Unable to open %s", filename)); 
210                 return -1; 
211         } 
212  
213         strcpy(AVI_stream.filename, filename);
214
215         // Get a handle to the video stream within the AVI file 
216         hr = AVIFileGetStream(pfile, &pstream, streamtypeVIDEO, 0); 
217         if (hr != 0){ 
218                 //nprintf(("Warning", "AVI ==> Unable to open video stream in %s", filename)); 
219                 return -1; 
220         } 
221
222         // Store the pointer to stream, since we'll need it later to read from disk
223         AVI_stream.pstream = pstream;
224         AVI_stream.pfile = pfile;
225
226         // Get information on the stream
227         hr = AVIStreamInfo( pstream, &avi_stream_info, sizeof(AVISTREAMINFO) );
228         if (hr != 0){ 
229                 //nprintf(("Warning", "AVI ==> Unable to retreive stream info in %s", filename)); 
230                 return -1; 
231         } 
232
233
234         int buffer_size;
235         
236         int start_sample = AVIStreamStart(pstream);
237         Assert( start_sample == 0 );
238
239         int end_sample = AVIStreamEnd(pstream);
240         Assert( end_sample >= start_sample );
241
242         // store the number of frames in the AVI_info[] structure
243         AVI_stream.num_frames = end_sample;             // start sample must be 0
244         Assert(AVI_stream.num_frames == AVIStreamLength(pstream) );
245
246
247         // Get information on the stream
248         hr = AVIStreamInfo( pstream, &avi_stream_info, sizeof(AVISTREAMINFO) );
249         if (hr != 0){ 
250                 //nprintf(("Warning", "AVI ==> Unable to retreive stream info in %s", filename)); 
251                 return -1; 
252         } 
253
254         buffer_size = avi_stream_info.dwSuggestedBufferSize;
255         Assert( buffer_size > 0 );
256         AVI_stream.min_compressed_buffer_size = buffer_size;
257
258         // determine the format of the AVI image data
259         ubyte* format_buffer;
260         long format_buffer_size;
261         BITMAPINFO* bitmap_info;
262
263         hr = AVIStreamFormatSize(pstream, 0, &format_buffer_size);
264         Assert( format_buffer_size > 0 );
265
266         format_buffer = (ubyte*) malloc(format_buffer_size);
267         Assert(format_buffer != NULL);  // format_buffer is free'ed when AVI is free'ed, since memory is used by b_info member in AVI_info[] structure
268
269         hr = AVIStreamReadFormat(pstream, 0, format_buffer, &format_buffer_size);
270         bitmap_info = (BITMAPINFO*)format_buffer;
271
272
273         switch ( bitmap_info->bmiHeader.biCompression ) {
274                 case BI_RLE8:
275                         break;
276
277                 default:
278                         Assert(0);
279                         break;
280         }
281
282         AVI_stream.w = bitmap_info->bmiHeader.biWidth;
283         AVI_stream.h = bitmap_info->bmiHeader.biHeight;
284         AVI_stream.bpp = bitmap_info->bmiHeader.biBitCount;
285                 
286         // create the palette translation look-up table
287         //
288         // Transparency:  If the palette color is full green, then treat as transparent
289         //                                              
290         RGBQUAD* pal;
291         pal = (RGBQUAD*)(bitmap_info->bmiColors);
292
293         // Store the palette in the AVI stream structure
294         for ( int i = 0; i < 256; i++ ) {
295                 AVI_stream.palette[i*3]   = pal[i].rgbRed;
296                 AVI_stream.palette[i*3+1] = pal[i].rgbGreen;
297                 AVI_stream.palette[i*3+2] = pal[i].rgbBlue;
298         }       
299
300
301         //      memcpy(AVI_stream.palette, pal, 256*3);
302         
303 /*
304         int transparent_found = 0;
305         for ( i = 0; i < 256; i++ ) {
306
307                 //nprintf(("AVI", "AVI ==> R: %d  G: %d  B: %d\n", pal[i].rgbRed, pal[i].rgbGreen, pal[i].rgbBlue));
308                 if ( pal[i].rgbRed < 5 && pal[i].rgbGreen > 250 && pal[i].rgbBlue < 5 ) {
309                         avi_stream->pal_translation[i]  = TRANSPARENT_INDEX;
310                         break;  // found transparent, continue in j for loop, since don't need check any more
311                 }
312                 else
313                         avi_stream->pal_translation[i] = palette_find(  pal[i].rgbRed, pal[i].rgbGreen, pal[i].rgbBlue ); 
314         }       
315
316         for ( j = i+1; j < 256; j++ ) {
317                 avi_stream->pal_translation[j] = palette_find(  pal[j].rgbRed, pal[j].rgbGreen, pal[j].rgbBlue ); 
318         }
319 */
320
321         free(format_buffer);
322
323         // set the flag to used, so to make sure we only process one AVI stream at a time
324         AVI_stream.flags |= AVI_STREAM_F_USED;  
325
326
327         return 0;
328 }
329
330
331 // AVI_stream_close() should be called when you are finished reading all the frames of an AVI
332 //
333 void AVI_stream_close()
334 {       
335 //      Assert( AVI_stream.flags & AVI_STREAM_F_USED);
336
337    AVIStreamRelease(AVI_stream.pstream);                                // closes the video stream
338         AVIFileRelease(AVI_stream.pfile);                                       // closes the file 
339         AVI_stream.flags &= ~AVI_STREAM_F_USED;                 // clear the used flag
340
341         AVIFileExit();          // releases AVIFile library 
342         AVI_stream_inited = 0;
343
344 }
345
346
347
348
349 // AVI_stream_get_next_frame() will take the next RLE'd AVI frame and return the
350 // uncompressed data in the buffer pointer supplied as a parameter.  The caller is
351 // responsible for allocating the memory before-hand (the memory required is easily
352 // calculated by looking at the w and h members in AVI_stream).
353 // 
354 // returns:    0 ==> success
355 //            !0 ==> error
356 //
357 int AVI_stream_get_frame(ubyte* buffer, int frame_number)
358 {
359         if ( frame_number > AVI_stream.num_frames ) {
360                 buffer = NULL;
361                 return -1;
362         }
363
364         Assert( (frame_number - 1) >= 0 );
365
366         ubyte* compressed_frame = (ubyte*)malloc(AVI_stream.min_compressed_buffer_size);
367         Assert( compressed_frame != NULL );
368
369         long num_bytes_used;
370         long num_samples_used;
371
372         AVIStreamRead( AVI_stream.pstream, frame_number-1, 1, compressed_frame, AVI_stream.min_compressed_buffer_size, &num_bytes_used, &num_samples_used);
373         Assert(num_samples_used == 1);
374                 
375         AVI_decompress_RLE8(compressed_frame, buffer, AVI_stream.w, AVI_stream.h);
376
377         free( compressed_frame );
378         return 0;
379
380 }
381
382
383
384 // -------------------------------------------------------------------------------------------------
385 // AVI_decompress_RLE8() will decompress the data pointed to by src, and store in dest.
386 //
387 //      NOTE:  1. memory for dest must be already allocated before calling function
388 //
389
390 void AVI_decompress_RLE8(ubyte* src, ubyte* dest, int w, int h)
391 {
392         int src_index = 0;
393         int dest_index = 0;
394         int i;
395
396         Assert( src != NULL);
397         Assert( dest != NULL);
398         Assert( w > 0 );
399         Assert( h > 0 );
400
401         ubyte count;
402         ubyte run;
403         ubyte control_code;
404
405         int size_src = w * h + 1;
406
407         int scan_line = h-1;    
408         int height_offset = scan_line * w;
409
410         while ( src_index < size_src ) {
411                 
412                 count = src[src_index];
413                 
414                 if ( count == 0 ) {     // control code follows
415                         src_index++;
416                         control_code = src[src_index];
417                         if ( control_code == 1 ) {
418                                 src_index++;
419 //                              nprintf(("AVI","AVI ==> Reached end of compressed image\n"));
420                                 break;
421                         }
422                         else if ( control_code == 0 ) {
423                                 src_index++;
424                                 scan_line--;
425                                 height_offset = scan_line * w;  // only need to calc once per scanline
426                                 dest_index = 0;
427                                 //nprintf(("AVI","AVI ==> Reached end of line in compressed image\n"));
428                         }
429                         else if ( control_code == 2 ) {
430                                 Assert(0);
431                         }
432                         else {
433                                 // in absolute mode
434                                 src_index++;
435                                 for ( i = 0; i < control_code; i++ ) {
436
437                                         dest[height_offset + dest_index] = src[src_index];
438                                         dest_index++;
439                                         src_index++;
440                                 }
441                                 // run must end on a word boundry
442                                 if ( control_code & 1 )
443                                         src_index++;
444                         }
445                 }
446                 else {
447                         src_index++;
448                         run = src[src_index];
449                         // nprintf(("AVI","AVI ==> Got %d pixel run of %d\n", src[src_index], count));
450                         memset( &dest[height_offset + dest_index], run, count );
451                         dest_index += count;
452                         src_index++;
453                 }
454         }       // end while
455
456 }
457
458 int save_anim_header()
459 {
460         int i, new_format_id = 0;
461
462         Assert(anim_fp);
463         fclose(anim_fp);
464         anim_fp = fopen(anim_save_filename, "r+b");
465
466         if (!fwrite(&new_format_id, 2, 1, anim_fp))
467                 return -1;
468         if (!fwrite(&Anim.version, 2, 1, anim_fp))
469                 return -1;
470         if (!fwrite(&Anim.fps, 2, 1, anim_fp))
471                 return -1;
472         if (!fwrite(&Anim.xparent_r, 1, 1, anim_fp))
473                 return -1;
474         if (!fwrite(&Anim.xparent_g, 1, 1, anim_fp))
475                 return -1;
476         if (!fwrite(&Anim.xparent_b, 1, 1, anim_fp))
477                 return -1;
478         if (!fwrite(&Anim.width, 2, 1, anim_fp))
479                 return -1;
480         if (!fwrite(&Anim.height, 2, 1, anim_fp))
481                 return -1;
482         if (!fwrite(&Anim.total_frames, 2, 1, anim_fp))
483                 return -1;
484         if (!fwrite(&Anim.packer_code, 1, 1, anim_fp))
485                 return -1;
486         if (fwrite(&Anim.palette, 3, 256, anim_fp) != 256)
487                 return -1;
488         if (!fwrite(&total_key_frames, 2, 1, anim_fp))
489                 return -1;
490
491         for (i=0; i<Anim.num_keys; i++) {
492                 if (!fwrite(&Anim.keys[i].frame_num, 2, 1, anim_fp))
493                         return -1;
494
495                 if (!fwrite(&Anim.keys[i].offset, 4, 1, anim_fp))
496                         return -1;
497         }
498
499         if (!fwrite(&anim_offset, 4, 1, anim_fp))
500                 return -1;
501
502         return 0;
503 }
504
505 // This function allocates a linked list of key frame headers.
506 // It is responsible for determining which frames in an anim
507 // should be key frames.
508 int allocate_key_frames(int total_frames)
509 {
510         int count = 0, frame = 1, rate = key_frame_rate, last_frame;
511
512         if (!rate)
513                 rate = total_frames;
514
515         while (frame <= total_frames) {
516                 count++;
517                 frame += rate;
518         }
519
520         if (force_key_frame >= 0)
521                 count++;
522
523         if (count)
524                 Anim.keys = (key_frame *) malloc(count * sizeof(key_frame));
525
526         count = 0;
527         frame = last_frame = 1;
528         while (frame <= total_frames) {
529                 if ((force_key_frame > last_frame) && (force_key_frame < frame))
530                         Anim.keys[count++].frame_num = force_key_frame;
531
532                 Anim.keys[count++].frame_num = frame;
533                 frame += rate;
534         }
535
536         if (force_key_frame > last_frame)
537                 Anim.keys[count++].frame_num = force_key_frame;
538
539         Anim.num_keys = count;
540         return count;  // number of key frames
541 }
542
543 int anim_save_init(char *file, int width, int height, int frames)
544 {
545         Assert(file);
546         anim_save_filename = file;
547         anim_fp = fopen(file, "wb");
548         if (!anim_fp)
549                 return -1;
550
551         Anim.version = ANIM_VERSION;
552         Anim.fps = Default_fps;
553         Anim.width = width;
554         Anim.height = height;
555         Anim.packer_code = PACKER_CODE;
556         Anim.xparent_r = Xparent_color.r;
557         Anim.xparent_g = Xparent_color.g;
558         Anim.xparent_b = Xparent_color.b;
559         Anim.total_frames = frames;
560         anim_offset = 0;
561         cur_frame_num = 0;
562         total_key_frames = allocate_key_frames(frames);
563         fseek(anim_fp, ANIM_HEADER_SIZE + total_key_frames * 6, SEEK_SET);
564
565         switch ( Compression_type ) {
566                 case CUSTOM_DELTA_RLE:
567                         Key_frame_compression = PACKING_METHOD_RLE_KEY;
568                         Regular_frame_compression = PACKING_METHOD_RLE;
569                         break;
570
571                 case STD_DELTA_RLE:
572                         Key_frame_compression = PACKING_METHOD_STD_RLE_KEY;
573                         Regular_frame_compression = PACKING_METHOD_STD_RLE;
574                         break;
575
576                 default:
577                         Int3();
578                         return -1;
579                         break;
580         } // end switch
581
582         return 0;
583 }
584
585 int anim_save_frame()
586 {
587         ubyte *temp;
588         int i, size;
589         key_frame *keyp = NULL;
590
591         Assert(anim_fp);
592         cur_frame_num++;
593         Assert(cur_frame_num <= Anim.total_frames);
594
595         for (i=0; i<Anim.num_keys; i++)
596                 if (Anim.keys[i].frame_num == cur_frame_num) {
597                         keyp = &Anim.keys[i];
598                         break;
599                 }
600
601         if (keyp) {
602                 fprintf(stdout, "*");
603                 fflush(stdout);
604                 keyp->offset = anim_offset;
605                 size = pack_key_frame(cur_frame, anim_buffer, Anim.width * Anim.height, ANIM_BUFFER_MAX, Key_frame_compression);
606
607         } else {
608                 fprintf(stdout, ".");
609                 fflush(stdout);
610                 size = pack_frame(cur_frame, last_frame, anim_buffer, Anim.width * Anim.height, ANIM_BUFFER_MAX, Regular_frame_compression);
611         }
612
613         if (size < 0)
614                 return -1;
615
616         if ((int) fwrite(anim_buffer, 1, size, anim_fp) != size)
617                 return -1;
618
619         anim_offset += size;
620         temp = cur_frame;
621         cur_frame = last_frame;
622         last_frame = temp;
623         return 0;
624 }
625
626
627 // converts an avi file to an anim file
628 //
629 // returns:   0 ==> success
630 //                !0 ==> failure
631 //
632 int convert_avi_to_anim(char* filename)
633 {
634         char ani_filename[255];
635         int ret = 1;
636         int rc, i, xparent_pal_index;
637         int avi_stream_opened = 0;
638
639         rc = AVI_stream_open(filename);
640         if ( rc != 0 ) {
641                 // could not open the AVI stream
642                 goto Finish;
643         }
644         avi_stream_opened = 1;
645         
646         Assert(AVI_stream.bpp == 8);
647         cur_frame = (ubyte*) malloc(AVI_stream.w * AVI_stream.h);
648         last_frame = (ubyte*) malloc(AVI_stream.w * AVI_stream.h);
649         Assert(cur_frame && last_frame);
650
651         strcpy(ani_filename, AVI_stream.filename);
652         strcpy(ani_filename + strlen(ani_filename) - 3, "ani");
653
654         memcpy(Anim.palette, AVI_stream.palette, 768);
655
656         if (Use_custom_xparent_color) {
657                 // Need to look at pixel in top-left 
658                 rc = AVI_stream_get_frame(cur_frame, 1);
659                 xparent_pal_index = cur_frame[0];
660                 Xparent_color.r = Anim.palette[xparent_pal_index * 3];
661                 Xparent_color.g = Anim.palette[xparent_pal_index * 3 + 1];
662                 Xparent_color.b = Anim.palette[xparent_pal_index * 3 + 2];
663
664         } else {
665                 Xparent_color.r = 0;
666                 Xparent_color.g = 255;
667                 Xparent_color.b = 0;
668         }
669         
670         rc = anim_save_init(ani_filename, AVI_stream.w, AVI_stream.h, AVI_stream.num_frames);
671         if (rc == -1)
672                 goto Finish;
673
674         for ( i=1; i <= AVI_stream.num_frames; i++ ) {
675                 // get uncompressed frame from the AVI
676                 rc = AVI_stream_get_frame(cur_frame, i);
677                 if ( rc != 0 )
678                         goto Finish;
679
680                 // pass to the anim compression
681                 rc = anim_save_frame();
682                 if ( rc != 0 )
683                         goto Finish;
684         }
685
686         rc = save_anim_header();
687         if ( rc != 0 )
688                 goto Finish;
689
690         ret = 0;
691
692         Finish:
693         // done with the AVI.. close the stream
694         if ( avi_stream_opened )
695                 AVI_stream_close();
696
697         if ( anim_fp )
698                 fclose(anim_fp);
699
700         free(cur_frame);
701         free(last_frame);
702         fprintf(stdout,"\n");
703         fflush(stdout);
704         return ret;
705 }
706
707 int convert_frames_to_anim(char *filename)
708 {
709         int first_frame, frame, pos, width, height, xparent_pal_index, r = -1;
710         char ani_filename[255], name[255], temp[8];     
711         int rc;
712         FILE *fp;
713
714         Assert(strlen(filename) < 254);
715         strcpy(name, filename);
716         strcpy(ani_filename, filename);
717         strcpy(ani_filename + strlen(ani_filename) - 8, ".ani");
718         pos = strlen(name) - 8;
719         frame = first_frame = atoi(&name[pos]);
720         force_key_frame -= frame;
721
722         // first file
723         fp = fopen(name, "rb");
724         if(fp != NULL){
725                 do {
726                         fclose(fp);
727                         frame++;
728                         sprintf(temp, "%04.4d", frame);
729                         strncpy(&name[pos], temp, 4);   
730
731                         // next file
732                         fp = fopen(name, "rb");
733                 } while(fp != NULL);    
734         }
735
736         rc = pcx_read_header(filename, &width, &height, NULL);
737         if (rc != PCX_ERROR_NONE) {
738                 fprintf(stdout, "An error reading the PCX file %s.  It may not exist.\n", filename);
739                 return -1;
740         }
741
742         cur_frame = (ubyte *) malloc(width * height);
743         last_frame = (ubyte *) malloc(width * height);
744
745         rc = pcx_read_bitmap_8bpp(filename, cur_frame, Anim.palette);
746         if (rc != PCX_ERROR_NONE) {
747                 fprintf(stdout, "An error reading the PCX file %s.  It may not exist.\n", filename);
748                 return -1;
749         }
750
751         if (Use_custom_xparent_color) {
752                 // Need to look at pixel in top-left 
753                 xparent_pal_index = ((ubyte *) cur_frame)[0];
754                 Xparent_color.r = Anim.palette[xparent_pal_index * 3];
755                 Xparent_color.g = Anim.palette[xparent_pal_index * 3 + 1];
756                 Xparent_color.b = Anim.palette[xparent_pal_index * 3 + 2];
757
758         } else {
759                 Xparent_color.r = 0;
760                 Xparent_color.g = 255;
761                 Xparent_color.b = 0;
762         }
763
764         if (anim_save_init(ani_filename, width, height, frame - first_frame))
765                 goto done;
766
767         while (first_frame < frame) {
768                 sprintf(temp, "%04.4d", first_frame);
769                 strncpy(&name[pos], temp, 4);
770                 rc = pcx_read_bitmap_8bpp(name, cur_frame, Anim.palette);
771                 if (rc != PCX_ERROR_NONE)
772                         goto done;
773
774                 if (anim_save_frame())
775                         goto done;
776
777                 first_frame++;
778         }
779
780         if (save_anim_header())
781                 goto done;
782
783         r = 0;
784
785 done:
786         fclose(anim_fp);
787         free(cur_frame);
788         free(last_frame);
789         fprintf(stdout, "\n");
790         fflush(stdout);
791         return r;
792 }
793