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