#include "vorbis_custom_decoder.h" #ifdef VGM_USE_VORBIS #include /* **************************************************************************** */ /* DEFS */ /* **************************************************************************** */ static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size); static int build_header_comment(uint8_t * buf, size_t bufsize); /* **************************************************************************** */ /* EXTERNAL API */ /* **************************************************************************** */ /** * VID1 removes the Ogg layer and uses a block layout with custom packet headers. * * Info from hcs's vid1_2ogg: https://github.com/hcs64/vgm_ripping/tree/master/demux/vid1_2ogg */ int vorbis_custom_setup_init_vid1(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) { off_t offset = start_offset; size_t packet_size = 0; /* read header packets (id/setup), each with an VID1 header */ /* normal identificacion packet */ get_packet_header(streamFile, &offset, &packet_size); if (packet_size > data->buffer_size) goto fail; data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile); if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */ offset += packet_size; /* generate comment packet */ data->op.bytes = build_header_comment(data->buffer, data->buffer_size); if (!data->op.bytes) goto fail; if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */ /* normal setup packet */ get_packet_header(streamFile, &offset, &packet_size); if (packet_size > data->buffer_size) goto fail; data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile); if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */ offset += packet_size; return 1; fail: return 0; } int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) { size_t bytes; /* test block start */ if (read_32bitBE(stream->offset + 0x00,stream->streamfile) == 0x4652414D) { /* "FRAM" */ stream->offset += 0x20; if (read_32bitBE(stream->offset + 0x00,stream->streamfile) == 0x56494444) { /* "VIDD"*/ stream->offset += read_32bitBE(stream->offset + 0x04, stream->streamfile); } if (read_32bitBE(stream->offset + 0x00,stream->streamfile) == 0x41554444) { /* "AUDD" */ data->block_offset = stream->offset; data->block_size = read_32bitBE(stream->offset + 0x0c,stream->streamfile); stream->offset += 0x14; /* actual start, rest is chunk sizes and maybe granule info */ } } /* get packet info the VID1 header */ get_packet_header(stream->streamfile, &stream->offset, (uint32_t*)&data->op.bytes); if (data->op.bytes == 0 || data->op.bytes > data->buffer_size) goto fail; /* EOF or end padding */ /* read raw block */ bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile); stream->offset += data->op.bytes; if (bytes != data->op.bytes) goto fail; /* wrong packet? */ //todo: sometimes there are short packets like 01be590000 and Vorbis complains and skips, no idea /* test block end (weird size calc but seems ok) */ if ((stream->offset - (data->block_offset + 0x14)) >= (data->block_size - 0x06)) { stream->offset = data->block_offset + read_32bitBE(data->block_offset + 0x04,stream->streamfile); } return 1; fail: return 0; } /* **************************************************************************** */ /* INTERNAL HELPERS */ /* **************************************************************************** */ static int build_header_comment(uint8_t * buf, size_t bufsize) { int bytes = 0x19; if (bytes > bufsize) return 0; put_8bit (buf+0x00, 0x03); /* packet_type (comments) */ memcpy (buf+0x01, "vorbis", 6); /* id */ put_32bitLE(buf+0x07, 0x09); /* vendor_length */ memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */ put_32bitLE(buf+0x14, 0x00); /* user_comment_list_length */ put_8bit (buf+0x18, 0x01); /* framing_flag (fixed) */ return bytes; } /* read header in Vorbis bitpacking format */ static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size) { uint8_t ibuf[0x04]; /* header buffer */ size_t ibufsize = 0x04; /* header ~max */ vgm_bitstream ib = {0}; uint32_t size_bits; if (read_streamfile(ibuf,(*offset),ibufsize, streamFile) != ibufsize) goto fail; ib.buf = ibuf; ib.bufsize = ibufsize; ib.b_off = 0; ib.mode = BITSTREAM_VORBIS; /* read using Vorbis weird LSF */ r_bits(&ib, 4,&size_bits); r_bits(&ib, (size_bits+1),(uint32_t*)size); /* special meaning, seen in silent frames */ if (size_bits == 0 && *size == 0 && (uint8_t)read_8bit(*offset, streamFile)==0x80) { *size = 0x01; } /* pad and convert to byte offset */ if (ib.b_off % 8) ib.b_off += 8 - (ib.b_off % 8); *offset += (ib.b_off/8); return 1; fail: return 0; } #endif