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