winamp/Src/nsavi/ParserBase.cpp
2024-09-24 14:54:57 +02:00

493 lines
12 KiB
C++

#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
}