#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) { 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; /* 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=-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) 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 } /* 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; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(2,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; 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->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,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,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 (vgmstream) close_vgmstream(vgmstream); return NULL; }