mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-21 02:45:52 +01:00
153 lines
5.6 KiB
C
153 lines
5.6 KiB
C
#include "vorbis_custom_decoder.h"
|
|
|
|
#ifdef VGM_USE_VORBIS
|
|
#include <vorbis/codec.h>
|
|
|
|
|
|
/* **************************************************************************** */
|
|
/* 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" */
|
|
read_32bitBE(stream->offset + 0x20,stream->streamfile) == 0x41554444) { /* "AUDD" */
|
|
data->block_offset = stream->offset;
|
|
data->block_size = read_32bitBE(stream->offset + 0x2c,stream->streamfile);
|
|
stream->offset += 0x34; /* 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) {
|
|
VGM_LOG("VID1 Vorbis: wrong packet (0x%lx) @ %lx\n", data->op.bytes, stream->offset);
|
|
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) {
|
|
VGM_LOG("VID1 Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset-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 + 0x34)) >= (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
|