Add Neversoft .OGG [Gun (GC)]

This commit is contained in:
bnnm 2017-12-02 16:33:43 +01:00
parent 6eef1074a2
commit b9c3bd9a01
11 changed files with 306 additions and 9 deletions

View File

@ -50,6 +50,7 @@ vorbis_custom_codec_data * init_vorbis_custom_codec_data(STREAMFILE *streamFile,
case VORBIS_WWISE: ok = vorbis_custom_setup_init_wwise(streamFile, start_offset, data); break;
case VORBIS_OGL: ok = vorbis_custom_setup_init_ogl(streamFile, start_offset, data); break;
case VORBIS_SK: ok = vorbis_custom_setup_init_sk(streamFile, start_offset, data); break;
case VORBIS_VID1: ok = vorbis_custom_setup_init_vid1(streamFile, start_offset, data); break;
default: goto fail;
}
if(!ok) goto fail;
@ -131,6 +132,7 @@ void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t sample
case VORBIS_WWISE: ok = vorbis_custom_parse_packet_wwise(stream, data); break;
case VORBIS_OGL: ok = vorbis_custom_parse_packet_ogl(stream, data); break;
case VORBIS_SK: ok = vorbis_custom_parse_packet_sk(stream, data); break;
case VORBIS_VID1: ok = vorbis_custom_parse_packet_vid1(stream, data); break;
default: goto decode_fail;
}
if(!ok) {
@ -142,8 +144,9 @@ void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t sample
/* parse the fake ogg packet into a logical vorbis block */
rc = vorbis_synthesis(&data->vb,&data->op);
if (rc == OV_ENOTAUDIO) {
VGM_LOG("Vorbis: not an audio packet @ %lx\n",stream->offset);
continue; /* not tested */
VGM_LOG("Vorbis: not an audio packet (size=0x%x) @ %lx\n",(size_t)data->op.bytes,stream->offset);
//VGM_LOGB(data->op.packet, (size_t)data->op.bytes,0);
continue; /* seems ok? */
} else if (rc != 0) {
VGM_LOG("Vorbis: cannot parse Vorbis block @ %lx\n",stream->offset);
goto decode_fail;

View File

@ -9,11 +9,13 @@ int vorbis_custom_setup_init_fsb(STREAMFILE *streamFile, off_t start_offset, vor
int vorbis_custom_setup_init_wwise(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data);
int vorbis_custom_setup_init_ogl(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data);
int vorbis_custom_setup_init_sk(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data);
int vorbis_custom_setup_init_vid1(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data);
int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data);
int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data);
int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data);
int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data);
int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data);
#endif/* VGM_USE_VORBIS */
#endif/*_VORBIS_CUSTOM_DECODER_H_ */

View File

@ -0,0 +1,191 @@
#include "vorbis_custom_decoder.h"
#ifdef VGM_USE_VORBIS
#include <vorbis/codec.h>
/* **************************************************************************** */
/* DEFS */
/* **************************************************************************** */
/* An internal struct to pass around and simulate a bitstream. */
typedef struct {
uint8_t * buf; /* buffer to read/write*/
size_t bufsize; /* max size of the buffer */
off_t b_off; /* current offset in bits inside the buffer */
} vgm_bitstream;
static int r_bits(vgm_bitstream * iw, int num_bits, uint32_t * value);
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) {
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;
/* 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;
}
/* Read bits (max 32) from buf and update the bit offset. Vorbis packs values in LSB order and byte by byte.
* (ex. from 2 bytes 00100111 00000001 we can could read 4b=0111 and 6b=010010, 6b=remainder (second value is split into the 2nd byte) */
static int r_bits(vgm_bitstream * ib, int num_bits, uint32_t * value) {
off_t off, pos;
int i, bit_buf, bit_val;
if (num_bits == 0) return 1;
if (num_bits > 32 || num_bits < 0 || ib->b_off + num_bits > ib->bufsize*8) goto fail;
*value = 0; /* set all bits to 0 */
off = ib->b_off / 8; /* byte offset */
pos = ib->b_off % 8; /* bit sub-offset */
for (i = 0; i < num_bits; i++) {
bit_buf = (1U << pos) & 0xFF; /* bit check for buf */
bit_val = (1U << i); /* bit to set in value */
if (ib->buf[off] & bit_buf) /* is bit in buf set? */
*value |= bit_val; /* set bit */
pos++; /* new byte starts */
if (pos%8 == 0) {
pos = 0;
off++;
}
}
ib->b_off += num_bits;
return 1;
fail:
return 0;
}
#endif

View File

@ -319,11 +319,11 @@ static const char* extension_list[] = {
"vawx",
"vb",
"vbk",
"vds",
"vdm",
"vgs",
"vgv",
"vig",
"vds",
"vdm",
"vms",
"voi",
"vpk",
@ -914,6 +914,7 @@ static const meta_info meta_info_list[] = {
{meta_VXN, "Gameloft VXN header"},
{meta_EA_SNR_SNS, "Electronic Arts SNR+SNS header"},
{meta_EA_SPS, "Electronic Arts SPS header"},
{meta_NGC_VID1, "Neversoft VID1 header"},
#ifdef VGM_USE_VORBIS
{meta_OGG_VORBIS, "Ogg Vorbis"},

View File

@ -625,6 +625,10 @@
<File
RelativePath=".\meta\ngc_ulw.c"
>
</File>
<File
RelativePath=".\meta\ngc_vid1.c"
>
</File>
<File
RelativePath=".\meta\ngca.c"
@ -1554,6 +1558,10 @@
RelativePath=".\coding\vorbis_custom_utils_sk.c"
>
</File>
<File
RelativePath=".\coding\vorbis_custom_utils_vid1.c"
>
</File>
<File
RelativePath=".\coding\vorbis_custom_utils_wwise.c"
>

View File

@ -287,6 +287,7 @@
<ClCompile Include="meta\ngc_tydsp.c" />
<ClCompile Include="meta\ngc_ymf.c" />
<ClCompile Include="meta\ngc_ulw.c" />
<ClCompile Include="meta\ngc_vid1.c" />
<ClCompile Include="meta\nub_xma.c" />
<ClCompile Include="meta\nwa.c" />
<ClCompile Include="meta\ogg_vorbis_file.c" />
@ -465,6 +466,7 @@
<ClCompile Include="coding\vorbis_custom_utils_fsb.c" />
<ClCompile Include="coding\vorbis_custom_utils_ogl.c" />
<ClCompile Include="coding\vorbis_custom_utils_sk.c" />
<ClCompile Include="coding\vorbis_custom_utils_vid1.c" />
<ClCompile Include="coding\vorbis_custom_utils_wwise.c" />
<ClCompile Include="coding\ws_decoder.c" />
<ClCompile Include="coding\xa_decoder.c" />

View File

@ -376,6 +376,9 @@
<ClCompile Include="meta\ngc_ulw.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ngc_vid1.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\nub_xma.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -907,6 +910,9 @@
<ClCompile Include="coding\vorbis_custom_utils_sk.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\vorbis_custom_utils_vid1.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\vorbis_custom_utils_wwise.c">
<Filter>coding\Source Files</Filter>
</ClCompile>

View File

@ -698,4 +698,7 @@ VGMSTREAM * init_vgmstream_vxn(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_sps(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ngc_vid1(STREAMFILE * streamFile);
#endif /*_META_H*/

75
src/meta/ngc_vid1.c Normal file
View File

@ -0,0 +1,75 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* VID1 - from Neversoft games (Gun, Tony Hawk's American Wasteland GC) */
VGMSTREAM * init_vgmstream_ngc_vid1(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, header_offset, header_size;
int loop_flag = 0, channel_count;
/* check extension */
if (!check_extensions(streamFile,"ogg,logg"))
goto fail;
/* chunked/blocked format containing video or audio frames */
if (read_32bitBE(0x00,streamFile) != 0x56494431) /* "VID1" */
goto fail;
/* find actual header start/size in the chunks */
{
header_offset = read_32bitBE(0x04, streamFile);
if (read_32bitBE(header_offset,streamFile) != 0x48454144) /* "HEAD" */
goto fail;
start_offset = header_offset + read_32bitBE(header_offset + 0x04, streamFile);
header_offset += 0x0c;
/* videos have VIDH before AUDH, and VIDD in blocks, but aren't parsed ATM */
if (read_32bitBE(header_offset,streamFile) != 0x41554448) /* "AUDH" */
goto fail;
header_size = read_32bitBE(header_offset + 0x04, streamFile);
header_offset += 0x0c;
if (read_32bitBE(header_offset,streamFile) != 0x56415544) /* "VAUD" (Vorbis audio?) */
goto fail;
header_offset += 0x04;
header_size -= 0x10;
}
channel_count = read_8bit(header_offset + 0x04,streamFile);
/* other values unknown, maybe related to vorbis (ex. bitrate/encoding modes) */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitBE(header_offset + 0x00, streamFile);
vgmstream->num_samples = read_32bitBE(header_offset + 0x1c, streamFile);
vgmstream->meta_type = meta_NGC_VID1;
#ifdef VGM_USE_VORBIS
{
vorbis_custom_config cfg = {0};
vgmstream->layout_type = layout_none;
vgmstream->coding_type = coding_VORBIS_custom;
vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, header_offset + 0x20, VORBIS_VID1, &cfg);
if (!vgmstream->codec_data) goto fail;
}
#else
goto fail;
#endif
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -369,6 +369,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_vxn,
init_vgmstream_ea_snr_sns,
init_vgmstream_ea_sps,
init_vgmstream_ngc_vid1,
init_vgmstream_txth, /* should go at the end (lower priority) */
#ifdef VGM_USE_FFMPEG

View File

@ -632,6 +632,7 @@ typedef enum {
meta_VXN, /* Gameloft mobile games */
meta_EA_SNR_SNS, /* Electronic Arts SNR+SNS (Burnout Paradise) */
meta_EA_SPS, /* Electronic Arts SPS (Burnout Crash) */
meta_NGC_VID1, /* Neversoft .ogg (Gun GC) */
#ifdef VGM_USE_VORBIS
meta_OGG_VORBIS, /* Ogg Vorbis */
@ -804,11 +805,12 @@ typedef struct {
/* custom Vorbis modes */
typedef enum {
VORBIS_FSB, /* simplified/external setup packets, custom packet headers */
VORBIS_WWISE, /* many variations (custom setup, headers and data) */
VORBIS_OGL, /* custom packet headers */
VORBIS_SK /* "OggS" replaced by "SK" */
//VORBIS_LYN /* two interleaved Ogg (including setup, duplicated) */
VORBIS_FSB, /* FMOD FSB: simplified/external setup packets, custom packet headers */
VORBIS_WWISE, /* Wwise WEM: many variations (custom setup, headers and data) */
VORBIS_OGL, /* Shin'en OGL: custom packet headers */
VORBIS_SK, /* Silicon Knights AUD: "OggS" replaced by "SK" */
VORBIS_VID1, /* Neversoft VID1: custom packet blocks/headers */
//VORBIS_LYN /* Ubisoft LyN: two interleaved Ogg (including setup, duplicated) */
} vorbis_custom_t;
/* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */
@ -859,6 +861,9 @@ typedef struct {
uint8_t prev_blockflag; /* blockflag in the last decoded packet */
/* Ogg-style Vorbis: packet within a page */
int current_packet;
/* reference for page/blocks */
off_t block_offset;
size_t block_size;
} vorbis_custom_codec_data;
#endif