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