mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-29 19:37:30 +01:00
Fix some Cstr .dsp samples/looping issues and cleanup
This commit is contained in:
parent
267bf6be87
commit
56978adb83
337
src/meta/cstr.c
337
src/meta/cstr.c
@ -1,299 +1,106 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* .dsp w/ Cstr header, seen in Star Fox Assault and Donkey Konga */
|
||||
VGMSTREAM * init_vgmstream_cstr(STREAMFILE *streamFile) {
|
||||
|
||||
/* 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;
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
int loop_flag;
|
||||
off_t start_offset;
|
||||
off_t first_data;
|
||||
off_t loop_offset;
|
||||
size_t interleave;
|
||||
int loop_adjust;
|
||||
int double_loop_end = 0;
|
||||
size_t interleave, first_skip;
|
||||
int loop_flag, channels, sample_rate;
|
||||
int loop_start, loop_end, num_samples, num_nibbles;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("dsp",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check header */
|
||||
if ((uint32_t)read_32bitBE(0,streamFile)!=0x43737472) /* "Cstr" */
|
||||
goto fail;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"header ok\n");
|
||||
#endif
|
||||
|
||||
if (read_8bit(0x1b,streamFile)==1) {
|
||||
/* mono version, much simpler to handle */
|
||||
/* Only seen in R Racing Evolution radio sfx */
|
||||
|
||||
start_offset = 0x80;
|
||||
loop_flag = read_16bitBE(0x2c,streamFile);
|
||||
|
||||
/* check initial predictor/scale */
|
||||
if (read_16bitBE(0x5e,streamFile) != (uint8_t)read_8bit(start_offset,streamFile))
|
||||
goto fail;
|
||||
|
||||
/* check type==0 and gain==0 */
|
||||
if (read_16bitBE(0x2e,streamFile) || read_16bitBE(0x5c,streamFile))
|
||||
goto fail;
|
||||
|
||||
loop_offset = start_offset+read_32bitBE(0x10,streamFile);
|
||||
if (loop_flag) {
|
||||
if (read_16bitBE(0x64,streamFile) != (uint8_t)read_8bit(loop_offset,streamFile)) goto fail;
|
||||
}
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
|
||||
vgmstream = allocate_vgmstream(1,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->sample_rate = read_32bitBE(0x28,streamFile);
|
||||
vgmstream->num_samples = read_32bitBE(0x20,streamFile);
|
||||
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = dsp_nibbles_to_samples(
|
||||
read_32bitBE(0x30,streamFile));
|
||||
vgmstream->loop_end_sample = dsp_nibbles_to_samples(
|
||||
read_32bitBE(0x34,streamFile))+1;
|
||||
}
|
||||
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_DSP_CSTR;
|
||||
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<16;i++)
|
||||
vgmstream->ch[0].adpcm_coef[i]=read_16bitBE(0x3c+i*2,streamFile);
|
||||
}
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
|
||||
if (!vgmstream->ch[0].streamfile) goto fail;
|
||||
|
||||
vgmstream->ch[0].channel_start_offset=
|
||||
vgmstream->ch[0].offset=
|
||||
start_offset;
|
||||
|
||||
return vgmstream;
|
||||
} /* end mono */
|
||||
|
||||
interleave = read_16bitBE(0x06,streamFile);
|
||||
start_offset = 0xe0;
|
||||
first_data = start_offset+read_32bitBE(0x0c,streamFile);
|
||||
loop_flag = read_16bitBE(0x2c,streamFile);
|
||||
|
||||
if (!loop_flag) {
|
||||
/* Nonlooped tracks seem to follow no discernable pattern
|
||||
* with where they actually start.
|
||||
* But! with the magic of initial p/s redundancy, we can guess.
|
||||
*/
|
||||
while (first_data<start_offset+0x800 &&
|
||||
(read_16bitBE(0x5e,streamFile) != (uint8_t)read_8bit(first_data,streamFile) ||
|
||||
read_16bitBE(0xbe,streamFile) != (uint8_t)read_8bit(first_data+interleave,streamFile)))
|
||||
first_data+=8;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"guessed first_data at %#x\n",first_data);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* check initial predictor/scale */
|
||||
if (read_16bitBE(0x5e,streamFile) != (uint8_t)read_8bit(first_data,streamFile))
|
||||
goto fail;
|
||||
if (read_16bitBE(0xbe,streamFile) != (uint8_t)read_8bit(first_data+interleave,streamFile))
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"dsp"))
|
||||
goto fail;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"p/s ok\n");
|
||||
#endif
|
||||
|
||||
/* check type==0 and gain==0 */
|
||||
if (read_16bitBE(0x2e,streamFile) || read_16bitBE(0x5c,streamFile))
|
||||
goto fail;
|
||||
if (read_16bitBE(0x8e,streamFile) || read_16bitBE(0xbc,streamFile))
|
||||
if (read_u32be(0x00,sf) != 0x43737472) /* "Cstr" */
|
||||
goto fail;
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"type & gain ok\n");
|
||||
#endif
|
||||
/* 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 */
|
||||
|
||||
/* check for loop flag agreement */
|
||||
if (read_16bitBE(0x2c,streamFile) != read_16bitBE(0x8c,streamFile))
|
||||
goto fail;
|
||||
/* 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) */
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop flags agree\n");
|
||||
#endif
|
||||
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_offset = start_offset+read_32bitBE(0x10,streamFile)*2;
|
||||
if (loop_flag) {
|
||||
int loops_ok=0;
|
||||
/* check loop predictor/scale */
|
||||
/* some fuzz allowed */
|
||||
for (loop_adjust=0;loop_adjust>=-0x10;loop_adjust-=8) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"looking for loop p/s at %#x,%#x\n",loop_offset-interleave+loop_adjust,loop_offset+loop_adjust);
|
||||
#endif
|
||||
if (read_16bitBE(0x64,streamFile) == (uint8_t)read_8bit(loop_offset-interleave+loop_adjust,streamFile) &&
|
||||
read_16bitBE(0xc4,streamFile) == (uint8_t)read_8bit(loop_offset+loop_adjust,streamFile)) {
|
||||
loops_ok=1;
|
||||
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;
|
||||
}
|
||||
if (!loops_ok)
|
||||
for (loop_adjust=interleave;loop_adjust<=interleave+0x10;loop_adjust+=8) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"looking for loop p/s at %#x,%#x\n",loop_offset-interleave+loop_adjust,loop_offset+loop_adjust);
|
||||
#endif
|
||||
if (read_16bitBE(0x64,streamFile) == (uint8_t)read_8bit(loop_offset-interleave+loop_adjust,streamFile) &&
|
||||
read_16bitBE(0xc4,streamFile) == (uint8_t)read_8bit(loop_offset+loop_adjust,streamFile)) {
|
||||
loops_ok=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!loops_ok) goto fail;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop p/s ok (with %#4x adjust)\n",loop_adjust);
|
||||
#endif
|
||||
|
||||
/* check for agreement */
|
||||
/* loop end (channel 1 & 2 headers) */
|
||||
if (read_32bitBE(0x34,streamFile) != read_32bitBE(0x94,streamFile))
|
||||
goto fail;
|
||||
|
||||
/* Mr. Driller oddity */
|
||||
if (dsp_nibbles_to_samples(read_32bitBE(0x34,streamFile)*2)+1 <= read_32bitBE(0x20,streamFile)) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop end <= half total samples, should be doubled\n");
|
||||
#endif
|
||||
double_loop_end = 1;
|
||||
}
|
||||
|
||||
/* loop start (Cstr header and channel 1 header) */
|
||||
if (read_32bitBE(0x30,streamFile) != read_32bitBE(0x10,streamFile)
|
||||
#if 0
|
||||
/* this particular glitch only true for SFA, though it
|
||||
* seems like something similar happens in Donkey Konga */
|
||||
/* loop start (Cstr, channel 1 & 2 headers) */
|
||||
|| (read_32bitBE(0x0c,streamFile)+read_32bitLE(0x30,streamFile)) !=
|
||||
read_32bitBE(0x90,streamFile)
|
||||
#endif
|
||||
)
|
||||
/* alternatively (Donkey Konga) the header loop is 0x0c+0x10 */
|
||||
if (
|
||||
/* loop start (Cstr header and channel 1 header) */
|
||||
read_32bitBE(0x30,streamFile) != read_32bitBE(0x10,streamFile)+
|
||||
read_32bitBE(0x0c,streamFile))
|
||||
/* further alternatively (Donkey Konga), if we loop back to
|
||||
* the very first frame 0x30 might be 0x00000002 (which
|
||||
* is a *valid* std dsp loop start, imagine that) while 0x10
|
||||
* is 0x00000000 */
|
||||
if (!(read_32bitBE(0x30,streamFile) == 2 &&
|
||||
read_32bitBE(0x10,streamFile) == 0))
|
||||
/* lest there be too few alternatives, in Mr. Driller we
|
||||
* find that [0x30] + [0x0c] + 8 = [0x10]*2 */
|
||||
if (!(double_loop_end &&
|
||||
read_32bitBE(0x30,streamFile) +
|
||||
read_32bitBE(0x0c,streamFile) + 8 ==
|
||||
read_32bitBE(0x10,streamFile)*2)) {
|
||||
/* for Mr.Driller Drill Land, no idea about the above but seems to play and loop ok */
|
||||
VGM_LOG("Cstr: bad loop check ignored\n");
|
||||
//goto fail;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop points agree\n");
|
||||
#endif
|
||||
/* not found */
|
||||
if (first_skip == 0x800)
|
||||
first_skip = 0;
|
||||
}
|
||||
|
||||
/* assure that sample counts, sample rates agree */
|
||||
if (
|
||||
/* sample count (channel 1 & 2 headers) */
|
||||
read_32bitBE(0x20,streamFile) != read_32bitBE(0x80,streamFile) ||
|
||||
/* sample rate (channel 1 & 2 headers) */
|
||||
read_32bitBE(0x28,streamFile) != read_32bitBE(0x88,streamFile) ||
|
||||
/* sample count (Cstr header and channel 1 header) */
|
||||
read_32bitLE(0x14,streamFile) != read_32bitBE(0x20,streamFile) ||
|
||||
/* sample rate (Cstr header and channel 1 header) */
|
||||
(uint16_t)read_16bitLE(0x18,streamFile) != read_32bitBE(0x28,streamFile))
|
||||
goto fail;
|
||||
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(2,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->sample_rate = read_32bitBE(0x28,streamFile);
|
||||
/* This is a slight hack to counteract their hack.
|
||||
* All the data is ofset by first_data so that the loop
|
||||
* point occurs at a block boundary. However, I always begin decoding
|
||||
* right after the header, as that is the start of the first block and
|
||||
* my interleave code relies on starting at the beginning of a block.
|
||||
* So we decode a few silent samples at the beginning, and here we make up
|
||||
* for it by lengthening the track by that much.
|
||||
*/
|
||||
vgmstream->num_samples = read_32bitBE(0x20,streamFile) +
|
||||
(first_data-start_offset)/8*14;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
vgmstream->loop_start_sample = dsp_bytes_to_samples(loop_start, channels);
|
||||
|
||||
if (loop_flag) {
|
||||
off_t loop_start_bytes = loop_offset-start_offset-interleave;
|
||||
vgmstream->loop_start_sample = dsp_nibbles_to_samples((loop_start_bytes/(2*interleave)*interleave+loop_start_bytes%(interleave*2))*2);
|
||||
/*dsp_nibbles_to_samples(loop_start_bytes);*/
|
||||
/*dsp_nibbles_to_samples(read_32bitBE(0x30,streamFile)*2-inter);*/
|
||||
vgmstream->loop_end_sample = dsp_nibbles_to_samples(
|
||||
read_32bitBE(0x34,streamFile))+1;
|
||||
|
||||
if (double_loop_end)
|
||||
vgmstream->loop_end_sample =
|
||||
dsp_nibbles_to_samples(read_32bitBE(0x34,streamFile)*2)+1;
|
||||
|
||||
if (vgmstream->loop_end_sample > vgmstream->num_samples) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr,"loop_end_sample > num_samples, adjusting\n");
|
||||
#endif
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<16;i++)
|
||||
vgmstream->ch[0].adpcm_coef[i]=read_16bitBE(0x3c+i*2,streamFile);
|
||||
for (i=0;i<16;i++)
|
||||
vgmstream->ch[1].adpcm_coef[i]=read_16bitBE(0x9c+i*2,streamFile);
|
||||
}
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<2;i++) {
|
||||
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
|
||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=
|
||||
start_offset+interleave*i;
|
||||
}
|
||||
}
|
||||
dsp_read_coefs_be(vgmstream, sf, 0x3c, 0x60);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user