vgmstream/src/meta/cstr.c

107 lines
3.7 KiB
C
Raw Normal View History

#include "meta.h"
#include "../coding/coding.h"
/* Cstr - from Namco NuSound v1 games [Mr. Driller (GC), Star Fox Assault (GC), Donkey Konga (GC)] */
VGMSTREAM * init_vgmstream_cstr(STREAMFILE* sf) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
size_t interleave, first_skip;
int loop_flag, channels, sample_rate;
int loop_start, loop_end, num_samples, num_nibbles;
/* checks */
if (!check_extensions(sf,"dsp"))
goto fail;
if (read_u32be(0x00,sf) != 0x43737472) /* "Cstr" */
goto fail;
/* 0x04: version (0x0066 = 0.66 as seen in nus_config .txt) */
interleave = read_u16be(0x06, sf);
/* 0x08: config? (volume/pan/etc?) */
first_skip = read_s32be(0x0c, sf); /* first interleaved block has normal size, but starts late */
loop_start = read_s32be(0x10, sf);
/* 0x14: num samples LE or null in mono files */
/* 0x18: sample rate LE or null in mono files */
/* 0x01a: always 0x40 */
channels = read_u8(0x1b, sf); /* mono only seen in R:Racing Evolution radio sfx */
/* 0x1c: always null */
/* next is DSP header, with some oddities:
* - loop flag isn't always set vs Cstr's flag (won't have DSP loop_ps/etc)
* - loop start nibbles can be wrong even with loop flag set
* - wonky loop_ps as a result (other fields agree between channels) */
num_samples = read_s32be(0x20 + 0x00, sf);
num_nibbles = read_s32be(0x20 + 0x04, sf);
sample_rate = read_s32be(0x20 + 0x08, sf);
//loop_flag = read_s16be(0x20 + 0x0c, sf);
//loop_start = read_s32be(0x20 + 0x10, sf);
loop_end = read_s32be(0x20 + 0x14, sf);
loop_flag = (loop_start >= 0);
start_offset = 0x20 + 0x60 * channels + first_skip;
/* nonlooped tracks may not set first skip for no reason, but can be tested with initial p/s */
if (!loop_flag && channels == 2 && first_skip == 0) {
while (first_skip < 0x800) {
if (read_u16be(0x20 + 0x3e, sf) == read_u8(start_offset + first_skip, sf) &&
read_u16be(0x20 + 0x60 + 0x3e, sf) == read_u8(start_offset + first_skip + interleave, sf))
break;
first_skip += 0x08;
}
/* not found */
if (first_skip == 0x800)
first_skip = 0;
}
if (first_skip > 0 && loop_start >= (interleave - first_skip))
loop_start = loop_start - (interleave - first_skip);
loop_start = loop_start * 2;
/* Mr. Driller oddity, unreliable loop flag */
if (loop_end == num_nibbles) {
loop_flag = 0;
}
/* Mr. Driller oddity, half nibbles */
if (loop_end * 2 + 1 <= num_nibbles) {
loop_end = loop_end * 2;
}
/* no loop_ps checks given how buggy the format is */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = dsp_bytes_to_samples(loop_start, channels);
vgmstream->loop_end_sample = dsp_nibbles_to_samples(loop_end) + 1;
/* Donkey Konga 3 oddity, loop/num nibbles not correct vs final samples */
if (vgmstream->loop_end_sample > num_samples)
vgmstream->loop_end_sample = num_samples;
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = interleave;
vgmstream->interleave_first_block_size = interleave - first_skip;
vgmstream->interleave_first_skip = first_skip;
vgmstream->meta_type = meta_DSP_CSTR;
dsp_read_coefs_be(vgmstream, sf, 0x3c, 0x60);
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}