]> icculus.org git repositories - taylor/freespace2.git/blob - src/ac/convert.cpp
initial pass at getting ac up and working
[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
117 #include "pstypes.h"
118 #include "convert.h"
119 #include "pcxutils.h"
120 #include "animplay.h"
121 #include "packunpack.h"
122
123 #define AVI_STREAM_F_USED       ( 1 << 0 )
124
125 typedef struct _frame_index {
126         unsigned int offset;
127         unsigned int size;
128 } FRAMEINDEX;
129
130 typedef struct AVI_STREAM_TYPE {
131         FILE            *pfile;
132         FRAMEINDEX      *frame_index;
133         unsigned int    movi_offset;
134         int                     num_frames;
135         int                     current_frame;
136         int                     w,h,bpp;
137         int                     min_compressed_buffer_size;
138         ubyte                   palette[768];
139         char                    filename[255];
140         //ubyte                 pal_translation[256];           // palette translation look-up table
141         int             flags;
142 }       AVI_STREAM_TYPE;
143
144 #define AVIF_HASINDEX 0x00000010
145
146 typedef struct _avimainheader {
147         unsigned int fcc;
148         unsigned int cb;
149         unsigned int dwMicroSecPerFrame;
150         unsigned int dwMaxBytesPerSec;
151         unsigned int dwPaddingGranularity;
152         unsigned int dwFlags;
153         unsigned int dwTotalFrames;
154         unsigned int dwInitialFrames;
155         unsigned int dwStreams;
156         unsigned int dwSuggestedBufferSize;
157         unsigned int dwWidth;
158         unsigned int dwHeight;
159         unsigned int dwReserved[4];
160 } AVIMAINHEADER;
161
162 #define AVISF_VIDEO_PALCHANGES 0x00010000
163
164 typedef struct _avistreamheader {
165         unsigned int fcc;
166         unsigned int cb;
167         unsigned int fccType;
168         unsigned int fccHandler;
169         unsigned int dwFlags;
170         unsigned short wPriority;
171         unsigned short wLanguage;
172         unsigned int dwInitialFrames;
173         unsigned int dwScale;
174         unsigned int dwRate;
175         unsigned int dwStart;
176         unsigned int dwLength;
177         unsigned int dwSuggestedBufferSize;
178         unsigned int dwQuality;
179         unsigned int dwSampleSize;
180         struct {
181                 short left;
182                 short top;
183                 short right;
184                 short bottom;
185         } rcFrame;
186 } AVISTREAMHEADER;
187
188 #define BI_RLE8 0x00000001
189
190 typedef struct _bitmapinfo {
191         struct {
192                 unsigned int biSize;
193                 int biWidth;
194                 int biHeight;
195                 unsigned short biPlanes;
196                 unsigned short biBitCount;
197                 unsigned int biCompression;
198                 unsigned int biSizeImage;
199                 int biXPelsPerMeter;
200                 int biYPelsPerMeter;
201                 unsigned int biClrUsed;
202                 unsigned int biClrImportant;
203         } bmiHeader;
204
205         struct {
206                 ubyte b;
207                 ubyte g;
208                 ubyte r;
209                 ubyte p;
210         } bmiColors[256];
211 } BITMAPINFO;
212
213
214 // Internal function prototypes
215 int     AVI_stream_open(char* filename);
216 void    AVI_stream_close();
217 int     AVI_stream_get_frame(ubyte* buffer, int frame_number);
218 void    AVI_decompress_RLE8(ubyte* src, ubyte* dest, unsigned int srcSize);
219
220 // Global to file
221 static          AVI_STREAM_TYPE AVI_stream;     
222 static int      AVI_stream_inited = 0;
223
224 // Globals 
225 char    *anim_save_filename;
226 ubyte *cur_frame, *last_frame;
227 ubyte   anim_buffer[ANIM_BUFFER_MAX];
228 int     key_frame_rate;
229 int     force_key_frame;
230 int     total_key_frames;
231 int     anim_offset;
232 int     cur_frame_num;
233 int     Default_fps;
234 int     Use_custom_xparent_color;
235 rgb_triple      Xparent_color;
236
237 int     Compression_type;               // what kind of RLE compression is going to be used 
238 int     Key_frame_compression, Regular_frame_compression;
239
240 key_frame *first_frame;
241 FILE *anim_fp = NULL;
242 anim Anim;
243
244 // AVI_stream_init() is called only to clear USED flag of the AVI_stream structure
245 // and reset the current frame.
246 //
247 //      This does not need to be called explicity, since it will be called by AVI_stream_open()
248 // anyways.
249 //
250 void AVI_stream_init()
251 {
252         AVI_stream.flags &= ~AVI_STREAM_F_USED;
253         AVI_stream.current_frame = 0;
254         AVI_stream_inited = 1;
255
256         AVI_stream.pfile = NULL;
257         AVI_stream.frame_index = NULL;
258 }
259
260 // AVI_stream_open() will open the AVI file and prepare it for reading, but will not 
261 // store any of the frame data. 
262 //
263 //      returns:   0 ==> success
264 //           !0 ==> could not open the AVI stream
265 //
266 // The filename is expected to be an absolute pathname (or file in the current working directory)
267 //
268 int AVI_stream_open(char* filename)
269 {
270         if ( !AVI_stream_inited )
271                 AVI_stream_init();
272
273         FILE *pfile = NULL;
274         int id = 0;
275         unsigned int tag = 0, size = 0, next_chunk;
276         unsigned int s_tag, tmp;
277         AVIMAINHEADER avi_header;
278         AVISTREAMHEADER stream_header;
279         BITMAPINFO bitmap_header;
280         unsigned int file_size = 0;
281
282         SDL_assert( !(AVI_stream.flags & AVI_STREAM_F_USED) );
283
284         pfile = fopen(filename, "rb");
285
286         if (pfile == NULL) {
287                 printf("AVI ==> Unable to open %s", filename);
288                 return -1;
289         }
290
291         // get file size
292         fseek(pfile, 0, SEEK_END);
293         file_size = ftell(pfile);
294         fseek(pfile, 0, SEEK_SET);
295
296         // check for valid file type
297         fread(&id, 1, 4, pfile);
298
299         // 'RIFF'
300         if (id != 0x46464952) {
301                 printf("Not a RIFF file '%s'\n", filename);
302                 fclose(pfile);
303                 return -1;
304         }
305
306         // skip RIFF size
307         fread(&id, 1, 4, pfile);
308
309         // check for valid RIFF type
310         fread(&id, 1, 4, pfile);
311
312         // 'AVI '
313         if (id != 0x20495641) {
314                 printf("Not an AVI file '%s'\n", filename);
315                 fclose(pfile);
316                 return -1;
317         }
318
319         // used for main 'LIST' chunks
320         unsigned int offset_tmp = 0;
321
322         // parse WAVE tags
323         while ( ftell(pfile) < file_size ) {
324                 fread(&tag, 1, 4, pfile);
325                 fread(&size, 1, 4, pfile);
326
327                 next_chunk = ftell(pfile) + size;
328
329                 switch (tag) {
330                         // 'LIST'
331                         case 0x5453494c: {
332                                 // sub tag
333                                 fread(&s_tag, 1, 4, pfile);
334
335                                 switch (s_tag) {
336                                         // 'hdrl'
337                                         case 0x6c726468: {
338                                                 fread(&avi_header.fcc, 1, sizeof(int), pfile);
339                                                 fread(&avi_header.cb, 1, sizeof(int), pfile);
340                                                 fread(&avi_header.dwMicroSecPerFrame, 1, sizeof(int), pfile);
341                                                 fread(&avi_header.dwMaxBytesPerSec, 1, sizeof(int), pfile);
342                                                 fread(&avi_header.dwPaddingGranularity, 1, sizeof(int), pfile);
343                                                 fread(&avi_header.dwFlags, 1, sizeof(int), pfile);
344                                                 fread(&avi_header.dwTotalFrames, 1, sizeof(int), pfile);
345                                                 fread(&avi_header.dwInitialFrames, 1, sizeof(int), pfile);
346                                                 fread(&avi_header.dwStreams, 1, sizeof(int), pfile);
347                                                 fread(&avi_header.dwSuggestedBufferSize, 1, sizeof(int), pfile);
348                                                 fread(&avi_header.dwWidth, 1, sizeof(int), pfile);
349                                                 fread(&avi_header.dwHeight, 1, sizeof(int), pfile);
350                                                 fread(&avi_header.dwReserved, 1, sizeof(avi_header.dwReserved), pfile);
351
352                                                 // check for 'avih'
353                                                 SDL_assert(avi_header.fcc == 0x68697661);
354
355                                                 // we're stupid, can only handle a single stream
356                                         //      if (avi_header.dwStreams != 1) {
357                                         //              printf("AVI has more than one stream '%s'\n", filename);
358                                         //              fclose(pfile);
359                                         //              return -1;
360                                         //      }
361
362                                                 // update next_chunk offset for sub-chunk
363                                                 offset_tmp = next_chunk;
364                                                 next_chunk = ftell(pfile);
365
366                                                 break;
367                                         }
368
369                                         // 'strl' - subchunk of 'hdrl'
370                                         case 0x6c727473: {
371                                                 fread(&stream_header.fcc, 1, sizeof(int), pfile);
372                                                 fread(&stream_header.cb, 1, sizeof(int), pfile);
373                                                 fread(&stream_header.fccType, 1, sizeof(int), pfile);
374                                                 fread(&stream_header.fccHandler, 1, sizeof(int), pfile);
375                                                 fread(&stream_header.dwFlags, 1, sizeof(int), pfile);
376                                                 fread(&stream_header.wPriority, 1, sizeof(short), pfile);
377                                                 fread(&stream_header.wLanguage, 1, sizeof(short), pfile);
378                                                 fread(&stream_header.dwInitialFrames, 1, sizeof(int), pfile);
379                                                 fread(&stream_header.dwScale, 1, sizeof(int), pfile);
380                                                 fread(&stream_header.dwRate, 1, sizeof(int), pfile);
381                                                 fread(&stream_header.dwStart, 1, sizeof(int), pfile);
382                                                 fread(&stream_header.dwLength, 1, sizeof(int), pfile);
383                                                 fread(&stream_header.dwSuggestedBufferSize, 1, sizeof(int), pfile);
384                                                 fread(&stream_header.dwQuality, 1, sizeof(int), pfile);
385                                                 fread(&stream_header.dwSampleSize, 1, sizeof(int), pfile);
386                                                 fread(&stream_header.rcFrame.left, 1, sizeof(short), pfile);
387                                                 fread(&stream_header.rcFrame.top, 1, sizeof(short), pfile);
388                                                 fread(&stream_header.rcFrame.right, 1, sizeof(short), pfile);
389                                                 fread(&stream_header.rcFrame.bottom, 1, sizeof(short), pfile);
390
391                                                 // check for 'strh'
392                                                 SDL_assert(stream_header.fcc == 0x68727473);
393
394                                                 // check stream type, can only handle 'vids'
395                                                 if (stream_header.fccType != 0x73646976) {
396                                                         printf("AVI => first stream must be video '%s'\n", filename);
397                                                         fclose(pfile);
398                                                         return -1;
399                                                 }
400                                                 SDL_assert(stream_header.fccType == 0x73646976);
401
402                                                 // only handle 'MRLE' encoding (or 'mrle' if wrong)
403                                                 if ( (stream_header.fccHandler != 0x454c524d) && (stream_header.fccHandler != 0x656c726d) ) {
404                                                         printf("AVI is not MRLE encoded '%s'\n", filename);
405                                                         fclose(pfile);
406                                                         return -1;
407                                                 }
408
409                                                 // no pal changes for you cowboy
410                                                 if (stream_header.dwFlags & AVISF_VIDEO_PALCHANGES) {
411                                                         printf("AVI cannot have palette changes '%s'\n", filename);
412                                                         fclose(pfile);
413                                                         return -1;
414                                                 }
415
416                                                 // next stream sub-chunk -------------------------------
417
418                                                 // check for 'strf'
419                                                 fread(&tmp, 1, 4, pfile);
420                                                 SDL_assert(tmp == 0x66727473);
421
422                                                 // size of 'strf'
423                                                 fread(&tmp, 1, 4, pfile);
424
425                                                 fread(&bitmap_header.bmiHeader.biSize, 1, sizeof(int), pfile);
426                                                 fread(&bitmap_header.bmiHeader.biWidth, 1, sizeof(int), pfile);
427                                                 fread(&bitmap_header.bmiHeader.biHeight, 1, sizeof(int), pfile);
428                                                 fread(&bitmap_header.bmiHeader.biPlanes, 1, sizeof(short), pfile);
429                                                 fread(&bitmap_header.bmiHeader.biBitCount, 1, sizeof(short), pfile);
430                                                 fread(&bitmap_header.bmiHeader.biCompression, 1, sizeof(int), pfile);
431                                                 fread(&bitmap_header.bmiHeader.biSizeImage, 1, sizeof(int), pfile);
432                                                 fread(&bitmap_header.bmiHeader.biXPelsPerMeter, 1, sizeof(int), pfile);
433                                                 fread(&bitmap_header.bmiHeader.biYPelsPerMeter, 1, sizeof(int), pfile);
434                                                 fread(&bitmap_header.bmiHeader.biClrUsed, 1, sizeof(int), pfile);
435                                                 fread(&bitmap_header.bmiHeader.biClrImportant, 1, sizeof(int), pfile);
436
437                                                 // verify bpp is 8 and compression is RLE8
438                                                 if ( (bitmap_header.bmiHeader.biBitCount != 8) || (bitmap_header.bmiHeader.biCompression != BI_RLE8) ) {
439                                                         printf("AVI has wrong bpp or compression '%s'\n", filename);
440                                                         fclose(pfile);
441                                                         return -1;
442                                                 }
443
444                                                 // palette
445                                                 for (int i = 0; i < 256; i++) {
446                                                         fread(&bitmap_header.bmiColors[i].b, 1, 1, pfile);
447                                                         fread(&bitmap_header.bmiColors[i].g, 1, 1, pfile);
448                                                         fread(&bitmap_header.bmiColors[i].r, 1, 1, pfile);
449                                                         fread(&bitmap_header.bmiColors[i].p, 1, 1, pfile);
450                                                 }
451
452                                                 // reset next_chunk back to main 'LIST'
453                                                 next_chunk = offset_tmp;
454
455                                                 break;
456                                         }
457
458                                         // 'movi'
459                                         case 0x69766f6d: {
460                                                 // need this for later
461                                                 AVI_stream.movi_offset = ftell(pfile);
462
463 #if 0
464                                                 // TODO: this crap is broken and unsafe!
465
466                                                 if ( !(avi_header.dwFlags & AVIF_HASINDEX) ) {
467                                         //              printf("AVI => Building index... (%d)", avi_header.dwTotalFrames);
468
469                                                         SDL_assert(AVI_stream.frame_index == NULL);
470
471                                                         AVI_stream.frame_index = (FRAMEINDEX*) malloc(sizeof(FRAMEINDEX) * avi_header.dwTotalFrames);
472                                                         SDL_assert(AVI_stream.frame_index != NULL);
473
474                                                         memset(AVI_stream.frame_index, 0, sizeof(FRAMEINDEX) * avi_header.dwTotalFrames);
475
476                                                         unsigned short b1, b2;
477                                                         unsigned int i = 0;
478
479                                                         while ( ftell(pfile) < next_chunk ) {
480                                                                 fread(&b1, 1, 2, pfile);
481                                                                 fread(&b2, 1, 2, pfile);
482
483                                                                 // check for compressed DIB on first stream '00dc'
484                                                                 if ( (b1 == 0x3030) && (b2 == 0x6364) ) {
485                                                                         SDL_assert(i < avi_header.dwTotalFrames);
486
487                                                                 //      printf("\rAVI => Building index... %d / %d", i+1, avi_header.dwTotalFrames);
488
489                                                                         AVI_stream.frame_index[i].offset = ftell(pfile) - AVI_stream.movi_offset;
490
491                                                                         if (i > 0) {
492                                                                                 AVI_stream.frame_index[i-1].size = AVI_stream.frame_index[i].offset - AVI_stream.frame_index[i-1].offset - 4;
493                                                                         } else if (i == avi_header.dwTotalFrames-1) {
494                                                                                 AVI_stream.frame_index[i].size = next_chunk - ftell(pfile);
495                                                                                 printf("HERERERERERERE\n");
496                                                                         }
497                                 //      printf("%d, %d, %d\n", ftell(pfile), AVI_stream.movi_offset, i);
498                                                                         i++;
499                                                                 }
500                                                         }
501
502                                                 //      printf("\rAVI => Building index... done              \n");
503                                                 }
504 #endif
505
506                                                 break;
507                                         }
508
509                                         default:
510                                                 printf("sub-something else: 0x%x\n", tag);
511                                                 break;
512                                 }
513
514                                 break;
515                         }
516
517                         // 'idx1'
518                         case 0x31786469: {
519                                 SDL_assert(AVI_stream.frame_index == NULL);
520
521                                 AVI_stream.frame_index = (FRAMEINDEX*) malloc(sizeof(FRAMEINDEX) * avi_header.dwTotalFrames);
522                                 SDL_assert(AVI_stream.frame_index != NULL);
523
524                                 memset(AVI_stream.frame_index, 0, sizeof(FRAMEINDEX) * avi_header.dwTotalFrames);
525
526                                 unsigned int c_id, c_flags, c_offset, c_size, i = 0;
527
528                                 while ( ftell(pfile) < next_chunk ) {
529                                         fread(&c_id, 1, sizeof(int), pfile);
530                                         fread(&c_flags, 1, sizeof(int), pfile);
531                                         fread(&c_offset, 1, sizeof(int), pfile);
532                                         fread(&c_size, 1, sizeof(int), pfile);
533
534                                         // only interested in stream 0, compressed data: '00dc'
535                                         if (c_id == 0x63643030) {
536                                                 SDL_assert(i < avi_header.dwTotalFrames);
537
538                                                 AVI_stream.frame_index[i].offset = c_offset;
539                                                 AVI_stream.frame_index[i].size = c_size;
540                                                 i++;
541                                         }
542                                 }
543
544                                 break;
545                         }
546
547                         // drop everything else
548                         default:
549                                 break;
550                 }
551
552                 fseek(pfile, next_chunk, SEEK_SET);
553         }
554
555         // make sure we have a frame index
556         if (AVI_stream.frame_index == NULL) {
557                 printf("No frame index found or created '%s'\n", filename);
558                 fclose(pfile);
559                 return -1;
560         }
561
562         strcpy(AVI_stream.filename, filename);
563         AVI_stream.pfile = pfile;
564
565         AVI_stream.min_compressed_buffer_size = max(avi_header.dwSuggestedBufferSize, stream_header.dwSuggestedBufferSize);
566         AVI_stream.min_compressed_buffer_size = max(AVI_stream.min_compressed_buffer_size, bitmap_header.bmiHeader.biWidth*bitmap_header.bmiHeader.biHeight);
567
568         AVI_stream.w = bitmap_header.bmiHeader.biWidth;
569         AVI_stream.h = bitmap_header.bmiHeader.biHeight;
570         AVI_stream.bpp = bitmap_header.bmiHeader.biBitCount;
571
572         // store the number of frames in the AVI_info[] structure
573         AVI_stream.num_frames = avi_header.dwTotalFrames;
574
575         // Store the palette in the AVI stream structure
576         for (int i = 0; i < 256; i++) {
577                 AVI_stream.palette[i*3]   = bitmap_header.bmiColors[i].r;
578                 AVI_stream.palette[i*3+1] = bitmap_header.bmiColors[i].g;
579                 AVI_stream.palette[i*3+2] = bitmap_header.bmiColors[i].b;
580         }
581
582         printf("filename: %s\n", filename);
583         printf("w: %d, h: %d, bpp: %d\n", AVI_stream.w, AVI_stream.h, AVI_stream.bpp);
584         printf("num frames: %d\n", AVI_stream.num_frames);
585
586         // set the flag to used, so to make sure we only process one AVI stream at a time
587         AVI_stream.flags |= AVI_STREAM_F_USED;  
588
589         return 0;
590 }
591
592
593 // AVI_stream_close() should be called when you are finished reading all the frames of an AVI
594 //
595 void AVI_stream_close()
596 {       
597 //      SDL_assert( AVI_stream.flags & AVI_STREAM_F_USED);
598
599         if (AVI_stream.pfile && fi) {
600                 fclose(AVI_stream.pfile);
601                 AVI_stream.pfile = NULL;
602         }
603
604         if (AVI_stream.frame_index) {
605                 free(AVI_stream.frame_index);
606                 AVI_stream.frame_index = NULL;
607         }
608
609         AVI_stream.flags &= ~AVI_STREAM_F_USED;                 // clear the used flag
610
611         AVI_stream_inited = 0;
612 }
613
614
615
616
617 // AVI_stream_get_next_frame() will take the next RLE'd AVI frame and return the
618 // uncompressed data in the buffer pointer supplied as a parameter.  The caller is
619 // responsible for allocating the memory before-hand (the memory required is easily
620 // calculated by looking at the w and h members in AVI_stream).
621 // 
622 // returns:    0 ==> success
623 //            !0 ==> error
624 //
625 int AVI_stream_get_frame(ubyte* buffer, int frame_number)
626 {
627         return -1;
628         if ( frame_number > AVI_stream.num_frames ) {
629                 buffer = NULL;
630                 return -1;
631         }
632
633         SDL_assert( (frame_number - 1) >= 0 );
634
635         ubyte* compressed_frame = (ubyte*)malloc(AVI_stream.min_compressed_buffer_size);
636         SDL_assert( compressed_frame != NULL );
637
638         unsigned int offset;
639
640         if (AVI_stream.frame_index[frame_number-1].offset > AVI_stream.movi_offset) {
641                 offset = AVI_stream.frame_index[frame_number-1].offset;
642         } else if (AVI_stream.frame_index[frame_number-1].offset == AVI_stream.movi_offset) {
643                 offset = AVI_stream.frame_index[frame_number-1].offset + sizeof(int);
644         } else {
645                 offset = AVI_stream.movi_offset + AVI_stream.frame_index[frame_number-1].offset;
646         }
647
648         fseek(AVI_stream.pfile, offset, SEEK_SET);
649         fread(compressed_frame, 1, AVI_stream.frame_index[frame_number-1].size, AVI_stream.pfile);
650
651         AVI_decompress_RLE8(compressed_frame, buffer, AVI_stream.frame_index[frame_number-1].size);
652
653         free( compressed_frame );
654         return 0;
655
656 }
657
658
659
660 // -------------------------------------------------------------------------------------------------
661 // AVI_decompress_RLE8() will decompress the data pointed to by src, and store in dest.
662 //
663 //      NOTE:  1. memory for dest must be already allocated before calling function
664 //
665
666 void AVI_decompress_RLE8(ubyte* src, ubyte* dest, unsigned int srcSize)
667 {
668         int src_index = 0;
669         int dest_index = 0;
670         int i;
671
672         SDL_assert( src != NULL);
673         SDL_assert( dest != NULL);
674         SDL_assert( srcSize > 0 );
675
676         ubyte count;
677         ubyte run;
678         ubyte control_code;
679         ubyte x_off;
680         ubyte y_off;
681
682         int size_src = srcSize;
683
684         int scan_line = AVI_stream.h-1;
685         int height_offset = scan_line * AVI_stream.w;
686
687         while ( src_index < size_src ) {
688                 
689                 count = src[src_index];
690                 
691                 if ( count == 0 ) {     // control code follows
692                         src_index++;
693                         control_code = src[src_index];
694                         if ( control_code == 1 ) {
695                                 src_index++;
696 //                              nprintf(("AVI","AVI ==> Reached end of compressed image\n"));
697                                 break;
698                         }
699                         else if ( control_code == 0 ) {
700                                 src_index++;
701                                 scan_line--;
702                                 height_offset = scan_line * AVI_stream.w;       // only need to calc once per scanline
703                                 dest_index = 0;
704                                 //nprintf(("AVI","AVI ==> Reached end of line in compressed image\n"));
705                         }
706                         else if ( control_code == 2 ) {
707                                 // delta - horizontal and veritcal offsets
708                                 src_index++;
709                                 x_off = src[src_index];
710
711                                 if (x_off) {
712                                         dest_index += x_off;
713                                 }
714
715                                 src_index++;
716                                 y_off = src[src_index];
717
718                                 if (y_off) {
719                                         scan_line -= y_off;
720                                         height_offset = scan_line * AVI_stream.w;
721                                 }
722
723                                 src_index++;
724                         }
725                         else {
726                                 // in absolute mode
727                                 src_index++;
728                                 for ( i = 0; i < control_code; i++ ) {
729                                         SDL_assert( (height_offset + dest_index) < (AVI_stream.w * AVI_stream.h) );
730                                         dest[height_offset + dest_index] = src[src_index];
731                                         dest_index++;
732                                         src_index++;
733                                 }
734                                 // run must end on a word boundry
735                                 if ( control_code & 1 )
736                                         src_index++;
737                         }
738                 }
739                 else {
740                         src_index++;
741                         run = src[src_index];
742                         // nprintf(("AVI","AVI ==> Got %d pixel run of %d\n", src[src_index], count));
743                         if (dest_index+count > AVI_stream.w) count = AVI_stream.w - dest_index;
744                         SDL_assert( (height_offset + dest_index + count) <= (AVI_stream.w * AVI_stream.h) );
745                         memset( &dest[height_offset + dest_index], run, count );
746                         dest_index += count;
747                         src_index++;
748                 }
749         }       // end while
750
751 }
752
753 int save_anim_header()
754 {
755         int i, new_format_id = 0;
756
757         SDL_assert(anim_fp);
758         fclose(anim_fp);
759         anim_fp = fopen(anim_save_filename, "r+b");
760
761         if (!fwrite(&new_format_id, 2, 1, anim_fp))
762                 return -1;
763         if (!fwrite(&Anim.version, 2, 1, anim_fp))
764                 return -1;
765         if (!fwrite(&Anim.fps, 2, 1, anim_fp))
766                 return -1;
767         if (!fwrite(&Anim.xparent_r, 1, 1, anim_fp))
768                 return -1;
769         if (!fwrite(&Anim.xparent_g, 1, 1, anim_fp))
770                 return -1;
771         if (!fwrite(&Anim.xparent_b, 1, 1, anim_fp))
772                 return -1;
773         if (!fwrite(&Anim.width, 2, 1, anim_fp))
774                 return -1;
775         if (!fwrite(&Anim.height, 2, 1, anim_fp))
776                 return -1;
777         if (!fwrite(&Anim.total_frames, 2, 1, anim_fp))
778                 return -1;
779         if (!fwrite(&Anim.packer_code, 1, 1, anim_fp))
780                 return -1;
781         if (fwrite(&Anim.palette, 3, 256, anim_fp) != 256)
782                 return -1;
783         if (!fwrite(&total_key_frames, 2, 1, anim_fp))
784                 return -1;
785
786         for (i=0; i<Anim.num_keys; i++) {
787                 if (!fwrite(&Anim.keys[i].frame_num, 2, 1, anim_fp))
788                         return -1;
789
790                 if (!fwrite(&Anim.keys[i].offset, 4, 1, anim_fp))
791                         return -1;
792         }
793
794         if (!fwrite(&anim_offset, 4, 1, anim_fp))
795                 return -1;
796
797         return 0;
798 }
799
800 // This function allocates a linked list of key frame headers.
801 // It is responsible for determining which frames in an anim
802 // should be key frames.
803 int allocate_key_frames(int total_frames)
804 {
805         int count = 0, frame = 1, rate = key_frame_rate, last_frame;
806
807         if (!rate)
808                 rate = total_frames;
809
810         while (frame <= total_frames) {
811                 count++;
812                 frame += rate;
813         }
814
815         if (force_key_frame >= 0)
816                 count++;
817
818         if (count)
819                 Anim.keys = (key_frame *) malloc(count * sizeof(key_frame));
820
821         count = 0;
822         frame = last_frame = 1;
823         while (frame <= total_frames) {
824                 if ((force_key_frame > last_frame) && (force_key_frame < frame))
825                         Anim.keys[count++].frame_num = force_key_frame;
826
827                 Anim.keys[count++].frame_num = frame;
828                 frame += rate;
829         }
830
831         if (force_key_frame > last_frame)
832                 Anim.keys[count++].frame_num = force_key_frame;
833
834         Anim.num_keys = count;
835         return count;  // number of key frames
836 }
837
838 int anim_save_init(char *file, int width, int height, int frames)
839 {
840         SDL_assert(file);
841         anim_save_filename = file;
842         anim_fp = fopen(file, "wb");
843         if (!anim_fp)
844                 return -1;
845
846         Anim.version = ANIM_VERSION;
847         Anim.fps = Default_fps;
848         Anim.width = width;
849         Anim.height = height;
850         Anim.packer_code = PACKER_CODE;
851         Anim.xparent_r = Xparent_color.r;
852         Anim.xparent_g = Xparent_color.g;
853         Anim.xparent_b = Xparent_color.b;
854         Anim.total_frames = frames;
855         anim_offset = 0;
856         cur_frame_num = 0;
857         total_key_frames = allocate_key_frames(frames);
858         fseek(anim_fp, ANIM_HEADER_SIZE + total_key_frames * 6, SEEK_SET);
859
860         switch ( Compression_type ) {
861                 case CUSTOM_DELTA_RLE:
862                         Key_frame_compression = PACKING_METHOD_RLE_KEY;
863                         Regular_frame_compression = PACKING_METHOD_RLE;
864                         break;
865
866                 case STD_DELTA_RLE:
867                         Key_frame_compression = PACKING_METHOD_STD_RLE_KEY;
868                         Regular_frame_compression = PACKING_METHOD_STD_RLE;
869                         break;
870
871                 default:
872                         Int3();
873                         return -1;
874                         break;
875         } // end switch
876
877         return 0;
878 }
879
880 int anim_save_frame()
881 {
882         ubyte *temp;
883         int i, size;
884         key_frame *keyp = NULL;
885
886         SDL_assert(anim_fp);
887         cur_frame_num++;
888         SDL_assert(cur_frame_num <= Anim.total_frames);
889
890         for (i=0; i<Anim.num_keys; i++)
891                 if (Anim.keys[i].frame_num == cur_frame_num) {
892                         keyp = &Anim.keys[i];
893                         break;
894                 }
895
896         if (keyp) {
897                 fprintf(stdout, "*");
898                 fflush(stdout);
899                 keyp->offset = anim_offset;
900                 size = pack_key_frame(cur_frame, anim_buffer, Anim.width * Anim.height, ANIM_BUFFER_MAX, Key_frame_compression);
901
902         } else {
903                 fprintf(stdout, ".");
904                 fflush(stdout);
905                 size = pack_frame(cur_frame, last_frame, anim_buffer, Anim.width * Anim.height, ANIM_BUFFER_MAX, Regular_frame_compression);
906         }
907
908         if (size < 0)
909                 return -1;
910
911         if ((int) fwrite(anim_buffer, 1, size, anim_fp) != size)
912                 return -1;
913
914         anim_offset += size;
915         temp = cur_frame;
916         cur_frame = last_frame;
917         last_frame = temp;
918         return 0;
919 }
920
921
922 // converts an avi file to an anim file
923 //
924 // returns:   0 ==> success
925 //                !0 ==> failure
926 //
927 int convert_avi_to_anim(char* filename)
928 {
929         char ani_filename[255];
930         int ret = 1;
931         int rc, i, xparent_pal_index;
932         int avi_stream_opened = 0;
933
934         rc = AVI_stream_open(filename);
935         if ( rc != 0 ) {
936                 // could not open the AVI stream
937                 goto Finish;
938         }
939         avi_stream_opened = 1;
940         
941         SDL_assert(AVI_stream.bpp == 8);
942         cur_frame = (ubyte*) malloc(AVI_stream.w * AVI_stream.h);
943         last_frame = (ubyte*) malloc(AVI_stream.w * AVI_stream.h);
944         SDL_assert(cur_frame && last_frame);
945
946         strcpy(ani_filename, AVI_stream.filename);
947         strcpy(ani_filename + strlen(ani_filename) - 3, "ani");
948
949         memcpy(Anim.palette, AVI_stream.palette, 768);
950
951         if (Use_custom_xparent_color) {
952                 // Need to look at pixel in top-left 
953                 rc = AVI_stream_get_frame(cur_frame, 1);
954                 if ( rc != 0 )
955                         goto Finish;
956
957                 xparent_pal_index = cur_frame[0];
958                 Xparent_color.r = Anim.palette[xparent_pal_index * 3];
959                 Xparent_color.g = Anim.palette[xparent_pal_index * 3 + 1];
960                 Xparent_color.b = Anim.palette[xparent_pal_index * 3 + 2];
961
962         } else {
963                 Xparent_color.r = 0;
964                 Xparent_color.g = 255;
965                 Xparent_color.b = 0;
966         }
967         
968         rc = anim_save_init(ani_filename, AVI_stream.w, AVI_stream.h, AVI_stream.num_frames);
969         if (rc == -1)
970                 goto Finish;
971
972         for ( i=1; i <= AVI_stream.num_frames; i++ ) {
973                 // get uncompressed frame from the AVI
974                 rc = AVI_stream_get_frame(cur_frame, i);
975                 if ( rc != 0 )
976                         goto Finish;
977
978                 // pass to the anim compression
979                 rc = anim_save_frame();
980                 if ( rc != 0 )
981                         goto Finish;
982         }
983
984         rc = save_anim_header();
985         if ( rc != 0 )
986                 goto Finish;
987
988         ret = 0;
989
990         Finish:
991         // done with the AVI.. close the stream
992         if ( avi_stream_opened )
993                 AVI_stream_close();
994
995         if ( anim_fp )
996                 fclose(anim_fp);
997
998         free(cur_frame);
999         free(last_frame);
1000         fprintf(stdout,"\n");
1001         fflush(stdout);
1002         return ret;
1003 }
1004
1005 int convert_frames_to_anim(char *filename)
1006 {
1007         int first_frame, frame, pos, width, height, xparent_pal_index, r = -1;
1008         char ani_filename[255], name[255], temp[8];     
1009         int rc;
1010         FILE *fp;
1011
1012         SDL_assert(strlen(filename) < 254);
1013         strcpy(name, filename);
1014         strcpy(ani_filename, filename);
1015         strcpy(ani_filename + strlen(ani_filename) - 8, ".ani");
1016         pos = strlen(name) - 8;
1017         frame = first_frame = atoi(&name[pos]);
1018         force_key_frame -= frame;
1019
1020         // first file
1021         fp = fopen(name, "rb");
1022         if(fp != NULL){
1023                 do {
1024                         fclose(fp);
1025                         frame++;
1026                         sprintf(temp, "%04d", frame);
1027                         strncpy(&name[pos], temp, 4);   
1028
1029                         // next file
1030                         fp = fopen(name, "rb");
1031                 } while(fp != NULL);    
1032         }
1033
1034         rc = pcx_read_header(filename, &width, &height, NULL);
1035         if (rc != PCX_ERROR_NONE) {
1036                 fprintf(stdout, "An error reading the PCX file %s.  It may not exist.\n", filename);
1037                 return -1;
1038         }
1039
1040         cur_frame = (ubyte *) malloc(width * height);
1041         last_frame = (ubyte *) malloc(width * height);
1042
1043         rc = pcx_read_bitmap_8bpp(filename, cur_frame, Anim.palette);
1044         if (rc != PCX_ERROR_NONE) {
1045                 fprintf(stdout, "An error reading the PCX file %s.  It may not exist.\n", filename);
1046                 return -1;
1047         }
1048
1049         if (Use_custom_xparent_color) {
1050                 // Need to look at pixel in top-left 
1051                 xparent_pal_index = ((ubyte *) cur_frame)[0];
1052                 Xparent_color.r = Anim.palette[xparent_pal_index * 3];
1053                 Xparent_color.g = Anim.palette[xparent_pal_index * 3 + 1];
1054                 Xparent_color.b = Anim.palette[xparent_pal_index * 3 + 2];
1055
1056         } else {
1057                 Xparent_color.r = 0;
1058                 Xparent_color.g = 255;
1059                 Xparent_color.b = 0;
1060         }
1061
1062         if (anim_save_init(ani_filename, width, height, frame - first_frame))
1063                 goto done;
1064
1065         while (first_frame < frame) {
1066                 sprintf(temp, "%04d", first_frame);
1067                 strncpy(&name[pos], temp, 4);
1068                 rc = pcx_read_bitmap_8bpp(name, cur_frame, Anim.palette);
1069                 if (rc != PCX_ERROR_NONE)
1070                         goto done;
1071
1072                 if (anim_save_frame())
1073                         goto done;
1074
1075                 first_frame++;
1076         }
1077
1078         if (save_anim_header())
1079                 goto done;
1080
1081         r = 0;
1082
1083 done:
1084         fclose(anim_fp);
1085         free(cur_frame);
1086         free(last_frame);
1087         fprintf(stdout, "\n");
1088         fflush(stdout);
1089         return r;
1090 }
1091