git-svn-id: https://vgmstream.svn.sourceforge.net/svnroot/vgmstream@108 51a99a44-fe44-0410-b1ba-c3e57ba2b86b
This commit is contained in:
halleyscometsw 2008-05-12 13:31:48 +00:00
parent 014d22b5eb
commit bad669a3f9
4 changed files with 207 additions and 22 deletions

View File

@ -43,6 +43,7 @@ File types supported by this version of vgmstream:
- standard, with dual file stereo
- RS03
- Cstr
- .stm
- .gcsw (16 bit PCM)
- .ads/.ss2 (PSX ADPCM)
- .npsf (PSX ADPCM)

View File

@ -25,6 +25,8 @@ VGMSTREAM * init_vgmstream_ngc_adpdtk(const char * const filename);
VGMSTREAM * init_vgmstream_ngc_dsp_std(const char * const filename);
VGMSTREAM * init_vgmstream_ngc_dsp_stm(const char * const filename);
VGMSTREAM * init_vgmstream_ps2_ads(const char * const filename);
VGMSTREAM * init_vgmstream_ps2_npsf(const char * const filename);

View File

@ -152,9 +152,187 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(const char * const filename) {
return vgmstream;
/* clean up anything we may have opened */
fail:
/* clean up anything we may have opened */
if (infile) close_streamfile(infile);
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}
/* Some very simple stereo variants of standard dsp just use the standard header
* twice and add interleave, or just concatenate the channels. We'll support
* them all here.
* Note that Cstr isn't here, despite using the form of the standard header,
* because its loop values are wacky. */
/* .stm
* Used in Paper Mario 2, Fire Emblem: Path of Radiance, Cubivore
* I suspected that this was an Intelligent Systems format, but its use in
* Cubivore calls that into question. */
VGMSTREAM * init_vgmstream_ngc_dsp_stm(const char * const filename) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * infile = NULL;
struct dsp_header ch0_header, ch1_header;
int i;
int stm_header_sample_rate;
int channel_count;
const off_t start_offset = 0x100;
off_t first_channel_size;
off_t second_channel_start;
/* check extension, case insensitive */
/* to avoid collision with Scream Tracker 2 Modules, also ending in .stm
* and supported by default in Winamp, it was policy in the old days to
* rename these files to .dsp */
if (strcasecmp("stm",filename_extension(filename)) &&
strcasecmp("dsp",filename_extension(filename))) goto fail;
/* try to open the file for header reading */
infile = open_streamfile(filename);
if (!infile) goto fail;
/* check intro magic */
if (read_16bitBE(0, infile) != 0x0200) goto fail;
channel_count = read_32bitBE(4, infile);
/* only stereo and mono are known */
if (channel_count != 1 && channel_count != 2) goto fail;
first_channel_size = read_32bitBE(8, infile);
/* this is bad rounding, wastes space, but it looks like that's what's
* used */
second_channel_start = ((start_offset+first_channel_size)+0x20)/0x20*0x20;
/* an additional check */
stm_header_sample_rate = (uint16_t)read_16bitBE(2, infile);
/* read the DSP headers */
if (read_dsp_header(&ch0_header, 0x40, infile)) goto fail;
if (channel_count == 2) {
if (read_dsp_header(&ch1_header, 0xa0, infile)) goto fail;
}
/* checks for fist channel */
{
if (ch0_header.sample_rate != stm_header_sample_rate) goto fail;
/* check initial predictor/scale */
if (ch0_header.initial_ps != (uint8_t)read_8bit(start_offset, infile))
goto fail;
/* check type==0 and gain==0 */
if (ch0_header.format || ch0_header.gain)
goto fail;
if (ch0_header.loop_flag) {
off_t loop_off;
/* check loop predictor/scale */
loop_off = ch0_header.loop_start_offset/16*8;
if (ch0_header.loop_ps != (uint8_t)read_8bit(start_offset+loop_off,infile))
goto fail;
}
}
/* checks for second channel */
if (channel_count == 2) {
if (ch1_header.sample_rate != stm_header_sample_rate) goto fail;
/* check for agreement with first channel header */
if (
ch0_header.sample_count != ch1_header.sample_count ||
ch0_header.nibble_count != ch1_header.nibble_count ||
ch0_header.loop_flag != ch1_header.loop_flag ||
ch0_header.loop_start_offset != ch1_header.loop_start_offset ||
ch0_header.loop_end_offset != ch1_header.loop_end_offset
) goto fail;
/* check initial predictor/scale */
if (ch1_header.initial_ps != (uint8_t)read_8bit(second_channel_start, infile))
goto fail;
/* check type==0 and gain==0 */
if (ch1_header.format || ch1_header.gain)
goto fail;
if (ch1_header.loop_flag) {
off_t loop_off;
/* check loop predictor/scale */
loop_off = ch1_header.loop_start_offset/16*8;
/*printf("loop_start_offset=%x\nloop_ps=%x\nloop_off=%x\n",ch1_header.loop_start_offset,ch1_header.loop_ps,second_channel_start+loop_off);*/
if (ch1_header.loop_ps != (uint8_t)read_8bit(second_channel_start+loop_off,infile))
goto fail;
}
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, ch0_header.loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->num_samples = ch0_header.sample_count;
vgmstream->sample_rate = ch0_header.sample_rate;
vgmstream->loop_start_sample = dsp_nibbles_to_samples(
ch0_header.loop_start_offset);
vgmstream->loop_end_sample = dsp_nibbles_to_samples(
ch0_header.loop_end_offset)+1;
/* don't know why, but it does happen*/
if (vgmstream->loop_end_sample > vgmstream->num_samples)
vgmstream->loop_end_sample = vgmstream->num_samples;
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_DSP_STM;
/* coeffs */
for (i=0;i<16;i++)
vgmstream->ch[0].adpcm_coef[i] = ch0_header.coef[i];
/* initial history */
/* always 0 that I've ever seen, but for completeness... */
vgmstream->ch[0].adpcm_history1_16 = ch0_header.initial_hist1;
vgmstream->ch[0].adpcm_history2_16 = ch0_header.initial_hist2;
if (channel_count == 2) {
/* coeffs */
for (i=0;i<16;i++)
vgmstream->ch[1].adpcm_coef[i] = ch1_header.coef[i];
/* initial history */
/* always 0 that I've ever seen, but for completeness... */
vgmstream->ch[1].adpcm_history1_16 = ch1_header.initial_hist1;
vgmstream->ch[1].adpcm_history2_16 = ch1_header.initial_hist2;
}
close_streamfile(infile); infile=NULL;
/* open the file for reading */
vgmstream->ch[0].streamfile = open_streamfile(filename);
if (!vgmstream->ch[0].streamfile) goto fail;
vgmstream->ch[0].channel_start_offset=
vgmstream->ch[0].offset=start_offset;
if (channel_count == 2) {
vgmstream->ch[1].streamfile = open_streamfile(filename);
if (!vgmstream->ch[1].streamfile) goto fail;
vgmstream->ch[1].channel_start_offset=
vgmstream->ch[1].offset=start_offset+second_channel_start;
}
return vgmstream;
fail:
/* clean up anything we may have opened */
if (infile) close_streamfile(infile);
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -15,27 +15,28 @@
* List of functions that will recognize files. These should correspond pretty
* directly to the metadata types
*/
#define INIT_VGMSTREAM_FCNS 19
#define INIT_VGMSTREAM_FCNS 20
VGMSTREAM * (*init_vgmstream_fcns[INIT_VGMSTREAM_FCNS])(const char * const) = {
init_vgmstream_adx,
init_vgmstream_brstm,
init_vgmstream_nds_strm,
init_vgmstream_agsc,
init_vgmstream_ngc_adpdtk,
init_vgmstream_rsf,
init_vgmstream_afc,
init_vgmstream_ast,
init_vgmstream_halpst,
init_vgmstream_rs03,
init_vgmstream_ngc_dsp_std,
init_vgmstream_Cstr,
init_vgmstream_gcsw,
init_vgmstream_ps2_ads,
init_vgmstream_ps2_npsf,
init_vgmstream_rwsd,
init_vgmstream_cdxa,
init_vgmstream_ps2_rxw,
init_vgmstream_ps2_int,
init_vgmstream_adx, /* 0 */
init_vgmstream_brstm, /* 1 */
init_vgmstream_nds_strm, /* 2 */
init_vgmstream_agsc, /* 3 */
init_vgmstream_ngc_adpdtk, /* 4 */
init_vgmstream_rsf, /* 5 */
init_vgmstream_afc, /* 6 */
init_vgmstream_ast, /* 7 */
init_vgmstream_halpst, /* 8 */
init_vgmstream_rs03, /* 9 */
init_vgmstream_ngc_dsp_std, /* 10 */
init_vgmstream_Cstr, /* 11 */
init_vgmstream_gcsw, /* 12 */
init_vgmstream_ps2_ads, /* 13 */
init_vgmstream_ps2_npsf, /* 14 */
init_vgmstream_rwsd, /* 15 */
init_vgmstream_cdxa, /* 16 */
init_vgmstream_ps2_rxw, /* 17 */
init_vgmstream_ps2_int, /* 18 */
init_vgmstream_ngc_dsp_stm, /* 19 */
};
@ -592,7 +593,10 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
snprintf(temp,TEMPSIZE,"RXWS File (Arc The Lad)");
break;
case meta_PS2_RAW:
snprintf(temp,TEMPSIZE,"RAW Interleaved PCM");
snprintf(temp,TEMPSIZE,"assumed RAW Interleaved PCM by .int extension");
break;
case meta_DSP_STM:
snprintf(temp,TEMPSIZE,"Nintendo STM header");
break;
default:
snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET");