Clean up headers a bit more.
[blender.git] / source / blender / avi / intern / avi.c
1 /*
2  * $Id$
3  *
4  * This is external code.
5  *
6  * ***** BEGIN GPL LICENSE BLOCK *****
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  *
31  */
32
33 #include <stdlib.h>
34 #include <string.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <ctype.h>
38
39 #include "MEM_guardedalloc.h"
40
41 #include "AVI_avi.h"
42 #include "avi_intern.h"
43
44 #include "endian.h"
45
46 static int AVI_DEBUG=0;
47 static char DEBUG_FCC[4];
48
49 #define DEBUG_PRINT(x) if(AVI_DEBUG) printf("AVI DEBUG: " x);
50
51 /* local functions */
52 char *fcc_to_char (unsigned int fcc);
53 char *tcc_to_char (unsigned int tcc);
54
55
56
57 /* implemetation */
58
59 unsigned int GET_FCC (FILE *fp) {
60         unsigned char tmp[4];
61
62         tmp[0] = getc(fp);
63         tmp[1] = getc(fp);
64         tmp[2] = getc(fp);
65         tmp[3] = getc(fp);
66
67         return FCC (tmp);
68 }
69
70 unsigned int GET_TCC (FILE *fp) {
71         char tmp[5];
72
73         tmp[0] = getc(fp);
74         tmp[1] = getc(fp);
75         tmp[2] = 0;
76         tmp[3] = 0;
77
78         return FCC (tmp);
79 }
80
81 char *fcc_to_char (unsigned int fcc) {
82         DEBUG_FCC[0]= (fcc)&0177;
83         DEBUG_FCC[1]= (fcc>>8)&0177;
84         DEBUG_FCC[2]= (fcc>>16)&0177;
85         DEBUG_FCC[3]= (fcc>>24)&0177;
86
87         return DEBUG_FCC;       
88 }
89
90 char *tcc_to_char (unsigned int tcc) {
91         DEBUG_FCC[0]= (tcc)&0177;
92         DEBUG_FCC[1]= (tcc>>8)&0177;
93         DEBUG_FCC[2]= 0;
94         DEBUG_FCC[3]= 0;
95
96         return DEBUG_FCC;       
97 }
98
99 int AVI_get_stream (AviMovie *movie, int avist_type, int stream_num) {
100         int cur_stream;
101
102         if (movie == NULL)
103                 return -AVI_ERROR_OPTION;
104
105         for (cur_stream=0; cur_stream < movie->header->Streams; cur_stream++) {
106                 if (movie->streams[cur_stream].sh.Type == avist_type) {
107                         if (stream_num == 0)
108                                 return cur_stream;
109                         else
110                                 stream_num--;
111                 }
112         }
113
114         return -AVI_ERROR_FOUND;
115 }
116
117 static int fcc_get_stream (int fcc) {
118         char fccs[4];
119
120         fccs[0] = fcc;
121         fccs[1] = fcc>>8;
122         fccs[2] = fcc>>16;
123         fccs[3] = fcc>>24;
124
125         return 10*(fccs[0]-'0') + (fccs[1]-'0');
126 }
127
128 static int fcc_is_data (int fcc) {
129         char fccs[4];
130
131         fccs[0] = fcc;
132         fccs[1] = fcc>>8;
133         fccs[2] = fcc>>16;
134         fccs[3] = fcc>>24;
135
136         if (!isdigit (fccs[0]) || !isdigit (fccs[1]) || (fccs[2] != 'd' && fccs[2] != 'w'))
137                 return 0;
138         if (fccs[3] != 'b' && fccs[3] != 'c')
139                 return 0;
140
141         return 1;
142 }
143
144 AviError AVI_print_error (AviError in_error) {
145         int error;
146
147         if ((int) in_error < 0)
148                 error = -in_error;
149         else
150                 error = in_error;
151
152         switch (error) {
153         case AVI_ERROR_NONE:
154                 break;
155         case AVI_ERROR_COMPRESSION:
156                 printf ("AVI ERROR: compressed in an unsupported format\n");
157                 break;
158         case AVI_ERROR_OPEN:
159                 printf ("AVI ERROR: could not open file\n");
160                 break;
161         case AVI_ERROR_READING:
162                 printf ("AVI ERROR: could not read from file\n");
163                 break;
164         case AVI_ERROR_WRITING:
165                 printf ("AVI ERROR: could not write to file\n");
166                 break;
167         case AVI_ERROR_FORMAT:
168                 printf ("AVI ERROR: file is in an illegal or unrecognized format\n");
169                 break;
170         case AVI_ERROR_ALLOC:
171                 printf ("AVI ERROR: error encountered while allocating memory\n");
172                 break;
173         case AVI_ERROR_OPTION:
174                 printf ("AVI ERROR: program made illegal request\n");
175                 break;
176         case AVI_ERROR_FOUND:
177                 printf ("AVI ERROR: movie did not contain expected item\n");
178                 break;
179         default: 
180                 break;
181         }
182
183         return in_error;
184 }
185 /*
186 void AVI_set_debug (int mode) {
187         AVI_DEBUG= mode;
188 }
189 */
190 /*
191 int AVI_is_avi (char *name) {
192         FILE *fp;
193         int ret;
194         
195         fp = fopen (name, "rb");
196         if (fp == NULL)
197                 return 0;
198
199         if (GET_FCC (fp) != FCC("RIFF") ||
200                 !GET_FCC (fp) ||
201                 GET_FCC (fp) != FCC("AVI ")) {
202                 ret = 0;
203         } else {
204                 ret = 1;
205         }
206
207         fclose(fp);
208         return ret;
209 }
210 */
211
212 int AVI_is_avi (const char *name) {
213         int temp, fcca, j;
214         AviMovie movie= {0};
215         AviMainHeader header;
216         AviBitmapInfoHeader bheader;
217         int movie_tracks = 0;
218         
219         DEBUG_PRINT("opening movie\n");
220
221         movie.type = AVI_MOVIE_READ;
222         movie.fp = fopen (name, "rb");
223         movie.offset_table = NULL;
224
225         if (movie.fp == NULL)
226                 return 0;
227
228         if (GET_FCC (movie.fp) != FCC("RIFF") ||
229                 !(movie.size = GET_FCC (movie.fp))) {
230                 fclose(movie.fp);
231                 return 0;
232         }
233
234         movie.header = &header;
235
236         if (GET_FCC (movie.fp) != FCC("AVI ") ||
237                 GET_FCC (movie.fp) != FCC("LIST") ||
238                 !GET_FCC (movie.fp) ||
239                 GET_FCC (movie.fp) != FCC("hdrl") ||
240                 (movie.header->fcc = GET_FCC (movie.fp)) != FCC("avih") ||
241                 !(movie.header->size = GET_FCC (movie.fp))) {
242                 DEBUG_PRINT("bad initial header info\n");
243                 fclose(movie.fp);
244                 return 0;
245         }
246         
247         movie.header->MicroSecPerFrame = GET_FCC(movie.fp);
248         movie.header->MaxBytesPerSec = GET_FCC(movie.fp);
249         movie.header->PaddingGranularity = GET_FCC(movie.fp);
250         movie.header->Flags = GET_FCC(movie.fp);
251         movie.header->TotalFrames = GET_FCC(movie.fp);
252         movie.header->InitialFrames = GET_FCC(movie.fp);
253         movie.header->Streams = GET_FCC(movie.fp);
254         movie.header->SuggestedBufferSize = GET_FCC(movie.fp);
255         movie.header->Width = GET_FCC(movie.fp);
256         movie.header->Height = GET_FCC(movie.fp);
257         movie.header->Reserved[0] = GET_FCC(movie.fp);
258         movie.header->Reserved[1] = GET_FCC(movie.fp);
259         movie.header->Reserved[2] = GET_FCC(movie.fp);
260         movie.header->Reserved[3] = GET_FCC(movie.fp);
261
262         fseek (movie.fp, movie.header->size-14*4, SEEK_CUR);
263
264         if (movie.header->Streams < 1) {
265                 DEBUG_PRINT("streams less than 1\n");
266                 fclose(movie.fp);
267                 return 0;
268         }
269         
270         movie.streams = (AviStreamRec *) MEM_callocN (sizeof(AviStreamRec) * movie.header->Streams, "moviestreams");
271
272         for (temp=0; temp < movie.header->Streams; temp++) {
273
274                 if (GET_FCC(movie.fp) != FCC("LIST") ||
275                         !GET_FCC (movie.fp) ||
276                         GET_FCC (movie.fp) != FCC ("strl") ||
277                         (movie.streams[temp].sh.fcc = GET_FCC (movie.fp)) != FCC ("strh") ||
278                         !(movie.streams[temp].sh.size = GET_FCC (movie.fp))) {
279                         DEBUG_PRINT("bad stream header information\n");
280                         
281                         MEM_freeN(movie.streams);
282                         fclose(movie.fp);
283                         return 0;                               
284                 }
285
286                 movie.streams[temp].sh.Type = GET_FCC (movie.fp);
287                 movie.streams[temp].sh.Handler = GET_FCC (movie.fp);
288
289                 fcca = movie.streams[temp].sh.Handler;
290                 
291                 if (movie.streams[temp].sh.Type == FCC("vids")) {
292                         if (fcca == FCC ("DIB ") ||
293                                 fcca == FCC ("RGB ") ||
294                                 fcca == FCC ("rgb ") ||
295                                 fcca == FCC ("RAW ") ||
296                                 fcca == 0) {
297                                 movie.streams[temp].format = AVI_FORMAT_AVI_RGB;
298                         } else if (fcca == FCC ("mjpg")||fcca == FCC ("MJPG")) {
299                                 movie.streams[temp].format = AVI_FORMAT_MJPEG;
300                         } else {
301                                 MEM_freeN(movie.streams);
302                                 fclose(movie.fp);
303                                 return 0;
304                         }
305                         movie_tracks++;
306                 }
307                 
308                 movie.streams[temp].sh.Flags = GET_FCC (movie.fp);
309                 movie.streams[temp].sh.Priority = GET_TCC (movie.fp);
310                 movie.streams[temp].sh.Language = GET_TCC (movie.fp);
311                 movie.streams[temp].sh.InitialFrames = GET_FCC (movie.fp);
312                 movie.streams[temp].sh.Scale = GET_FCC (movie.fp);
313                 movie.streams[temp].sh.Rate = GET_FCC (movie.fp);
314                 movie.streams[temp].sh.Start = GET_FCC (movie.fp);
315                 movie.streams[temp].sh.Length = GET_FCC (movie.fp);
316                 movie.streams[temp].sh.SuggestedBufferSize = GET_FCC (movie.fp);
317                 movie.streams[temp].sh.Quality = GET_FCC (movie.fp);
318                 movie.streams[temp].sh.SampleSize = GET_FCC (movie.fp);
319                 movie.streams[temp].sh.left = GET_TCC (movie.fp);
320                 movie.streams[temp].sh.top = GET_TCC (movie.fp);
321                 movie.streams[temp].sh.right = GET_TCC (movie.fp);
322                 movie.streams[temp].sh.bottom = GET_TCC (movie.fp);
323
324                 fseek (movie.fp, movie.streams[temp].sh.size-14*4, SEEK_CUR);
325
326                 if (GET_FCC (movie.fp) != FCC("strf")) {
327                         DEBUG_PRINT("no stream format information\n");
328                         MEM_freeN(movie.streams);
329                         fclose(movie.fp);
330                         return 0;
331                 }
332
333                 movie.streams[temp].sf_size= GET_FCC(movie.fp);
334                 if (movie.streams[temp].sh.Type == FCC("vids")) {
335                         j = movie.streams[temp].sf_size - (sizeof(AviBitmapInfoHeader) - 8);
336                         if (j >= 0) {
337                                 AviBitmapInfoHeader *bi;
338                                 
339                                 movie.streams[temp].sf= &bheader;
340                                 bi= (AviBitmapInfoHeader *) movie.streams[temp].sf;
341                                 
342                                 bi->fcc= FCC("strf");
343                                 bi->size= movie.streams[temp].sf_size;
344                                 bi->Size= GET_FCC(movie.fp);
345                                 bi->Width= GET_FCC(movie.fp);
346                                 bi->Height= GET_FCC(movie.fp);
347                                 bi->Planes= GET_TCC(movie.fp);
348                                 bi->BitCount= GET_TCC(movie.fp);
349                                 bi->Compression= GET_FCC(movie.fp);
350                                 bi->SizeImage= GET_FCC(movie.fp);
351                                 bi->XPelsPerMeter= GET_FCC(movie.fp);
352                                 bi->YPelsPerMeter= GET_FCC(movie.fp);
353                                 bi->ClrUsed= GET_FCC(movie.fp);
354                                 bi->ClrImportant= GET_FCC(movie.fp);
355                                 
356                                 fcca = bi->Compression;
357
358                                 if ( movie.streams[temp].format ==
359                                          AVI_FORMAT_AVI_RGB) {
360                                         if (fcca == FCC ("DIB ") ||
361                                                 fcca == FCC ("RGB ") ||
362                                                 fcca == FCC ("rgb ") ||
363                                                 fcca == FCC ("RAW ") ||
364                                                 fcca == 0 ) {
365                                         } else if ( fcca == FCC ("mjpg") || 
366                                                 fcca == FCC ("MJPG")) {
367                                                         movie.streams[temp].format = AVI_FORMAT_MJPEG;
368                                         } else {
369                                                 MEM_freeN(movie.streams);
370                                                 fclose(movie.fp);
371                                                 return 0;
372                                         }
373                                 }
374
375                         } 
376                         if (j > 0) fseek (movie.fp, j, SEEK_CUR);
377                 } else fseek (movie.fp, movie.streams[temp].sf_size, SEEK_CUR);
378
379                 /* Walk to the next LIST */             
380                 while (GET_FCC (movie.fp) != FCC("LIST")) {
381                         temp= GET_FCC (movie.fp);
382                         if (temp<0 || ftell(movie.fp) > movie.size) {
383                                 DEBUG_PRINT("incorrect size in header or error in AVI\n");
384                                 
385                                 MEM_freeN(movie.streams);
386                                 fclose(movie.fp);
387                                 return 0;                               
388                         }
389                         fseek(movie.fp, temp, SEEK_CUR);                        
390                 }
391
392                 fseek(movie.fp, -4L, SEEK_CUR);
393         }
394         
395         MEM_freeN(movie.streams);
396         fclose(movie.fp);
397
398         /* at least one video track is needed */
399         return (movie_tracks != 0); 
400                                        
401 }
402
403 AviError AVI_open_movie (const char *name, AviMovie *movie) {
404         int temp, fcca, size, j;
405         
406         DEBUG_PRINT("opening movie\n");
407
408         memset(movie, 0, sizeof(AviMovie));
409
410         movie->type = AVI_MOVIE_READ;
411         movie->fp = fopen (name, "rb");
412         movie->offset_table = NULL;
413
414         if (movie->fp == NULL)
415                 return AVI_ERROR_OPEN;
416
417         if (GET_FCC (movie->fp) != FCC("RIFF") ||
418                 !(movie->size = GET_FCC (movie->fp)))
419                 return AVI_ERROR_FORMAT;
420
421         movie->header = (AviMainHeader *) MEM_mallocN (sizeof (AviMainHeader), "movieheader");
422
423         if (GET_FCC (movie->fp) != FCC("AVI ") ||
424                 GET_FCC (movie->fp) != FCC("LIST") ||
425                 !GET_FCC (movie->fp) ||
426                 GET_FCC (movie->fp) != FCC("hdrl") ||
427                 (movie->header->fcc = GET_FCC (movie->fp)) != FCC("avih") ||
428                 !(movie->header->size = GET_FCC (movie->fp))) {
429                 DEBUG_PRINT("bad initial header info\n");
430                 return AVI_ERROR_FORMAT;
431         }
432         
433         movie->header->MicroSecPerFrame = GET_FCC(movie->fp);
434         movie->header->MaxBytesPerSec = GET_FCC(movie->fp);
435         movie->header->PaddingGranularity = GET_FCC(movie->fp);
436         movie->header->Flags = GET_FCC(movie->fp);
437         movie->header->TotalFrames = GET_FCC(movie->fp);
438         movie->header->InitialFrames = GET_FCC(movie->fp);
439         movie->header->Streams = GET_FCC(movie->fp);
440         movie->header->SuggestedBufferSize = GET_FCC(movie->fp);
441         movie->header->Width = GET_FCC(movie->fp);
442         movie->header->Height = GET_FCC(movie->fp);
443         movie->header->Reserved[0] = GET_FCC(movie->fp);
444         movie->header->Reserved[1] = GET_FCC(movie->fp);
445         movie->header->Reserved[2] = GET_FCC(movie->fp);
446         movie->header->Reserved[3] = GET_FCC(movie->fp);
447
448         fseek (movie->fp, movie->header->size-14*4, SEEK_CUR);
449
450         if (movie->header->Streams < 1) {
451                 DEBUG_PRINT("streams less than 1\n");
452                 return AVI_ERROR_FORMAT;
453         }
454         
455         movie->streams = (AviStreamRec *) MEM_callocN (sizeof(AviStreamRec) * movie->header->Streams, "moviestreams");
456
457         for (temp=0; temp < movie->header->Streams; temp++) {
458
459                 if (GET_FCC(movie->fp) != FCC("LIST") ||
460                         !GET_FCC (movie->fp) ||
461                         GET_FCC (movie->fp) != FCC ("strl") ||
462                         (movie->streams[temp].sh.fcc = GET_FCC (movie->fp)) != FCC ("strh") ||
463                         !(movie->streams[temp].sh.size = GET_FCC (movie->fp))) {
464                         DEBUG_PRINT("bad stream header information\n");
465                         return AVI_ERROR_FORMAT;                                
466                 }
467
468                 movie->streams[temp].sh.Type = GET_FCC (movie->fp);
469                 movie->streams[temp].sh.Handler = GET_FCC (movie->fp);
470
471                 fcca = movie->streams[temp].sh.Handler;
472                 
473                 if (movie->streams[temp].sh.Type == FCC("vids")) {
474                         if (fcca == FCC ("DIB ") ||
475                                 fcca == FCC ("RGB ") ||
476                                 fcca == FCC ("rgb ") ||
477                                 fcca == FCC ("RAW ") ||
478                                 fcca == 0) {
479                                 movie->streams[temp].format = AVI_FORMAT_AVI_RGB;
480                         } else if (fcca == FCC ("mjpg")||fcca == FCC ("MJPG")) {
481                                 movie->streams[temp].format = AVI_FORMAT_MJPEG;
482                         } else {
483                                 return AVI_ERROR_COMPRESSION;
484                         }
485                 }
486                 
487                 movie->streams[temp].sh.Flags = GET_FCC (movie->fp);
488                 movie->streams[temp].sh.Priority = GET_TCC (movie->fp);
489                 movie->streams[temp].sh.Language = GET_TCC (movie->fp);
490                 movie->streams[temp].sh.InitialFrames = GET_FCC (movie->fp);
491                 movie->streams[temp].sh.Scale = GET_FCC (movie->fp);
492                 movie->streams[temp].sh.Rate = GET_FCC (movie->fp);
493                 movie->streams[temp].sh.Start = GET_FCC (movie->fp);
494                 movie->streams[temp].sh.Length = GET_FCC (movie->fp);
495                 movie->streams[temp].sh.SuggestedBufferSize = GET_FCC (movie->fp);
496                 movie->streams[temp].sh.Quality = GET_FCC (movie->fp);
497                 movie->streams[temp].sh.SampleSize = GET_FCC (movie->fp);
498                 movie->streams[temp].sh.left = GET_TCC (movie->fp);
499                 movie->streams[temp].sh.top = GET_TCC (movie->fp);
500                 movie->streams[temp].sh.right = GET_TCC (movie->fp);
501                 movie->streams[temp].sh.bottom = GET_TCC (movie->fp);
502
503                 fseek (movie->fp, movie->streams[temp].sh.size-14*4, SEEK_CUR);
504
505                 if (GET_FCC (movie->fp) != FCC("strf")) {
506                         DEBUG_PRINT("no stream format information\n");
507                         return AVI_ERROR_FORMAT;
508                 }
509
510                 movie->streams[temp].sf_size= GET_FCC(movie->fp);
511                 if (movie->streams[temp].sh.Type == FCC("vids")) {
512                         j = movie->streams[temp].sf_size - (sizeof(AviBitmapInfoHeader) - 8);
513                         if (j >= 0) {
514                                 AviBitmapInfoHeader *bi;
515                                 
516                                 movie->streams[temp].sf= MEM_mallocN(sizeof(AviBitmapInfoHeader), "streamformat");
517                                 
518                                 bi= (AviBitmapInfoHeader *) movie->streams[temp].sf;
519                                 
520                                 bi->fcc= FCC("strf");
521                                 bi->size= movie->streams[temp].sf_size;
522                                 bi->Size= GET_FCC(movie->fp);
523                                 bi->Width= GET_FCC(movie->fp);
524                                 bi->Height= GET_FCC(movie->fp);
525                                 bi->Planes= GET_TCC(movie->fp);
526                                 bi->BitCount= GET_TCC(movie->fp);
527                                 bi->Compression= GET_FCC(movie->fp);
528                                 bi->SizeImage= GET_FCC(movie->fp);
529                                 bi->XPelsPerMeter= GET_FCC(movie->fp);
530                                 bi->YPelsPerMeter= GET_FCC(movie->fp);
531                                 bi->ClrUsed= GET_FCC(movie->fp);
532                                 bi->ClrImportant= GET_FCC(movie->fp);
533                                 
534                                 fcca = bi->Compression;
535
536                                                                 if ( movie->streams[temp].format ==
537                                          AVI_FORMAT_AVI_RGB) {
538                                         if (fcca == FCC ("DIB ") ||
539                                                 fcca == FCC ("RGB ") ||
540                                                 fcca == FCC ("rgb ") ||
541                                                 fcca == FCC ("RAW ") ||
542                                                 fcca == 0 ) {
543                                         } else if ( fcca == FCC ("mjpg") || 
544                                                 fcca == FCC ("MJPG")) {
545                                                         movie->streams[temp].format = AVI_FORMAT_MJPEG;
546                                         } else {
547                                                 return AVI_ERROR_COMPRESSION;
548                                         }
549                                 }
550
551                         } 
552                         if (j > 0) fseek (movie->fp, j, SEEK_CUR);
553                 } else fseek (movie->fp, movie->streams[temp].sf_size, SEEK_CUR);
554                 
555                 /* Walk to the next LIST */             
556                 while (GET_FCC (movie->fp) != FCC("LIST")) {
557                         temp= GET_FCC (movie->fp);
558                         if (temp<0 || ftell(movie->fp) > movie->size) {
559                                 DEBUG_PRINT("incorrect size in header or error in AVI\n");
560                                 return AVI_ERROR_FORMAT;                                
561                         }
562                         fseek(movie->fp, temp, SEEK_CUR);                       
563                 }
564                 
565                 fseek(movie->fp, -4L, SEEK_CUR);                
566         }
567
568         while (1) {
569                 temp = GET_FCC (movie->fp);
570                 size = GET_FCC (movie->fp);
571
572                 if (size == 0)
573                         break;
574
575                 if (temp == FCC("LIST")) {
576                         if (GET_FCC(movie->fp) == FCC ("movi"))
577                                 break;
578                         else
579                                 fseek (movie->fp, size-4, SEEK_CUR);
580                 } else {
581                         fseek (movie->fp, size, SEEK_CUR);
582                 }
583                 if (ftell(movie->fp) > movie->size) {
584                         DEBUG_PRINT("incorrect size in header or error in AVI\n");
585                         return AVI_ERROR_FORMAT;
586                 }
587         }
588
589         movie->movi_offset = ftell (movie->fp);
590         movie->read_offset = movie->movi_offset;
591         if (AVI_DEBUG) printf ("movi_offset is %d\n", movie->movi_offset);
592         
593         /* Read in the index if the file has one, otherwise create one */
594         if (movie->header->Flags & AVIF_HASINDEX) {
595                 fseek(movie->fp, size-4, SEEK_CUR);
596
597                 if (GET_FCC(movie->fp) != FCC("idx1")) {
598                         DEBUG_PRINT("bad index informatio\n");
599                         return AVI_ERROR_FORMAT;
600                 }
601
602                 movie->index_entries = GET_FCC (movie->fp)/sizeof(AviIndexEntry);
603                 if (movie->index_entries == 0) {
604                         DEBUG_PRINT("no index entries\n");
605                         return AVI_ERROR_FORMAT;
606                 }
607
608                 movie->entries = (AviIndexEntry *) MEM_mallocN (movie->index_entries * sizeof(AviIndexEntry),"movieentries");
609
610                 for (temp=0; temp < movie->index_entries; temp++) {
611                         movie->entries[temp].ChunkId = GET_FCC (movie->fp);
612                         movie->entries[temp].Flags = GET_FCC (movie->fp);
613                         movie->entries[temp].Offset = GET_FCC (movie->fp);
614                         movie->entries[temp].Size = GET_FCC (movie->fp);
615                         
616                         if (AVI_DEBUG) printf ("Index entry %04d: ChunkId:%s Flags:%d Offset:%d Size:%d\n", temp, fcc_to_char(movie->entries[temp].ChunkId), movie->entries[temp].Flags, movie->entries[temp].Offset, movie->entries[temp].Size);
617                 }
618
619 /* Some AVI's have offset entries in absolute coordinates
620  * instead of an offset from the movie beginning... this is...
621  * wacky, but we need to handle it. The wacky offset always
622  * starts at movi_offset it seems... so we'll check that.
623  * Note the the offset needs an extra 4 bytes for some 
624  * undetermined reason */
625  
626                 if (movie->entries[0].Offset == movie->movi_offset)
627                         movie->read_offset= 4;
628         }
629
630         DEBUG_PRINT("movie succesfully opened\n");
631         return AVI_ERROR_NONE;
632 }
633
634 void *AVI_read_frame (AviMovie *movie, AviFormat format, int frame, int stream) {
635         int cur_frame=-1, temp, i=0, rewind=1;
636         void *buffer;
637
638         /* Retrieve the record number of the desired frame in the index 
639                 If a chunk has Size 0 we need to rewind to previous frame */
640         while(rewind && frame > -1) {
641                 i=0;
642                 cur_frame=-1;
643                 rewind = 0;
644
645                 while (cur_frame < frame && i < movie->index_entries) {
646                         if (fcc_is_data (movie->entries[i].ChunkId) &&
647                                 fcc_get_stream (movie->entries[i].ChunkId) == stream) {
648                                 if ((cur_frame == frame -1) && (movie->entries[i].Size == 0)) {
649                                         rewind = 1;
650                                         frame = frame -1;
651                                 } else {
652                                         cur_frame++;
653                                 }
654                         }
655                         i++;
656                 }
657         }
658
659         if (cur_frame != frame) return NULL;
660
661
662         fseek (movie->fp, movie->read_offset + movie->entries[i-1].Offset, SEEK_SET);
663
664         temp = GET_FCC(movie->fp);
665         buffer = MEM_mallocN (temp,"readbuffer");
666
667         if (fread (buffer, 1, temp, movie->fp) != temp) {
668                 MEM_freeN(buffer);
669
670                 return NULL;
671         }
672         
673         buffer = avi_format_convert (movie, stream, buffer, movie->streams[stream].format, format, &temp);
674
675         return buffer;
676 }
677
678 AviError AVI_close (AviMovie *movie) {
679         int i;
680
681         fclose (movie->fp);
682
683         for (i=0; i < movie->header->Streams; i++) {
684                 if (movie->streams[i].sf != NULL)
685                         MEM_freeN (movie->streams[i].sf);
686         }
687
688         if (movie->header != NULL)
689                 MEM_freeN (movie->header);
690         if (movie->streams!= NULL)
691                 MEM_freeN (movie->streams);
692         if (movie->entries != NULL)
693                 MEM_freeN (movie->entries);
694         if (movie->offset_table != NULL)
695                 MEM_freeN (movie->offset_table);
696
697         return AVI_ERROR_NONE;
698 }
699
700 AviError AVI_open_compress (char *name, AviMovie *movie, int streams, ...) {
701         va_list ap;
702         AviList list;
703         AviChunk chunk;
704         int i;
705         int header_pos1, header_pos2;
706         int stream_pos1, stream_pos2;
707
708         movie->type = AVI_MOVIE_WRITE;
709         movie->fp = fopen (name, "wb");
710
711         movie->index_entries = 0;
712
713         if (movie->fp == NULL)
714                 return AVI_ERROR_OPEN;
715
716         movie->offset_table = (long *) MEM_mallocN ((1+streams*2) * sizeof (long),"offsettable");
717         
718         for (i=0; i < 1 + streams*2; i++)
719                 movie->offset_table[i] = -1L;
720
721         movie->entries = NULL;
722
723         movie->header = (AviMainHeader *) MEM_mallocN (sizeof(AviMainHeader),"movieheader");
724
725         movie->header->fcc = FCC("avih");
726         movie->header->size = 56;
727         movie->header->MicroSecPerFrame = 66667;
728          movie->header->MaxBytesPerSec = 0; 
729          movie->header->PaddingGranularity = 0; 
730          movie->header->Flags = AVIF_HASINDEX | AVIF_MUSTUSEINDEX;
731         movie->header->TotalFrames = 0;
732         movie->header->InitialFrames = 0;
733         movie->header->Streams = streams;
734         movie->header->SuggestedBufferSize = 0;
735         movie->header->Width = 0;
736         movie->header->Height = 0;
737         movie->header->Reserved[0] = 0;
738         movie->header->Reserved[1] = 0;
739         movie->header->Reserved[2] = 0;
740         movie->header->Reserved[3] = 0;
741
742         movie->streams = (AviStreamRec *) MEM_mallocN (sizeof(AviStreamRec) * movie->header->Streams,"moviestreams");
743
744         va_start (ap, streams);
745
746         for (i=0; i < movie->header->Streams; i++) {
747                 movie->streams[i].format = va_arg(ap, AviFormat);
748
749                 movie->streams[i].sh.fcc = FCC ("strh");
750                 movie->streams[i].sh.size = 56;
751                 movie->streams[i].sh.Type = avi_get_format_type (movie->streams[i].format);
752                 if (movie->streams[i].sh.Type == 0)
753                         return AVI_ERROR_FORMAT;
754
755                 movie->streams[i].sh.Handler = avi_get_format_fcc (movie->streams[i].format);
756                 if (movie->streams[i].sh.Handler == 0)
757                         return AVI_ERROR_FORMAT;
758
759                 movie->streams[i].sh.Flags = 0;
760                 movie->streams[i].sh.Priority = 0;
761                 movie->streams[i].sh.Language = 0;
762                 movie->streams[i].sh.InitialFrames = 0;
763                 movie->streams[i].sh.Scale = 66667;
764                 movie->streams[i].sh.Rate = 1000000;
765                 movie->streams[i].sh.Start = 0;
766                 movie->streams[i].sh.Length = 0;
767                 movie->streams[i].sh.SuggestedBufferSize = 0;
768                 movie->streams[i].sh.Quality = 10000;
769                 movie->streams[i].sh.SampleSize = 0;
770                 movie->streams[i].sh.left = 0;
771                 movie->streams[i].sh.top = 0;
772                 movie->streams[i].sh.right = 0;
773                 movie->streams[i].sh.bottom = 0;
774
775                 if (movie->streams[i].sh.Type == FCC("vids")) { 
776 /*
777                         if (movie->streams[i].format == AVI_FORMAT_MJPEG) {
778                                 movie->streams[i].sf = MEM_mallocN (sizeof(AviBitmapInfoHeader) 
779                                                                                 + sizeof(AviMJPEGUnknown),"moviestreamformatL");
780                                 movie->streams[i].sf_size = sizeof(AviBitmapInfoHeader) + sizeof(AviMJPEGUnknown);
781                         } else {
782 */
783                         movie->streams[i].sf = MEM_mallocN (sizeof(AviBitmapInfoHeader),  "moviestreamformatS");
784                         movie->streams[i].sf_size = sizeof(AviBitmapInfoHeader);
785
786                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->fcc = FCC ("strf");
787                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->size = movie->streams[i].sf_size - 8;
788                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->Size = movie->streams[i].sf_size - 8;
789                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->Width = 0;
790                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->Height = 0;
791                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->Planes = 1;
792                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->BitCount = 24;
793                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->Compression = avi_get_format_compression (movie->streams[i].format);
794                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->SizeImage = 0;
795                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->XPelsPerMeter = 0;
796                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->YPelsPerMeter = 0;
797                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->ClrUsed = 0;
798                         ((AviBitmapInfoHeader *) movie->streams[i].sf)->ClrImportant = 0;
799
800 /*
801                         if (movie->streams[i].format == AVI_FORMAT_MJPEG) {
802                                 AviMJPEGUnknown *tmp;
803                                 
804                                 tmp = (AviMJPEGUnknown *) ((char*) movie->streams[i].sf +sizeof(AviBitmapInfoHeader));
805                                 
806                                 tmp->a = 44;
807                                 tmp->b = 24;
808                                 tmp->c = 0;
809                                 tmp->d = 2;
810                                 tmp->e = 8;
811                                 tmp->f = 2;
812                                 tmp->g = 1;
813                         }
814                 } else if (movie->streams[i].sh.Type == FCC("auds")) {
815                         ;
816 */
817                 }
818         }
819
820         list.fcc = FCC("RIFF");
821         list.size = 0;
822         list.ids = FCC("AVI ");
823
824         awrite (movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST);
825
826         list.fcc = FCC("LIST");
827         list.size = 0;
828         list.ids = FCC("hdrl");
829
830         awrite (movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST);
831
832         header_pos1 = ftell(movie->fp);
833
834         movie->offset_table[0] = ftell(movie->fp);
835
836         awrite (movie, movie->header, 1, sizeof(AviMainHeader), movie->fp, AVI_MAINH);
837
838         for (i=0; i < movie->header->Streams; i++) {
839                 list.fcc = FCC("LIST");
840                 list.size = 0;
841                 list.ids = FCC("strl");
842
843                 awrite (movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST);
844
845                 stream_pos1 = ftell(movie->fp);
846
847                 movie->offset_table[1+i*2] = ftell(movie->fp);
848                 awrite (movie, &movie->streams[i].sh, 1, sizeof(AviStreamHeader), movie->fp, AVI_STREAMH);
849
850                 movie->offset_table[1+i*2+1] = ftell(movie->fp);
851                 awrite (movie, movie->streams[i].sf, 1, movie->streams[i].sf_size, movie->fp, AVI_BITMAPH);
852
853                 stream_pos2 = ftell(movie->fp);
854
855                 fseek (movie->fp, stream_pos1-8, SEEK_SET);
856
857                 PUT_FCCN((stream_pos2-stream_pos1+4L), movie->fp);
858
859                 fseek (movie->fp, stream_pos2, SEEK_SET);
860         }
861
862         if (ftell(movie->fp) < 2024 - 8) {
863                 chunk.fcc = FCC("JUNK");
864                 chunk.size = 2024-8-ftell(movie->fp);
865
866                 awrite (movie, &chunk, 1, sizeof(AviChunk), movie->fp, AVI_CHUNK);
867
868                 for (i=0; i < chunk.size; i++)
869                         putc(0, movie->fp);
870         }
871
872         header_pos2 = ftell(movie->fp);
873
874         list.fcc = FCC("LIST");
875         list.size = 0;
876         list.ids = FCC("movi");
877
878         awrite (movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST);
879
880         movie->movi_offset = ftell(movie->fp)-8L;
881
882         fseek (movie->fp, AVI_HDRL_SOFF, SEEK_SET);
883
884         PUT_FCCN((header_pos2-header_pos1+4L), movie->fp);
885
886         return AVI_ERROR_NONE;
887 }
888
889 AviError AVI_write_frame (AviMovie *movie, int frame_num, ...) {
890         AviList list;
891         AviChunk chunk;
892         AviIndexEntry *temp;
893         va_list ap;
894         int stream;
895         long rec_off;
896         AviFormat format;
897         void *buffer;
898         int size;
899
900         if (frame_num < 0)
901                 return AVI_ERROR_OPTION;
902
903         /* Allocate the new memory for the index entry */
904
905         if (frame_num+1 > movie->index_entries) {
906                 temp = (AviIndexEntry *) MEM_mallocN ((frame_num+1) * 
907                         (movie->header->Streams+1) * sizeof(AviIndexEntry),"newidxentry");
908                 if (movie->entries != NULL) {
909                         memcpy (temp, movie->entries, movie->index_entries * (movie->header->Streams+1)
910                                 * sizeof(AviIndexEntry));
911                         MEM_freeN (movie->entries);
912                 }
913
914                 movie->entries = temp;
915                 movie->index_entries = frame_num+1;
916         }
917
918         /* Slap a new record entry onto the end of the file */
919
920         fseek (movie->fp, 0L, SEEK_END);
921
922         list.fcc = FCC("LIST");
923         list.size = 0;
924         list.ids = FCC("rec ");
925
926         awrite (movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST);
927
928         rec_off = ftell (movie->fp)-8L;
929
930         /* Write a frame for every stream */
931
932         va_start (ap, frame_num);
933
934         for (stream=0; stream < movie->header->Streams; stream++) {
935                 unsigned int tbuf=0;
936                 
937                 format = va_arg (ap, AviFormat);
938                 buffer = va_arg (ap, void*);
939                 size = va_arg (ap, int);
940
941                 /* Convert the buffer into the output format */
942                 buffer = avi_format_convert (movie, stream, buffer, format, movie->streams[stream].format, &size);
943
944                 /* Write the header info for this data chunk */
945
946                 fseek (movie->fp, 0L, SEEK_END);
947
948                 chunk.fcc = avi_get_data_id (format, stream);
949                 chunk.size = size;
950                 
951                 if (size%4) chunk.size += 4 - size%4;
952                 
953                 awrite (movie, &chunk, 1, sizeof(AviChunk), movie->fp, AVI_CHUNK);
954
955                 /* Write the index entry for this data chunk */
956
957                 movie->entries[frame_num * (movie->header->Streams+1) + stream + 1].ChunkId = chunk.fcc;
958                 movie->entries[frame_num * (movie->header->Streams+1) + stream + 1].Flags = AVIIF_KEYFRAME;
959                 movie->entries[frame_num * (movie->header->Streams+1) + stream + 1].Offset = ftell(movie->fp)-12L-movie->movi_offset;
960                 movie->entries[frame_num * (movie->header->Streams+1) + stream + 1].Size = chunk.size;
961
962                 /* Write the chunk */
963                 awrite (movie, buffer, 1, size, movie->fp, AVI_RAW);
964                 MEM_freeN (buffer);
965
966                 if (size%4) awrite (movie, &tbuf, 1, 4-size%4, movie->fp, AVI_RAW);
967
968                 /* Update the stream headers length field */
969                 movie->streams[stream].sh.Length++;
970                 fseek (movie->fp, movie->offset_table[1+stream*2], SEEK_SET);
971                 awrite (movie, &movie->streams[stream].sh, 1, sizeof(AviStreamHeader), movie->fp, AVI_STREAMH);
972         }
973         va_end (ap);
974
975         /* Record the entry for the new record */
976
977         fseek (movie->fp, 0L, SEEK_END);
978
979         movie->entries[frame_num * (movie->header->Streams+1)].ChunkId = FCC("rec ");
980         movie->entries[frame_num * (movie->header->Streams+1)].Flags = AVIIF_LIST;
981         movie->entries[frame_num * (movie->header->Streams+1)].Offset = rec_off-8L-movie->movi_offset;
982         movie->entries[frame_num * (movie->header->Streams+1)].Size = ftell(movie->fp)-(rec_off+4L);
983
984         /* Update the record size */
985         fseek (movie->fp, rec_off, SEEK_SET);
986         PUT_FCCN (movie->entries[frame_num * (movie->header->Streams+1)].Size, movie->fp);
987
988         /* Update the main header information in the file */
989         movie->header->TotalFrames++;
990         fseek (movie->fp, movie->offset_table[0], SEEK_SET);
991         awrite (movie, movie->header, 1, sizeof(AviMainHeader), movie->fp, AVI_MAINH);
992
993         return AVI_ERROR_NONE;
994 }
995
996 AviError AVI_close_compress (AviMovie *movie) {
997         int temp, movi_size, i;
998
999         fseek (movie->fp, 0L, SEEK_END);
1000         movi_size = ftell (movie->fp);
1001
1002         PUT_FCC ("idx1", movie->fp);
1003         PUT_FCCN ((movie->index_entries*(movie->header->Streams+1)*16), movie->fp);
1004
1005         for (temp=0; temp < movie->index_entries*(movie->header->Streams+1); temp++)
1006                 awrite (movie, &movie->entries[temp], 1, sizeof(AviIndexEntry), movie->fp, AVI_INDEXE);
1007
1008         temp = ftell (movie->fp);
1009
1010         fseek (movie->fp, AVI_RIFF_SOFF, SEEK_SET);
1011
1012         PUT_FCCN((temp-8L), movie->fp);
1013
1014         fseek (movie->fp, movie->movi_offset, SEEK_SET);
1015
1016         PUT_FCCN((movi_size-(movie->movi_offset+4L)),movie->fp);
1017
1018         fclose (movie->fp);
1019
1020         for (i=0; i < movie->header->Streams; i++) {
1021                 if (movie->streams[i].sf != NULL)
1022                         MEM_freeN (movie->streams[i].sf);
1023         }
1024         if (movie->header != NULL)
1025                 MEM_freeN (movie->header);
1026         if (movie->entries != NULL)
1027                 MEM_freeN (movie->entries);
1028         if (movie->streams != NULL)
1029                 MEM_freeN (movie->streams);
1030         if (movie->offset_table != NULL)
1031                 MEM_freeN (movie->offset_table);
1032         return AVI_ERROR_NONE;
1033 }