mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-20 10:25:53 +01:00
276 lines
10 KiB
C
276 lines
10 KiB
C
#include "vorbis_custom_decoder.h"
|
|
|
|
#ifdef VGM_USE_VORBIS
|
|
#include <vorbis/codec.h>
|
|
|
|
#define FSB_VORBIS_USE_PRECOMPILED_FVS 1 /* if enabled vgmstream weights ~600kb more but doesn't need external .fvs packets */
|
|
#if FSB_VORBIS_USE_PRECOMPILED_FVS
|
|
#include "vorbis_custom_data_fsb.h"
|
|
#endif
|
|
|
|
|
|
/* **************************************************************************** */
|
|
/* DEFS */
|
|
/* **************************************************************************** */
|
|
|
|
static int build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long);
|
|
static int build_header_comment(uint8_t * buf, size_t bufsize);
|
|
static int build_header_setup(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile);
|
|
|
|
static int load_fvs_file_single(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile);
|
|
static int load_fvs_file_multi(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile);
|
|
static int load_fvs_array(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile);
|
|
|
|
|
|
/* **************************************************************************** */
|
|
/* EXTERNAL API */
|
|
/* **************************************************************************** */
|
|
|
|
/**
|
|
* FSB references an external setup packet by the setup_id, and packets have mini headers with the size.
|
|
*
|
|
* Format info from python-fsb5 (https://github.com/HearthSim/python-fsb5) and
|
|
* fsb-vorbis-extractor (https://github.com/tmiasko/fsb-vorbis-extractor).
|
|
*/
|
|
int vorbis_custom_setup_init_fsb(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) {
|
|
vorbis_custom_config cfg = data->config;
|
|
|
|
data->op.bytes = build_header_identification(data->buffer, data->buffer_size, cfg.channels, cfg.sample_rate, 256, 2048); /* FSB default block sizes */
|
|
if (!data->op.bytes) goto fail;
|
|
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */
|
|
|
|
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 */
|
|
|
|
data->op.bytes = build_header_setup(data->buffer, data->buffer_size, cfg.setup_id, streamFile);
|
|
if (!data->op.bytes) goto fail;
|
|
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */
|
|
|
|
return 1;
|
|
|
|
fail:
|
|
return 0;
|
|
}
|
|
|
|
|
|
int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) {
|
|
size_t bytes;
|
|
|
|
/* get next packet size from the FSB 16b header (doesn't count this 16b) */
|
|
data->op.bytes = (uint16_t)read_16bitLE(stream->offset, stream->streamfile);
|
|
stream->offset += 2;
|
|
if (data->op.bytes == 0 || data->op.bytes == 0xFFFF) {
|
|
VGM_LOG("FSB Vorbis: wrong packet (0x%lx) @ %lx\n", data->op.bytes, stream->offset-2);
|
|
goto fail; /* EOF or end padding */
|
|
}
|
|
|
|
/* read raw block */
|
|
bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile);
|
|
stream->offset += bytes;
|
|
if (bytes != data->op.bytes) {
|
|
VGM_LOG("Wwise Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset-bytes);
|
|
goto fail; /* wrong packet? */
|
|
}
|
|
|
|
return 1;
|
|
|
|
fail:
|
|
return 0;
|
|
}
|
|
|
|
/* **************************************************************************** */
|
|
/* INTERNAL HELPERS */
|
|
/* **************************************************************************** */
|
|
|
|
static int build_header_identification(uint8_t * buf, size_t bufsize, int channels, int sample_rate, int blocksize_short, int blocksize_long) {
|
|
int bytes = 0x1e;
|
|
uint8_t blocksizes, exp_blocksize_0, exp_blocksize_1;
|
|
|
|
if (bytes > bufsize) return 0;
|
|
|
|
/* guetto log2 for allowed blocksizes (2-exp), could be improved */
|
|
switch(blocksize_long) {
|
|
case 64: exp_blocksize_0 = 6; break;
|
|
case 128: exp_blocksize_0 = 7; break;
|
|
case 256: exp_blocksize_0 = 8; break;
|
|
case 512: exp_blocksize_0 = 9; break;
|
|
case 1024: exp_blocksize_0 = 10; break;
|
|
case 2048: exp_blocksize_0 = 11; break;
|
|
case 4096: exp_blocksize_0 = 12; break;
|
|
case 8192: exp_blocksize_0 = 13; break;
|
|
default: return 0;
|
|
}
|
|
switch(blocksize_short) {
|
|
case 64: exp_blocksize_1 = 6; break;
|
|
case 128: exp_blocksize_1 = 7; break;
|
|
case 256: exp_blocksize_1 = 8; break;
|
|
case 512: exp_blocksize_1 = 9; break;
|
|
case 1024: exp_blocksize_1 = 10; break;
|
|
case 2048: exp_blocksize_1 = 11; break;
|
|
case 4096: exp_blocksize_1 = 12; break;
|
|
case 8192: exp_blocksize_1 = 13; break;
|
|
default: return 0;
|
|
}
|
|
blocksizes = (exp_blocksize_0 << 4) | (exp_blocksize_1);
|
|
|
|
put_8bit (buf+0x00, 0x01); /* packet_type (id) */
|
|
memcpy (buf+0x01, "vorbis", 6); /* id */
|
|
put_32bitLE(buf+0x07, 0x00); /* vorbis_version (fixed) */
|
|
put_8bit (buf+0x0b, channels); /* audio_channels */
|
|
put_32bitLE(buf+0x0c, sample_rate); /* audio_sample_rate */
|
|
put_32bitLE(buf+0x10, 0x00); /* bitrate_maximum (optional hint) */
|
|
put_32bitLE(buf+0x14, 0x00); /* bitrate_nominal (optional hint) */
|
|
put_32bitLE(buf+0x18, 0x00); /* bitrate_minimum (optional hint) */
|
|
put_8bit (buf+0x1c, blocksizes); /* blocksize_0 + blocksize_1 nibbles */
|
|
put_8bit (buf+0x1d, 0x01); /* framing_flag (fixed) */
|
|
|
|
return bytes;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
static int build_header_setup(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) {
|
|
int bytes;
|
|
|
|
/* try to locate from the precompiled list */
|
|
bytes = load_fvs_array(buf, bufsize, setup_id, streamFile);
|
|
if (bytes)
|
|
return bytes;
|
|
|
|
/* try to load from external files */
|
|
bytes = load_fvs_file_single(buf, bufsize, setup_id, streamFile);
|
|
if (bytes)
|
|
return bytes;
|
|
|
|
bytes = load_fvs_file_multi(buf, bufsize, setup_id, streamFile);
|
|
if (bytes)
|
|
return bytes;
|
|
|
|
/* not found */
|
|
VGM_LOG("FSB Vorbis: setup_id %08x not found\n", setup_id);
|
|
return 0;
|
|
}
|
|
|
|
static int load_fvs_file_single(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) {
|
|
STREAMFILE * streamFileSetup = NULL;
|
|
|
|
{
|
|
char setupname[PATH_LIMIT];
|
|
char pathname[PATH_LIMIT];
|
|
char *path;
|
|
|
|
/* read "(dir/).fvs_{setup_id}" */
|
|
streamFile->get_name(streamFile,pathname,sizeof(pathname));
|
|
path = strrchr(pathname,DIR_SEPARATOR);
|
|
if (path)
|
|
*(path+1) = '\0';
|
|
else
|
|
pathname[0] = '\0';
|
|
|
|
snprintf(setupname,PATH_LIMIT,"%s.fvs_%08x", pathname, setup_id);
|
|
streamFileSetup = streamFile->open(streamFile,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
|
}
|
|
|
|
if (streamFileSetup) {
|
|
/* file found, get contents into the buffer */
|
|
size_t bytes = streamFileSetup->get_size(streamFileSetup);
|
|
if (bytes > bufsize) goto fail;
|
|
|
|
if (read_streamfile(buf, 0, bytes, streamFileSetup) != bytes)
|
|
goto fail;
|
|
|
|
streamFileSetup->close(streamFileSetup);
|
|
return bytes;
|
|
}
|
|
|
|
fail:
|
|
if (streamFileSetup) streamFileSetup->close(streamFileSetup);
|
|
return 0;
|
|
}
|
|
|
|
static int load_fvs_file_multi(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) {
|
|
STREAMFILE * streamFileSetup = NULL;
|
|
|
|
{
|
|
char setupname[PATH_LIMIT];
|
|
char pathname[PATH_LIMIT];
|
|
char *path;
|
|
|
|
/* read "(dir/).fvs" */
|
|
streamFile->get_name(streamFile,pathname,sizeof(pathname));
|
|
path = strrchr(pathname,DIR_SEPARATOR);
|
|
if (path)
|
|
*(path+1) = '\0';
|
|
else
|
|
pathname[0] = '\0';
|
|
|
|
snprintf(setupname,PATH_LIMIT,"%s.fvs", pathname);
|
|
streamFileSetup = streamFile->open(streamFile,setupname,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
|
}
|
|
|
|
if (streamFileSetup) {
|
|
/* file found: read mini-header (format by bnnm, feel free to change) and locate FVS */
|
|
int entries, i;
|
|
uint32_t offset = 0, size = 0;
|
|
|
|
if (read_32bitBE(0x0, streamFileSetup) != 0x56465653) goto fail; /* "VFVS" */
|
|
entries = read_32bitLE(0x08, streamFileSetup); /* 0x04=v0, 0x0c-0x20: reserved */
|
|
if (entries <= 0) goto fail;
|
|
|
|
for (i=0; i < entries; i++) { /* entry = id, offset, size, reserved */
|
|
if ((uint32_t)read_32bitLE(0x20 + i*0x10, streamFileSetup) == setup_id) {
|
|
offset = read_32bitLE(0x24 + i*0x10, streamFileSetup);
|
|
size = read_32bitLE(0x28 + i*0x10, streamFileSetup);
|
|
break;
|
|
}
|
|
}
|
|
if (!size || !offset || size > bufsize) goto fail;
|
|
|
|
/* read into buf */
|
|
if (read_streamfile(buf, offset, size, streamFileSetup) != size)
|
|
goto fail;
|
|
|
|
streamFileSetup->close(streamFileSetup);
|
|
return size;
|
|
}
|
|
|
|
fail:
|
|
if (streamFileSetup) streamFileSetup->close(streamFileSetup);
|
|
return 0;
|
|
}
|
|
|
|
static int load_fvs_array(uint8_t * buf, size_t bufsize, uint32_t setup_id, STREAMFILE *streamFile) {
|
|
#if FSB_VORBIS_USE_PRECOMPILED_FVS
|
|
int i, list_length;
|
|
|
|
list_length = sizeof(fvs_list) / sizeof(fvs_info);
|
|
for (i=0; i < list_length; i++) {
|
|
if (fvs_list[i].id == setup_id) {
|
|
if (fvs_list[i].size > bufsize) goto fail;
|
|
/* found: copy data as-is */
|
|
memcpy(buf,fvs_list[i].setup, fvs_list[i].size);
|
|
return fvs_list[i].size;
|
|
}
|
|
}
|
|
|
|
fail:
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#endif
|