diff --git a/src/Makefile b/src/Makefile index 48081030..9176be45 100644 --- a/src/Makefile +++ b/src/Makefile @@ -22,12 +22,13 @@ META_OBJS=meta/adx_header.o \ meta/ngc_adpdtk.o \ meta/rsf.o \ meta/rs03.o \ - meta/ngc_dsp_std.o + meta/ngc_dsp_std.o \ + meta/Cstr.o OBJECTS=vgmstream.o streamfile.o util.o $(CODING_OBJS) $(LAYOUT_OBJS) $(META_OBJS) libvgmstream.a: $(OBJECTS) - $(AR) crsv libvgmstream.a $(OBJECTS) + $(AR) crs libvgmstream.a $(OBJECTS) vgmstream-deps: $(CC) $(CFLAGS) -M -o vgmstream-deps diff --git a/src/coding/ngc_dsp_decoder.c b/src/coding/ngc_dsp_decoder.c index e40f8ae3..09e53a7d 100644 --- a/src/coding/ngc_dsp_decoder.c +++ b/src/coding/ngc_dsp_decoder.c @@ -20,6 +20,11 @@ void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspaci for (i=first_sample,sample_count=0; ioffset+1+i/2,stream->streamfile); +#if DEBUG + if (hist1==stream->loop_history1 && hist2==stream->loop_history2) fprintf(stderr,"yo! %#x (start %#x) %d\n",stream->offset+framesin*8+i/2,stream->channel_start_offset,stream->samples_done); + stream->samples_done++; +#endif + outbuf[sample_count] = clamp16(( (((i&1? get_low_nibble_signed(sample_byte): diff --git a/src/meta/Cstr.c b/src/meta/Cstr.c new file mode 100644 index 00000000..d1cfaec0 --- /dev/null +++ b/src/meta/Cstr.c @@ -0,0 +1,228 @@ +#include "Cstr.h" +#include "../coding/ngc_dsp_decoder.h" +#include "../util.h" + +/* .dsp w/ Cstr header, seen in Star Fox Assault and Donkey Konga */ + +VGMSTREAM * init_vgmstream_Cstr(const char * const filename) { + VGMSTREAM * vgmstream = NULL; + STREAMFILE * infile = NULL; + + int loop_flag; + off_t start_offset; + off_t first_data; + off_t loop_offset; + size_t interleave; + int loop_adjust; + + /* check extension, case insensitive */ + if (strcasecmp("dsp",filename_extension(filename))) goto fail; + + /* try to open the file for header reading */ + infile = open_streamfile(filename); + if (!infile) goto fail; + + /* check header */ + if ((uint32_t)read_32bitBE(0,infile)!=0x43737472) /* "Cstr" */ + goto fail; +#ifdef DEBUG + fprintf(stderr,"header ok\n"); +#endif + + interleave = read_16bitBE(0x06,infile); + start_offset = 0xe0; + first_data = start_offset+read_32bitBE(0x0c,infile); + loop_flag = read_16bitBE(0x2c,infile); + + 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=-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,infile) == (uint8_t)read_8bit(loop_offset-interleave+loop_adjust,infile) && + read_16bitBE(0xc4,infile) == (uint8_t)read_8bit(loop_offset+loop_adjust,infile)) { + loops_ok=1; + break; + } + } + 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,infile) == (uint8_t)read_8bit(loop_offset-interleave+loop_adjust,infile) && + read_16bitBE(0xc4,infile) == (uint8_t)read_8bit(loop_offset+loop_adjust,infile)) { + 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,infile) != read_32bitBE(0x94,infile)) + goto fail; + /* loop start (Cstr header and channel 1 header) */ + if (read_32bitBE(0x30,infile) != read_32bitBE(0x10,infile) +#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,infile)+read_32bitLE(0x30,infile)) != read_32bitBE(0x90,infile) +#endif + ) + /* alternatively (Donkey Konga) the header loop is 0x0c+0x10 */ + if ( + /* loop start (Cstr header and channel 1 header) */ + read_32bitBE(0x30,infile) != read_32bitBE(0x10,infile)+read_32bitBE(0x0c,infile)) + /* 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,infile) != 2 || read_32bitBE(0x10,infile) != 0) + goto fail; + +#ifdef DEBUG + fprintf(stderr,"loop points agree\n"); +#endif + } + + /* assure that sample counts, sample rates agree */ + if ( + /* sample count (channel 1 & 2 headers) */ + read_32bitBE(0x20,infile) != read_32bitBE(0x80,infile) || + /* sample rate (channel 1 & 2 headers) */ + read_32bitBE(0x28,infile) != read_32bitBE(0x88,infile) || + /* sample count (Cstr header and channel 1 header) */ + read_32bitLE(0x14,infile) != read_32bitBE(0x20,infile) || + /* sample rate (Cstr header and channel 1 header) */ + (uint16_t)read_16bitLE(0x18,infile) != read_32bitBE(0x28,infile)) + goto fail; + + /* build the VGMSTREAM */ + + vgmstream = allocate_vgmstream(2,loop_flag); + if (!vgmstream) goto fail; + + /* fill in the vital statistics */ + vgmstream->sample_rate = read_32bitBE(0x28,infile); + /* 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,infile) + + (first_data-start_offset)/8*14; + + 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,infile)*2-inter);*/ + vgmstream->loop_end_sample = dsp_nibbles_to_samples( + read_32bitBE(0x34,infile))+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->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = interleave; + 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,infile); + for (i=0;i<16;i++) + vgmstream->ch[1].adpcm_coef[i]=read_16bitBE(0x9c+i*2,infile); + } +#ifdef DEBUG + vgmstream->ch[0].loop_history1 = read_16bitBE(0x66,infile); + vgmstream->ch[0].loop_history2 = read_16bitBE(0x68,infile); + vgmstream->ch[1].loop_history1 = read_16bitBE(0xc6,infile); + vgmstream->ch[1].loop_history2 = read_16bitBE(0xc8,infile); +#endif + + close_streamfile(infile); infile=NULL; + + /* open the file for reading by each channel */ + { + int i; + for (i=0;i<2;i++) { + vgmstream->ch[i].streamfile = open_streamfile_buffer(filename,interleave); + + if (!vgmstream->ch[i].streamfile) goto fail; + + vgmstream->ch[i].channel_start_offset= + vgmstream->ch[i].offset= + start_offset+interleave*i; + } + } + + return vgmstream; + + /* clean up anything we may have opened */ +fail: + if (infile) close_streamfile(infile); + if (vgmstream) close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/meta/Cstr.h b/src/meta/Cstr.h new file mode 100644 index 00000000..32dd160d --- /dev/null +++ b/src/meta/Cstr.h @@ -0,0 +1,8 @@ +#include "../vgmstream.h" + +#ifndef _CSTR_H +#define _CSTR_H + +VGMSTREAM * init_vgmstream_Cstr(const char * const filename); + +#endif diff --git a/src/vgmstream.c b/src/vgmstream.c index 50a4b94e..7f7c6d8b 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -13,6 +13,7 @@ #include "meta/halpst.h" #include "meta/rs03.h" #include "meta/ngc_dsp_std.h" +#include "meta/Cstr.h" #include "layout/interleave.h" #include "layout/nolayout.h" #include "layout/blocked.h" @@ -28,7 +29,7 @@ * List of functions that will recognize files. These should correspond pretty * directly to the metadata types */ -#define INIT_VGMSTREAM_FCNS 11 +#define INIT_VGMSTREAM_FCNS 12 VGMSTREAM * (*init_vgmstream_fcns[INIT_VGMSTREAM_FCNS])(const char * const) = { init_vgmstream_adx, init_vgmstream_brstm, @@ -41,6 +42,7 @@ VGMSTREAM * (*init_vgmstream_fcns[INIT_VGMSTREAM_FCNS])(const char * const) = { init_vgmstream_halpst, init_vgmstream_rs03, init_vgmstream_ngc_dsp_std, + init_vgmstream_Cstr, }; @@ -335,26 +337,30 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) { /* if (vgmstream->loop_flag) {*/ /* is this the loop end? */ if (vgmstream->current_sample==vgmstream->loop_end_sample) { - /* RS03 and the Metroid Prime standard DSP files are - * apparently built with the assumption that the history - * is preserved through looping - */ + /* against everything I hold sacred, preserve adpcm + * history through loop for certain types */ if (vgmstream->meta_type == meta_DSP_STD || - vgmstream->meta_type == meta_DSP_RS03) { + vgmstream->meta_type == meta_DSP_RS03 || + vgmstream->meta_type == meta_DSP_CSTR) { int i; for (i=0;ichannels;i++) { vgmstream->loop_ch[i].adpcm_history1_16 = vgmstream->ch[i].adpcm_history1_16; vgmstream->loop_ch[i].adpcm_history2_16 = vgmstream->ch[i].adpcm_history2_16; } } - /* +#if DEBUG + { int i; for (i=0;ichannels;i++) { - fprintf(stderr,"ch%d hist: %04x %04x loop hist: %04x %04x\n",i, - vgmstream->ch[i].adpcm_history1_16,vgmstream->ch[i].adpcm_history2_16, - vgmstream->loop_ch[i].adpcm_history1_16,vgmstream->loop_ch[i].adpcm_history2_16); + fprintf(stderr,"ch%d hist: %04x %04x loop hist: %04x %04x\n",i, + vgmstream->ch[i].adpcm_history1_16,vgmstream->ch[i].adpcm_history2_16, + vgmstream->loop_ch[i].adpcm_history1_16,vgmstream->loop_ch[i].adpcm_history2_16); + fprintf(stderr,"ch%d offset: %x loop offset: %x\n",i, + vgmstream->ch[i].offset, + vgmstream->loop_ch[i].offset); } - */ + } +#endif /* restore! */ memcpy(vgmstream->ch,vgmstream->loop_ch,sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); vgmstream->current_sample=vgmstream->loop_sample; @@ -464,7 +470,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { snprintf(temp,TEMPSIZE,"interleave with short last block"); break; case layout_dtk_interleave: - snprintf(temp,TEMPSIZE,"DTK nibble interleave"); + snprintf(temp,TEMPSIZE,"ADP/DTK nibble interleave"); break; case layout_ast_blocked: snprintf(temp,TEMPSIZE,"AST blocked"); @@ -497,40 +503,43 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { switch (vgmstream->meta_type) { case meta_RSTM: - snprintf(temp,TEMPSIZE,"RSTM header"); + snprintf(temp,TEMPSIZE,"Nintendo RSTM header"); break; case meta_STRM: - snprintf(temp,TEMPSIZE,"NDS STRM header"); + snprintf(temp,TEMPSIZE,"Nintendo STRM header"); break; case meta_ADX_03: - snprintf(temp,TEMPSIZE,"ADX header type 03"); + snprintf(temp,TEMPSIZE,"CRI ADX header type 03"); break; case meta_ADX_04: - snprintf(temp,TEMPSIZE,"ADX header type 04"); + snprintf(temp,TEMPSIZE,"CRI ADX header type 04"); break; case meta_DSP_AGSC: - snprintf(temp,TEMPSIZE,"AGSC header"); + snprintf(temp,TEMPSIZE,"Retro Studios AGSC header"); break; case meta_NGC_ADPDTK: - snprintf(temp,TEMPSIZE,"assumed NGC DTK by .adp extension and valid first frame"); + snprintf(temp,TEMPSIZE,"assumed Nintendo ADP by .adp extension and valid first frame"); break; case meta_RSF: - snprintf(temp,TEMPSIZE,"assumed Retro Studios RSF by .rsf extension"); + snprintf(temp,TEMPSIZE,"assumed Retro Studios RSF by .rsf extension and valid first bytes"); break; case meta_AFC: - snprintf(temp,TEMPSIZE,"AFC header"); + snprintf(temp,TEMPSIZE,"Nintendo AFC header"); break; case meta_AST: - snprintf(temp,TEMPSIZE,"AST header"); + snprintf(temp,TEMPSIZE,"Nintendo AST header"); break; case meta_HALPST: - snprintf(temp,TEMPSIZE,"HALPST header"); + snprintf(temp,TEMPSIZE,"HAL Laboratory HALPST header"); break; case meta_DSP_RS03: snprintf(temp,TEMPSIZE,"Retro Studios RS03 header"); break; case meta_DSP_STD: - snprintf(temp,TEMPSIZE,"Standard NGC DSP header"); + snprintf(temp,TEMPSIZE,"Standard Nintendo DSP header"); + break; + case meta_DSP_CSTR: + snprintf(temp,TEMPSIZE,"Namco Cstr header"); break; default: snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET"); diff --git a/src/vgmstream.h b/src/vgmstream.h index 1e2febb3..cd757708 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -99,6 +99,11 @@ typedef struct { struct g72x_state g72x_state; /* state for G.721 decoder, sort of big but we might as well keep it around */ + +#ifdef DEBUG + int samples_done; + int16_t loop_history1,loop_history2; +#endif } VGMSTREAMCHANNEL; typedef struct {