aboutsummaryrefslogtreecommitdiff
path: root/Src/nsavi/ParserBase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/nsavi/ParserBase.cpp')
-rw-r--r--Src/nsavi/ParserBase.cpp493
1 files changed, 493 insertions, 0 deletions
diff --git a/Src/nsavi/ParserBase.cpp b/Src/nsavi/ParserBase.cpp
new file mode 100644
index 00000000..af1fb8b1
--- /dev/null
+++ b/Src/nsavi/ParserBase.cpp
@@ -0,0 +1,493 @@
+#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
+}