#include "ParserBase.h" nsavi::ParserBase::ParserBase(nsavi::avi_reader *_reader) { reader = _reader; riff_parsed = NOT_READ; header_list_parsed = NOT_READ; riff_start = 0; avi_header = 0; stream_list = 0; stream_list_size = 0; odml_header = 0; } // reads a chunk and updates parse state variable on error static int ReadChunk(nsavi::avi_reader *reader, nsavi::riff_chunk *chunk, nsavi::ParseState &state, uint32_t *bytes_read) { int ret = nsavi::read_riff_chunk(reader, chunk, bytes_read); if (ret == nsavi::READ_EOF) { state = nsavi::NOT_FOUND; return nsavi::READ_NOT_FOUND; } else if (ret > nsavi::READ_OK) { state = nsavi::PARSE_ERROR; return ret; } else if (ret < nsavi::READ_OK) { // pass-thru return value from avi_reader state = nsavi::PARSE_RESYNC; return ret; } return nsavi::READ_OK; } int nsavi::ParserBase::GetRIFFType(uint32_t *type) { if (riff_parsed == PARSE_RESYNC) reader->Seek(0); // seek to the beginning if we need to if (riff_parsed == NOT_READ) { uint32_t bytes_read; // assume we are at the beginning of the file int ret = ReadChunk(reader, &riff_header, riff_parsed, &bytes_read); if (ret) return ret; if (!riff_header.type) { riff_parsed = PARSE_ERROR; return READ_INVALID_DATA; } riff_start = reader->Tell(); riff_parsed = PARSED; } if (riff_parsed == PARSED) { *type = riff_header.type; return READ_OK; } // we'll only get here if GetRIFFType was called a second time after an initial failure return READ_INVALID_CALL; } // skips a chunk and updates a parser state variable on error static int SkipChunk(nsavi::avi_reader *reader, const nsavi::riff_chunk *chunk, nsavi::ParseState &state, uint32_t *bytes_read) { int ret = nsavi::skip_chunk(reader, chunk, bytes_read); if (ret == nsavi::READ_EOF) { state = nsavi::NOT_FOUND; return nsavi::READ_NOT_FOUND; } else if (ret > nsavi::READ_OK) { state = nsavi::PARSE_ERROR; return ret; } else if (ret < nsavi::READ_OK) { // pass-thru return value from avi_reader state = nsavi::PARSE_RESYNC; return ret; } return nsavi::READ_OK; } static int Read(nsavi::avi_reader *reader, void *buffer, uint32_t size, nsavi::ParseState &state, uint32_t *out_bytes_read) { uint32_t bytes_read; int ret = reader->Read(buffer, size, &bytes_read); if (ret > nsavi::READ_OK) { state = nsavi::PARSE_ERROR; return ret; } else if (ret < nsavi::READ_OK) { // pass-thru return value from avi_reader state = nsavi::PARSE_RESYNC; return ret; } else if (bytes_read != size) { state = nsavi::PARSE_ERROR; return nsavi::READ_EOF; } *out_bytes_read = bytes_read; return nsavi::READ_OK; } int nsavi::ParserBase::ParseStreamList(uint32_t chunk_size, STRL *stream, uint32_t *out_bytes_read) { uint32_t bytes_available = chunk_size; uint32_t stream_number = 0; while (bytes_available) { if (bytes_available < 8) { header_list_parsed = PARSE_ERROR; return READ_INVALID_DATA; } uint32_t bytes_read; riff_chunk chunk; int ret = ReadChunk(reader, &chunk, header_list_parsed, &bytes_read); if (ret) return ret; bytes_available -= bytes_read; if (bytes_available < chunk.size) { header_list_parsed = PARSE_ERROR; return READ_INVALID_DATA; } switch(chunk.id) { case 'hrts': // strh free(stream->stream_header); stream->stream_header = (nsavi::STRH *)malloc(chunk.size + sizeof(uint32_t)); if (stream->stream_header) { ret = Read(reader, ((uint8_t *)stream->stream_header) + sizeof(uint32_t), chunk.size, header_list_parsed, &bytes_read); if (ret) return ret; bytes_available-=bytes_read; stream->stream_header->size_bytes = chunk.size; if ((chunk.size & 1) && bytes_available) { bytes_available--; reader->Skip(1); } } else { return READ_OUT_OF_MEMORY; } break; case 'frts': // strf free(stream->stream_format); stream->stream_format = (nsavi::STRF *)malloc(chunk.size + sizeof(uint32_t)); if (stream->stream_format) { ret = Read(reader, ((uint8_t *)stream->stream_format) + sizeof(uint32_t), chunk.size, header_list_parsed, &bytes_read); if (ret) return ret; bytes_available-=bytes_read; stream->stream_format->size_bytes = chunk.size; if ((chunk.size & 1) && bytes_available) { bytes_available--; reader->Skip(1); } } else { return READ_OUT_OF_MEMORY; } break; case 'drts': // strd free(stream->stream_data); stream->stream_data = (nsavi::STRD *)malloc(chunk.size + sizeof(uint32_t)); if (stream->stream_data) { ret = Read(reader, ((uint8_t *)stream->stream_data) + sizeof(uint32_t), chunk.size, header_list_parsed, &bytes_read); if (ret) return ret; bytes_available-=bytes_read; stream->stream_data->size_bytes = chunk.size; if ((chunk.size & 1) && bytes_available) { bytes_available--; reader->Skip(1); } } else { return READ_OUT_OF_MEMORY; } break; case 'nrts': // strn free(stream->stream_name); stream->stream_name = (nsavi::STRN *)malloc(chunk.size + sizeof(uint32_t)); if (stream->stream_name) { ret = Read(reader, ((uint8_t *)stream->stream_name) + sizeof(uint32_t), chunk.size, header_list_parsed, &bytes_read); if (ret) return ret; bytes_available-=bytes_read; stream->stream_name->size_bytes = chunk.size; if ((chunk.size & 1) && bytes_available) { bytes_available--; reader->Skip(1); } } else { return READ_OUT_OF_MEMORY; } break; case 'xdni': // indx free(stream->stream_index); stream->stream_index = (nsavi::INDX *)malloc(chunk.size + sizeof(uint32_t)); if (stream->stream_index) { ret = Read(reader, &stream->stream_index->entry_size, chunk.size, header_list_parsed, &bytes_read); if (ret) return ret; bytes_available-=bytes_read; stream->stream_index->size_bytes = chunk.size; if ((chunk.size & 1) && bytes_available) { bytes_available--; reader->Skip(1); } } else { return READ_OUT_OF_MEMORY; } break; case nsaviFOURCC('v','p','r','p'): free(stream->video_properties); stream->video_properties = (nsavi::VPRP *)malloc(chunk.size + sizeof(uint32_t)); if (stream->video_properties) { ret = Read(reader, &stream->video_properties->video_format_token, chunk.size, header_list_parsed, &bytes_read); if (ret) return ret; bytes_available-=bytes_read; stream->video_properties->size_bytes = chunk.size; if ((chunk.size & 1) && bytes_available) { bytes_available--; reader->Skip(1); } } else { return READ_OUT_OF_MEMORY; } break; default: ret = SkipChunk(reader, &chunk, header_list_parsed, &bytes_read); if (ret) return ret; bytes_available -= bytes_read; break; } } if ((chunk_size & 1) && bytes_available) { bytes_available--; reader->Skip(1); } *out_bytes_read = chunk_size - bytes_available; // TODO: see what we managed to collect and return an error code accordingly return READ_OK; } int nsavi::ParserBase::ParseODML(uint32_t chunk_size, uint32_t *out_bytes_read) { uint32_t bytes_available = chunk_size; uint32_t stream_number = 0; while (bytes_available) { if (bytes_available < 8) { header_list_parsed = PARSE_ERROR; return READ_INVALID_DATA; } uint32_t bytes_read; riff_chunk chunk; int ret = ReadChunk(reader, &chunk, header_list_parsed, &bytes_read); if (ret) return ret; bytes_available -= bytes_read; if (bytes_available < chunk.size) { header_list_parsed = PARSE_ERROR; return READ_INVALID_DATA; } switch(chunk.id) { case 'hlmd': // dmlh free(odml_header); odml_header = (nsavi::DMLH *)malloc(chunk.size + sizeof(uint32_t)); if (odml_header) { ret = Read(reader, ((uint8_t *)odml_header) + sizeof(uint32_t), chunk.size, header_list_parsed, &bytes_read); if (ret) return ret; bytes_available-=bytes_read; odml_header->size_bytes = chunk.size; if ((chunk.size & 1) && bytes_available) { bytes_available--; reader->Skip(1); } } else { return READ_OUT_OF_MEMORY; } break; default: ret = SkipChunk(reader, &chunk, header_list_parsed, &bytes_read); if (ret) return ret; bytes_available -= bytes_read; break; } } if ((chunk_size & 1) && bytes_available) { bytes_available--; reader->Skip(1); } *out_bytes_read = chunk_size - bytes_available; // TODO: see what we managed to collect and return an error code accordingly return READ_OK; } int nsavi::ParserBase::ParseHeaderList(uint32_t chunk_size, uint32_t *out_bytes_read) { chunk_size = (chunk_size+1) & ~1; uint32_t bytes_available = chunk_size; uint32_t stream_number = 0; while (bytes_available) { if (bytes_available < 8) { header_list_parsed = NOT_FOUND; return READ_NOT_FOUND; } uint32_t bytes_read; riff_chunk chunk; int ret = ReadChunk(reader, &chunk, header_list_parsed, &bytes_read); if (ret) return ret; bytes_available -= bytes_read; if (bytes_available < chunk.size) { header_list_parsed = PARSE_ERROR; return READ_INVALID_DATA; } switch(chunk.id) { case 'hiva': // avih free(avi_header); avi_header = (nsavi::AVIH *)malloc(chunk.size + sizeof(uint32_t)); if (avi_header) { ret = Read(reader, ((uint8_t *)avi_header) + sizeof(uint32_t), chunk.size, header_list_parsed, &bytes_read); if (ret) return ret; bytes_available-=bytes_read; avi_header->size_bytes = chunk.size; if ((chunk.size & 1) && bytes_available) { bytes_available--; reader->Skip(1); } if (avi_header->streams && !stream_list) { // if we fail to allocate, no major worry (maybe avi_header->streams was incorrect and some huge value // we'll just dynamically allocate as needed stream_list_size = 0; if (avi_header->streams < 65536) /* set a reasonable upper bound */ { stream_list = (STRL *)calloc(avi_header->streams, sizeof(STRL)); if (stream_list) { stream_list_size = avi_header->streams; } } } } else { header_list_parsed = PARSE_ERROR; return READ_OUT_OF_MEMORY; } break; case 'TSIL': switch(chunk.type) { case 'lrts': { if (stream_list_size <= stream_number) { stream_list = (STRL *)realloc(stream_list, (stream_number+1) * sizeof(STRL)); if (!stream_list) { header_list_parsed = PARSE_ERROR; return READ_OUT_OF_MEMORY; } stream_list_size = stream_number+1; } STRL &stream = stream_list[stream_number]; memset(&stream, 0, sizeof(STRL)); ret = ParseStreamList(chunk.size, &stream, &bytes_read); if (ret) return ret; stream_number++; bytes_available-=bytes_read; if ((chunk.size & 1) && bytes_available) { bytes_available--; reader->Skip(1); } } break; case 'lmdo': ret = ParseODML(chunk.size, &bytes_read); if (ret) return ret; bytes_available -= bytes_read; break; default: ret = SkipChunk(reader, &chunk, header_list_parsed, &bytes_read); if (ret) return ret; bytes_available -= bytes_read; break; } break; default: ret = SkipChunk(reader, &chunk, header_list_parsed, &bytes_read); if (ret) return ret; bytes_available -= bytes_read; break; } } if ((chunk_size & 1) && bytes_available) { bytes_available--; reader->Skip(1); } stream_list_size = stream_number; *out_bytes_read = chunk_size - bytes_available; return READ_OK; // TODO: see what we managed to collect and return an error code accordingly }