DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/aviwriter/riff.cpp
00001 /*
00002  *  Copyright (C) 2018-2020 Jon Campbell
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 /* Shut up! */
00020 #define _CRT_NONSTDC_NO_DEPRECATE
00021 
00022 #include "rawint.h"
00023 #include "riff.h"
00024 #include <unistd.h>
00025 #include <stdlib.h>
00026 #include <string.h>
00027 #include <assert.h>
00028 #ifdef _MSC_VER
00029 # include <io.h>
00030 #endif
00031 
00032 int riff_std_read(void *a,void *b,size_t c) {
00033         riff_stack *rs;
00034         int rd;
00035         
00036         rs = (riff_stack*)a;
00037         if (rs->fd < 0) return -1;
00038         if (rs->trk_file_pointer < (int64_t)0) return -1;
00039         rd = (int)read(rs->fd,b,(unsigned int)c);
00040 
00041         if (rd < 0) rs->trk_file_pointer = -1LL;
00042         else rs->trk_file_pointer += rd;
00043 
00044         return rd;
00045 }
00046 
00047 int riff_std_write(void *a,const void *b,size_t c) {
00048         riff_stack *rs;
00049         int rd;
00050 
00051         rs = (riff_stack*)a;
00052         if (rs->fd < 0) return -1;
00053         if (rs->trk_file_pointer < (int64_t)0) return -1;
00054         rd = (int)write(rs->fd,b,(unsigned int)c);
00055 
00056         if (rd < 0) rs->trk_file_pointer = -1LL;
00057         else rs->trk_file_pointer += rd;
00058 
00059         return rd;
00060 }
00061 
00062 int64_t riff_std_seek(void *a,int64_t offset) {
00063         riff_stack *rs = (riff_stack*)a;
00064         if (rs->fd < 0) return -1;
00065         if (!rs->always_lseek && offset == rs->trk_file_pointer) return offset;
00066 #if defined(_MSC_VER)
00067         rs->trk_file_pointer = _lseeki64(rs->fd, offset, SEEK_SET);
00068 #else
00069         rs->trk_file_pointer = lseek(rs->fd, offset, SEEK_SET);
00070 #endif
00071         return rs->trk_file_pointer;
00072 }
00073 
00074 int riff_buf_read(void *a,void *b,size_t c) {
00075         riff_stack *rs = (riff_stack*)a;
00076         if (rs->buffer == NULL) return -1;
00077         if (rs->bufofs >= rs->buflen) return 0;
00078         if ((rs->bufofs+c) > rs->buflen) c = (size_t)(rs->buflen - rs->bufofs);
00079         memcpy(b,(char*)rs->buffer+rs->bufofs,c);
00080         rs->bufofs += (size_t)c;
00081         return (int)c;
00082 }
00083 
00084 int riff_buf_write(void *a,const void *b,size_t c) {
00085         riff_stack *rs = (riff_stack*)a;
00086         if (rs->buffer == NULL) return -1;
00087         if (rs->bufofs >= rs->buflen) return 0;
00088         if ((rs->bufofs+c) > rs->buflen) c = (size_t)(rs->buflen - rs->bufofs);
00089         memcpy((char*)rs->buffer+rs->bufofs,b,c);
00090         rs->bufofs += (size_t)c;
00091         return (int)c;
00092 }
00093 
00094 int64_t riff_buf_seek(void *a,int64_t offset) {
00095         riff_stack *rs = (riff_stack*)a;
00096         if (rs->buffer == NULL) return -1;
00097         if (offset < 0LL) offset = 0LL;
00098         else if (offset > (int64_t)(rs->buflen)) offset = (int64_t)(rs->buflen);
00099         rs->bufofs = (size_t)offset;
00100         return (int64_t)rs->bufofs;
00101 }
00102 
00103 /* NTS: By design, this code focuses only on closing the file descriptor.
00104  *      If you forgot to write sync headers or pop all off the stack,
00105  *      then the incomplete and invalid RIFF structure is your problem. */
00106 void riff_stack_close_source(riff_stack *s) {
00107         if (s->fd >= 0 && s->fd_owner) {
00108                 close(s->fd);
00109                 s->fd = -1;
00110         }
00111 }
00112 
00113 /* NTS: By default, does NOT take ownership of the file descriptor.
00114  *      If you want the RIFF stack to take ownership, and automatically
00115  *      free the descriptor on destruction/close, then you would call
00116  *      this function then call riff_stack_assign_fd_ownershop() */
00117 int riff_stack_assign_fd(riff_stack *s,int fd) {
00118         if (fd != s->fd) {
00119                 riff_stack_close_source(s);
00120                 s->fd_owner = 0;
00121                 s->fd = fd;
00122         }
00123         s->buffer = NULL;
00124         s->read = riff_std_read;
00125         s->seek = riff_std_seek;
00126         s->write = riff_std_write;
00127         s->trk_file_pointer = -1LL;
00128         return 1;
00129 }
00130 
00131 int riff_stack_assign_fd_ownership(riff_stack *s) {
00132         s->fd_owner = 1;
00133         return 1;
00134 }
00135 
00136 int riff_stack_assign_buffer(riff_stack *s,void *buffer,size_t len) {
00137         s->fd = -1;
00138         s->buflen = len;
00139         s->buffer = buffer;
00140         s->bufofs = (size_t)(0UL);
00141         s->read = riff_buf_read;
00142         s->seek = riff_buf_seek;
00143         s->write = riff_buf_write;
00144         s->trk_file_pointer = -1LL;
00145         return 1;
00146 }
00147 
00148 riff_stack *riff_stack_create(int depth) {
00149         riff_stack *s = (riff_stack*)malloc(sizeof(riff_stack));
00150         if (!s) return NULL;
00151         memset(s,0,sizeof(*s));
00152 
00153         if (depth == 0) depth = 32;
00154         else if (depth < 16) depth = 16;
00155         else if (depth > 512) depth = 512;
00156 
00157         s->fd = -1;
00158         s->current = -1;
00159         s->depth = depth;
00160         s->stack = (riff_chunk*)malloc(sizeof(riff_chunk)*(unsigned int)depth);
00161         if (!s->stack) {
00162                 free(s);
00163                 return NULL;
00164         }
00165         memset(s->stack,0,sizeof(riff_chunk)*(unsigned int)depth);
00166         return s;
00167 }
00168 
00169 riff_stack *riff_stack_destroy(riff_stack *s) {
00170         if (s) {
00171                 riff_stack_close_source(s);
00172                 if (s->stack) free(s->stack);
00173                 free(s);
00174         }
00175         return NULL;
00176 }
00177 
00178 int riff_stack_is_empty(riff_stack *s) {
00179         return (s->current == -1);
00180 }
00181 
00182 int riff_stack_empty(riff_stack *s) {
00183         s->current = -1;
00184         s->next_read = 0;
00185         s->next_write = 0;
00186         s->top = NULL;
00187         s->eof = 0;
00188         return 1;
00189 }
00190 
00191 riff_chunk *riff_stack_top(riff_stack *s) {
00192         if (!s) return NULL;
00193         if (s->current == -1) return NULL;
00194         return (s->top = (s->stack + s->current));
00195 }
00196 
00197 riff_chunk *riff_stack_pop(riff_stack *s) {
00198         riff_chunk *pc,*c;
00199 
00200         if (!s) return NULL;
00201         if (s->current == -1) return NULL;
00202 
00203         c = s->top;
00204         s->current--;
00205         if (s->current == -1) {
00206                 if (s->wmode) {
00207                         if (!c->disable_sync) riff_stack_header_sync(s,c);
00208                         if (c->data_length < (int64_t)c->write_offset)
00209                                 c->data_length = (uint32_t)c->write_offset;
00210 
00211                         c->absolute_data_length = (c->data_length + 1) & (~1UL);
00212                         s->next_write = c->absolute_data_offset + c->absolute_data_length;
00213                 }
00214                 s->top = NULL;
00215                 return NULL;
00216         }
00217         s->top = (s->stack + s->current);
00218 
00219         /* if we're writing a RIFF structure, we need to make sure
00220          * the parent chunk's data offsets are properly set up.
00221          * the above conditions ensure we don't get here unless
00222          * there was a parent chunk involved */
00223         if (s->wmode) {
00224                 unsigned char no_sync = c->disable_sync;
00225 
00226                 if (c->placeholder) {
00227                         c->placeholder = 0; /* caller pops a chunk off the stack to complete it, therefore the placeholder must be rewritten */
00228                         c->disable_sync = 0; /* it MUST be rewritten, disabling sync is no longer an option */
00229                         no_sync = 0; /* <--- ditto */
00230                 }
00231 
00232                 if (!no_sync) riff_stack_header_sync(s,c);
00233                 pc = s->top;
00234                 while (pc && c) {
00235                         if ((c->absolute_data_offset + c->absolute_data_length) >
00236                                 (pc->absolute_data_offset + pc->data_length)) {
00237                                 pc->data_length = (uint32_t)((c->absolute_data_offset + c->absolute_data_length) -
00238                                         pc->absolute_data_offset);
00239                                 pc->absolute_data_length = pc->data_length; /* FIXME: right? */
00240                         }
00241 
00242                         if (c->absolute_data_offset >= pc->absolute_data_offset) {
00243                                 int64_t limit = c->absolute_data_offset + c->absolute_data_length -
00244                                         pc->absolute_data_offset;
00245 
00246                                 if (pc->write_offset < limit)
00247                                         pc->write_offset = limit;
00248                         }
00249 
00250                         if (pc == s->stack)
00251                                 break;
00252 
00253                         c = pc--; /* NTS: remember pc = s->top = s->stack + <index> */
00254                 }
00255                 if (!no_sync) riff_stack_header_sync(s,s->top);
00256         }
00257 
00258         return s->top;
00259 }
00260 
00261 int riff_stack_push(riff_stack *s,riff_chunk *c) {
00262         riff_chunk *to;
00263         if (s == NULL || c == NULL) return 0;
00264         if (s->current < -1) return 0;
00265         if ((s->current+1) >= s->depth) return 0;
00266         to = s->stack + (++s->current);
00267         memcpy(to,c,sizeof(riff_chunk));
00268         s->top = to;
00269         return 1;
00270 }
00271 
00272 int64_t riff_stack_current_chunk_offset(riff_stack *s) {
00273         int64_t o;
00274         if (s == NULL) return -1LL;
00275         if (s->top->read_offset > (s->top->data_length-8LL)) return -1LL;
00276         o = s->top->absolute_data_offset+s->top->read_offset;
00277         return o;
00278 }
00279 
00280 int64_t riff_stack_seek(riff_stack *s,riff_chunk *c,int64_t of) {
00281         if (c) {
00282                 if (of > c->data_length) of = c->data_length;
00283                 else if (of < 0) of = 0;
00284                 c->read_offset = c->write_offset = of;
00285         }
00286         else {
00287                 if (of < 0) of = 0;
00288                 s->next_write = s->next_read = of;
00289         }
00290 
00291         return of;
00292 }
00293 
00294 int riff_stack_streamwrite(riff_stack *s,riff_chunk *c,const void *buf,size_t len) {
00295         if (s->write == NULL)
00296                 return -1;
00297 
00298         if (c) {
00299                 if (!c->wmode)
00300                         return -1;
00301 
00302                 /* once the data is written, you are not allowed to write any more */
00303                 if (c->write_offset != 0)
00304                         return -1;
00305 
00306                 /* AVI chunks are limited to 2GB or less */
00307                 if (((unsigned long long)c->write_offset+len) >= 0x80000000ULL)
00308                         return -1;
00309 
00310                 /* assume the write will complete and setup pointers now */
00311                 c->read_offset = c->write_offset = (int64_t)len;
00312                 c->data_length = (uint32_t)c->write_offset;
00313                 c->absolute_data_length = (c->data_length + 1UL) & (~1UL);
00314 
00315                 /* write the header NOW */
00316                 riff_stack_header_sync(s,c);
00317 
00318                 /* then write the data.
00319                  * the consequence of this design is that unlike riff_stack_write() the
00320                  * call is considered an absolute failure IF we were not able to write all
00321                  * the data to disk. We have to, the design of this code bets on it!
00322                  *
00323                  * NTS: We allow the caller to streamwrite NULL-length packets with buf=NULL and len=0 */
00324                 if (buf != NULL) {
00325                         int rd;
00326 
00327                         if (s->seek(s,c->absolute_data_offset) != c->absolute_data_offset) return 0;
00328                         rd = s->write(s,buf,len);
00329 
00330                         /* if we were not able to write all data, well, too bad. update the header */
00331                         if (rd < (int)len) {
00332                                 if (rd < 0) rd = 0;
00333                                 c->read_offset = c->write_offset = rd;
00334                                 c->data_length = (uint32_t)c->write_offset;
00335                                 c->absolute_data_length = (c->data_length + 1UL) & (~1UL);
00336 
00337                                 /* write the header NOW */
00338                                 riff_stack_header_sync(s,c);
00339 
00340                                 /* fail */
00341                                 return -1;
00342                         }
00343                 }
00344 
00345                 /* we already wrote the header, and the caller is SUPPOSED to
00346                  * use this function ONCE for a RIFF chunk. Don't let riff_stack_pop()
00347                  * waste it's time lseek()'ing back to rewrite the header */
00348                 c->disable_sync = 1;
00349         }
00350         else {
00351                 abort(); /* TODO */
00352         }
00353 
00354         return (int)len;
00355 }
00356 
00357 int riff_stack_write(riff_stack *s,riff_chunk *c,const void *buf,size_t len) {
00358         int rd;
00359 
00360         if (s->write == NULL)
00361                 return -1;
00362 
00363         if (c) {
00364                 if (!c->wmode)
00365                         return -1;
00366                 if (c->absolute_data_offset == ((int64_t)(-1)))
00367                         return -1;
00368 
00369                 /* AVI chunks are limited to 2GB or less */
00370                 if (((unsigned long long)c->write_offset+len) >= 0x80000000ULL)
00371                         return -1;
00372 
00373                 if (s->seek(s,c->absolute_data_offset+c->write_offset) !=
00374                         (c->absolute_data_offset+c->write_offset)) return 0;
00375                 rd = s->write(s,buf,len);
00376                 if (rd > 0) c->read_offset = (c->write_offset += rd);
00377                 if (c->data_length < (int64_t)c->write_offset)
00378                         c->data_length = (uint32_t)c->write_offset;
00379                 c->absolute_data_length = (c->data_length + 1UL) & (~1UL);
00380         }
00381         else {
00382                 if (s->seek(s,s->next_write) != s->next_write) return 0;
00383                 rd = s->write(s,buf,len);
00384                 if (rd > 0) s->next_read = (s->next_write += rd);
00385         }
00386 
00387         return rd;
00388 }
00389 
00390 int riff_stack_read(riff_stack *s,riff_chunk *c,void *buf,size_t len) {
00391         int rd;
00392 
00393         if (c) {
00394                 int64_t rem = c->data_length - c->read_offset;
00395                 if (rem > (int64_t)len) rem = (int64_t)len;
00396                 len = (size_t)rem;
00397                 if (len == 0) return 0;
00398                 if (c->absolute_data_offset == ((int64_t)(-1))) return 0;
00399                 if (s->seek(s,c->absolute_data_offset+c->read_offset) != (c->absolute_data_offset+c->read_offset)) return 0;
00400                 rd = s->read(s,buf,len);
00401                 if (rd > 0) c->write_offset = (c->read_offset += rd);
00402         }
00403         else {
00404                 if (s->seek(s,s->next_read) != s->next_read) return 0;
00405                 rd = s->read(s,buf,len);
00406                 if (rd > 0) s->next_write = (s->next_read += rd);
00407         }
00408 
00409         return rd;
00410 }
00411 
00412 /* given a chunk (pc), read a subchunk.
00413  * to read at the top (file) level, set pc == NULL.
00414  * 
00415  * if a subchunk is present, function returns 1 and the subchunk in (c).
00416  * else, (c) is not changed and function returns 0.
00417  *
00418  * The function needs the stack object to read, but does not automatically
00419  * push the chunk to the top of the stack. if you want to enter the
00420  * chunk to examine it, then you must push it to the stack yourself.
00421  *
00422  * this function DOES NOT check for you whether the chunk you are reading
00423  * should have subchunks. YOU are responsible for using
00424  * riff_stack_chunk_contains_subchunks() to determine that first. if you
00425  * use this function on a chunk that contains only data, you will get
00426  * garbage chunks and that's your fault.
00427  *
00428  * Anatomy of an AVI chunk (data only, always little endian):
00429  *
00430  * Offset       size / type     contents
00431  * -------------------------------------
00432  * +0x00 +0     4 / DWORD       FOURCC (4-byte ASCII identifying the chunk)
00433  * +0x04 +4     4 / DWORD       data length (not including header)
00434  * =0x08 +8     8
00435  *
00436  * Anatomy of an AVI chunk (list chunk containing subchunks):
00437  * 
00438  * Offset       size / type     contents
00439  * -------------------------------------
00440  * +0x00 +0     4 / DWORD       FOURCC ("RIFF" or "LIST")
00441  * +0x04 +4     4 / DWORD       data length (not including header but including 4-byte subchunk header that follows)
00442  * +0x08 +8     4 / DWORD       FOURCC (4-byte ASCII identifying the subchunk)
00443  * =0x0C +12    12
00444  * 
00445  * A word of caution about top-level chunks: Most RIFF formats are based on the top level
00446  * containing one major top-level chunk, and most (all) chunks are within that top level
00447  * chunk. If the file extends past 2GB, then the file at the top level is a series of
00448  * very large top-level RIFF chunks. It is very unlikely that you'll ever get a non-list
00449  * chunk at top level, so if this function returns one at toplevel, it's probably unrelated
00450  * junk at the end of the file.
00451  *
00452  * In most cases, your code should simply read the one chunk at the start of the file and
00453  * generally stay within the chunk, unless for example OpenDML AVI 2.0, in which you must
00454  * make sure you read the series of RIFF:AVI and RIFF:AVIX chunks until you reach the end
00455  * of the file or encounter any other chunk. Again, this code will NOT prevent you from
00456  * reading junk at the end of the file as an AVI chunk!
00457  */
00458 int riff_stack_readchunk(riff_stack *s,riff_chunk *pc,riff_chunk *c) {
00459         unsigned char buf[8];
00460 
00461         if (s == NULL) return 0; /* caller must provide (s) stack */
00462         if (c == NULL) return 0; /* caller must provide (c) chunk structure to fill in */
00463         if (s->wmode) return 0; /* no reading chunks while writing RIFF */
00464 
00465         if (pc != NULL) {
00466                 /* if parent chunk provided, read new chunk from parent chunk */
00467                 if (c->absolute_data_offset == ((int64_t)(-1))) return 0;
00468                 if ((pc->read_offset+8) > pc->data_length) return 0;
00469                 if (s->seek(s,pc->absolute_data_offset+pc->read_offset) != (pc->absolute_data_offset+pc->read_offset)) return 0;
00470                 c->absolute_header_offset = pc->absolute_data_offset+pc->read_offset;
00471         }
00472         else {
00473                 /* if parent chunk NOT provided, read from read pointer in file.
00474                  * return now if read pointer has signalled eof.
00475                  * signal eof and return if seek fails. */
00476                 if (s->eof) return 0;
00477                 if (s->seek(s,s->next_read) != s->next_read) {
00478                         s->eof = 1;
00479                         return 0;
00480                 }
00481                 c->absolute_header_offset = s->next_read;
00482         }
00483 
00484         /* read 8 bytes corresponding to the FOURCC and data length.
00485          * failure to read is the end of the chunk.
00486          * signal eof if the failure is at the top level. */
00487         if (s->read(s,buf,8) < 8) {
00488                 if (pc == NULL) s->eof = 1;
00489                 return 0;
00490         }
00491 
00492         c->wmode = 0;
00493         c->read_offset = 0;
00494         c->write_offset = 0;
00495         c->absolute_data_offset = c->absolute_header_offset + 8LL;
00496         c->list_fourcc = (riff_fourcc_t)(0UL);
00497         c->fourcc = __le_u32(buf+0);
00498         c->data_length = __le_u32(buf+4); /* <-- NOTE this is why AVI files have a 2GB limit */
00499         c->absolute_data_length = (c->data_length + 1ULL) & (~1ULL); /* <-- RIFF chunks are WORD aligned */
00500         c->absolute_offset_next_chunk = c->absolute_data_offset + c->absolute_data_length;
00501 
00502         /* consider it the end of the chunks if we read in a NULL fourcc, and mark EOF */
00503         if (c->fourcc == 0UL) {
00504                 if (pc) pc->read_offset = pc->write_offset = pc->data_length;
00505                 else s->eof = 1;
00506                 return 0;
00507         }
00508 
00509         /* some chunks like 'RIFF' or 'LIST' are actually 12 byte headers, with the real FOURCC
00510          * following the length, then subchunks follow in the data area. */
00511         if (c->fourcc == riff_RIFF || c->fourcc == riff_LIST) {
00512                 c->list_fourcc = c->fourcc;
00513                 if (c->data_length >= 4) {
00514                         if (s->read(s,buf,4) < 4) return 0;
00515                         c->fourcc = __le_u32(buf+0);
00516                         c->absolute_data_offset += 4;
00517                         c->absolute_data_length -= 4;
00518                         c->data_length -= 4;
00519                 }
00520                 else {
00521                         memset(buf,0,4);
00522                         if ((int)s->read(s,buf,c->absolute_data_length) < (int)c->absolute_data_length) return 0;
00523                         c->fourcc = __le_u32(buf+0);
00524                         c->absolute_data_offset += c->absolute_data_length;
00525                         c->absolute_data_length = 0;
00526                         c->data_length = 0;
00527                 }
00528         }
00529 
00530         if (pc) pc->read_offset = pc->write_offset = c->absolute_offset_next_chunk - pc->absolute_data_offset;
00531         else    s->next_read = s->next_write = c->absolute_offset_next_chunk;
00532         return 1;
00533 }
00534 
00535 int riff_stack_chunk_contains_subchunks(riff_chunk *c) {
00536         if (c == NULL) return 0;
00537         if (c->list_fourcc == riff_RIFF) return 1;
00538         if (c->list_fourcc == riff_LIST) return 1;
00539         return 0;
00540 }
00541 
00542 void riff_stack_fourcc_to_str(riff_fourcc_t t,char *tmp) {
00543         tmp[0] = (char)((t >>  0) & 0xFF);
00544         tmp[1] = (char)((t >>  8) & 0xFF);
00545         tmp[2] = (char)((t >> 16) & 0xFF);
00546         tmp[3] = (char)((t >> 24) & 0xFF);
00547         tmp[4] = (char)0;
00548 }
00549 
00550 void riff_stack_debug_print_indent(FILE *fp,int level) {
00551         while (level-- > 0) fprintf(fp,"  ");
00552 }
00553 
00554 void riff_stack_debug_print(FILE *fp,int level,riff_chunk *chunk) {
00555         char tmp[8];
00556 
00557         riff_stack_debug_print_indent(fp,level);
00558         fprintf(fp,"[%d] ",level);
00559         if (riff_stack_chunk_contains_subchunks(chunk)) {
00560                 riff_stack_fourcc_to_str(chunk->list_fourcc,tmp);
00561                 fprintf(fp,"'%s:",tmp);
00562                 riff_stack_fourcc_to_str(chunk->fourcc,tmp);
00563                 fprintf(fp,"%s' ",tmp);
00564         }
00565         else {
00566                 riff_stack_fourcc_to_str(chunk->fourcc,tmp);
00567                 fprintf(fp,"'%s' ",tmp);
00568         }
00569         fprintf(fp,"hdr=%llu data=%llu len=%lu data-end=%llu",
00570                 (unsigned long long)(chunk->absolute_header_offset),
00571                 (unsigned long long)(chunk->absolute_data_offset),
00572                 (unsigned long)(chunk->data_length),
00573                 (unsigned long long)(chunk->absolute_data_offset+chunk->data_length));
00574         fprintf(fp,"\n");
00575 }
00576 
00577 void riff_stack_debug_chunk_dump(FILE *fp,riff_stack *riff,riff_chunk *chunk) {
00578         unsigned char tmp[64];
00579         int rd,i,col=0,o;
00580 
00581         if (riff_stack_seek(riff,chunk,0LL) != 0LL)
00582                 return;
00583 
00584         rd = (int)riff_stack_read(riff,chunk,tmp,sizeof(tmp));
00585         for (i=0;i < ((rd+15)&(~15));) {
00586                 if (col == 0) fprintf(fp,"        ");
00587 
00588                 col++;
00589                 if (i < rd)     fprintf(fp,"%02X ",tmp[i]);
00590                 else            fprintf(fp,"   ");
00591                 i++;
00592 
00593                 if (col >= 16 || i == ((rd+15)&(~15))) {
00594                         for (col=0;col < 16;col++) {
00595                                 o = i+col-16;
00596                                 if (o >= rd)
00597                                         fwrite(" ",1,1,fp);
00598                                 else if (tmp[o] >= 32 && tmp[o] < 127)
00599                                         fwrite(tmp+o,1,1,fp);
00600                                 else
00601                                         fwrite(".",1,1,fp);
00602                         }
00603 
00604                         col = 0;
00605                         fprintf(fp,"\n");
00606                 }
00607         }
00608 }
00609 
00610 int riff_stack_eof(riff_stack *r) {
00611         if (r == NULL) return 0;
00612         if (r->current == -1) return r->eof;
00613         return 0;
00614 }
00615 
00616 void riff_chunk_improvise(riff_chunk *c,uint64_t ofs,uint32_t size) {
00617         c->absolute_header_offset = (int64_t)ofs;
00618         c->absolute_data_offset = (int64_t)ofs;
00619         c->absolute_offset_next_chunk = (int64_t)(ofs + size);
00620         c->fourcc = 0U;
00621         c->data_length = size;
00622         c->absolute_data_length = size;
00623         c->list_fourcc = 0U;
00624         c->read_offset = 0ULL;
00625         c->write_offset = 0ULL;
00626         c->wmode = 0;
00627 }
00628 
00629 int riff_stack_prepare_for_writing(riff_stack *r,int wmode) {
00630         if (r == NULL) return 0;
00631         if (r->wmode == wmode) return 1;
00632 
00633         /* no state changes while anything is stacked on */
00634         if (r->current >= 0) return 0;
00635 
00636         /* no state changes while reading an AVI chunk */
00637         if (r->next_read != 0LL && !r->eof) return 0;
00638 
00639         r->wmode = wmode > 0 ? 1 : 0;
00640         return 1;
00641 }
00642 
00643 int riff_stack_begin_new_chunk_here(riff_stack *s,riff_chunk *c) {
00644         riff_chunk *p = riff_stack_top(s);
00645 
00646         /* you can't begin new chunks if you're not writing the file */
00647         if (!s->wmode)
00648                 return 0;
00649 
00650         /* sanity check.
00651          * if we're currently in a subchunk, make sure the subchunk allows for subchunks within.
00652          * you can't just put chunks inside data. */
00653         if (p != NULL) {
00654                 if (!riff_stack_chunk_contains_subchunks(p)) {
00655                         fprintf(stderr,"BUG: riff_stack_begin_new_chunk_here() caller attempting to start new RIFF chunks inside a chunk that does not contain subchunks\n");
00656                         return 0;
00657                 }
00658         }
00659 
00660         /* zero the chunk and pick the header offset.
00661          * for debugging purposes, we do not assign the data offset until the
00662          * host program calls set_chunk_list_type/set_chunk_data_type.
00663          * the chunk itself must also remember that it's in write mode. */
00664         memset(c,0,sizeof(*c));
00665         if (p != NULL)
00666                 c->absolute_header_offset = p->absolute_data_offset + p->write_offset;
00667         else
00668                 c->absolute_header_offset = s->next_write;
00669 
00670         c->absolute_data_offset = (int64_t)(-1LL);
00671         c->wmode = 1;
00672         return 1;
00673 }
00674 
00675 int riff_stack_header_sync(riff_stack *s,riff_chunk *c) {
00676         unsigned char tmp[12];
00677 
00678         if (!c->wmode || !s->wmode)
00679                 return 0;
00680 
00681         if (s->seek(s,c->absolute_header_offset) != c->absolute_header_offset)
00682                 return 0;
00683 
00684         /* NTS: A placeholder value of 0xFFFFFFFF would technically violate the AVI standard
00685          *      because it is well known older programs treat the length as signed. We use
00686          *      0x7FFFFFFF instead. */
00687         if (c->list_fourcc != (riff_fourcc_t)0) {
00688                 __w_le_u32(tmp+0,c->list_fourcc);
00689                 if (c->placeholder)
00690                         __w_le_u32(tmp+4,0x7FFFFFFFUL);
00691                 else
00692                         __w_le_u32(tmp+4,c->data_length + 4UL); /* for some reason the length covers the fourcc */
00693                 __w_le_u32(tmp+8,c->fourcc);
00694                 if (s->write(s,tmp,12) < 12)
00695                         return 0;
00696         }
00697         else {
00698                 __w_le_u32(tmp+0,c->fourcc);
00699                 if (c->placeholder)
00700                         __w_le_u32(tmp+4,0x7FFFFFFFUL);
00701                 else
00702                         __w_le_u32(tmp+4,c->data_length);
00703                 if (s->write(s,tmp,8) < 8)
00704                         return 0;
00705         }
00706 
00707         return 1;
00708 }
00709 
00710 int riff_stack_header_sync_all(riff_stack *s) {
00711         int i = s->current;
00712 
00713         while (i >= 0) {
00714                 riff_chunk *c = s->stack + (i--);
00715                 if (!riff_stack_header_sync(s,c))
00716                         return 0;
00717         }
00718 
00719         return 1;
00720 }
00721 
00722 /* start a data chunk (no subchunks) with FOURCC (fcc) */
00723 int riff_stack_set_chunk_data_type(riff_chunk *c,riff_fourcc_t fcc) {
00724         if (!c->wmode)
00725                 return 0;
00726         if (c->write_offset != 0LL) {
00727                 fprintf(stderr,"BUG: riff_stack_set_chunk_data_type() caller attempted to set type after writing data!\n");
00728                 return 0;
00729         }
00730 
00731         c->fourcc = fcc;
00732         c->list_fourcc = (riff_fourcc_t)0;
00733         c->absolute_data_offset = c->absolute_header_offset + 8LL; /* <fourcc> <len> */
00734         return 1;
00735 }
00736 
00737 /* start a list chunk (with subchunks) with list type (list) usually "RIFF" and "LIST" and FOURCC (fcc).
00738  * For example: RIFF:AVI would be list = "RIFF" fcc = "AVI " */
00739 int riff_stack_set_chunk_list_type(riff_chunk *c,riff_fourcc_t list,riff_fourcc_t fcc) {
00740         if (!c->wmode)
00741                 return 0;
00742         if (c->write_offset != 0LL) {
00743                 fprintf(stderr,"BUG: riff_stack_set_chunk_list_type() caller attempted to set type after writing data!\n");
00744                 return 0;
00745         }
00746 
00747         c->fourcc = fcc;
00748         c->list_fourcc = list;
00749         c->absolute_data_offset = c->absolute_header_offset + 12LL; /* RIFF <len> <fourcc> */
00750         return 1;
00751 }
00752 
00753 void riff_stack_writing_sync(riff_stack *s) {
00754         int64_t noffset = 0;
00755 
00756         while (s->current >= 0) {
00757                 /* the caller uses this function when all chunks are to be completed,
00758                  * and the writing process may or may not be done. the AVI writer
00759                  * may uses this for instance when writing the next 'AVIX' chunk
00760                  * in an OpenDML file which requires all chunks to be completed.
00761                  *
00762                  * as part of the process we must clear all disable_sync and
00763                  * placeholder markings so the true state can be written back */
00764                 riff_chunk *t = riff_stack_top(s);
00765                 t->disable_sync = 0;
00766                 t->placeholder = 0;
00767                 riff_stack_header_sync_all(s);
00768                 assert(s->top->absolute_data_offset >= (int64_t)0);
00769                 int64_t x = s->top->absolute_data_offset + s->top->absolute_data_length;
00770                 if (noffset < x) noffset = x;
00771                 riff_stack_pop(s);
00772         }
00773 
00774         s->next_write = noffset;
00775 }
00776 
00777 /* if I wrote "len" bytes, would I hit or cross the AVI 2GB chunk limit at any level? */
00778 /* if so, the caller must pop down all AVI chunks, emptying the stack, and then create
00779  * new chunks to continue on */
00780 int riff_stack_chunk_limit(riff_stack *s,int len) {
00781         riff_chunk *lowest,*highest;
00782         unsigned long long base;
00783 
00784         if (s->current < 0) return 0;
00785 
00786         lowest = s->stack;
00787         assert(lowest->absolute_data_offset >= (int64_t)0);
00788         highest = riff_stack_top(s);
00789         assert(highest != NULL);
00790         assert(highest->absolute_data_offset >= (int64_t)0);
00791         base = (unsigned long long)lowest->absolute_data_offset;
00792 
00793         for (;highest >= lowest;highest--) {
00794                 signed long long rel = (signed long long)((unsigned long long)highest->absolute_data_offset - base);
00795                 /* subchunks should not be all over the place---sanity check! */
00796                 assert(rel >= 0);
00797 
00798                 if ((rel + highest->write_offset + len) >= 0x40000000LL) /* 1GB mark */
00799                         return 1;
00800         }
00801 
00802         return 0;
00803 }
00804 
00805 int riff_stack_enable_placeholder(riff_stack *s,riff_chunk *c) {
00806         if (s == NULL) return 0;
00807         if (c == NULL) return 0;
00808         c->placeholder = 1;
00809         return 1;
00810 }
00811