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