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