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