#ifdef _MSC_VER #define _CRT_SECURE_NO_DEPRECATE #endif #include #include #include #include "vgmstream.h" #include "meta/meta.h" #include "layout/layout.h" #include "coding/coding.h" /* * List of functions that will recognize files. These should correspond pretty * directly to the metadata types */ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = { 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_ngc_dsp_stm, init_vgmstream_ps2_exst, init_vgmstream_ps2_svag, init_vgmstream_ps2_mib, init_vgmstream_ngc_mpdsp, init_vgmstream_ps2_mic, init_vgmstream_ngc_dsp_std_int, init_vgmstream_raw, init_vgmstream_ps2_vag, init_vgmstream_psx_gms, init_vgmstream_ps2_str, init_vgmstream_ps2_ild, init_vgmstream_ps2_pnb, init_vgmstream_xbox_wavm, init_vgmstream_xbox_xwav, init_vgmstream_ngc_str, init_vgmstream_ea, init_vgmstream_caf, init_vgmstream_ps2_vpk, init_vgmstream_genh, #ifdef VGM_USE_VORBIS init_vgmstream_ogg_vorbis, #endif init_vgmstream_sadb, init_vgmstream_ps2_bmdx, init_vgmstream_wsi, init_vgmstream_aifc, init_vgmstream_str_snds, init_vgmstream_ws_aud, #ifdef VGM_USE_MPEG init_vgmstream_ahx, #endif init_vgmstream_ivb, init_vgmstream_amts, init_vgmstream_svs, init_vgmstream_riff, init_vgmstream_pos, init_vgmstream_nwa, init_vgmstream_eacs, init_vgmstream_xss, init_vgmstream_sl3, init_vgmstream_hgc1, init_vgmstream_aus, init_vgmstream_rws, init_vgmstream_rsd, init_vgmstream_fsb, init_vgmstream_rwx, init_vgmstream_xwb, init_vgmstream_xa30, init_vgmstream_musc, init_vgmstream_musx, init_vgmstream_leg, init_vgmstream_filp, init_vgmstream_ikm, init_vgmstream_sfs, init_vgmstream_bg00, init_vgmstream_dvi, init_vgmstream_kcey, init_vgmstream_ps2_rstm, init_vgmstream_acm, init_vgmstream_ps2_kces, init_vgmstream_ps2_dxh, }; #define INIT_VGMSTREAM_FCNS (sizeof(init_vgmstream_fcns)/sizeof(init_vgmstream_fcns[0])) /* internal version with all parameters */ VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile, int do_dfs) { int i; if (!streamFile) return NULL; /* try a series of formats, see which works */ for (i=0;isample_rate)) { close_vgmstream(vgmstream); continue; } /* dual file stereo */ if (do_dfs && ((vgmstream->meta_type == meta_DSP_STD) || (vgmstream->meta_type == meta_PS2_VAGp)) && vgmstream->channels == 1) { try_dual_file_stereo(vgmstream, streamFile); } /* save start things so we can restart for seeking */ /* copy the channels */ memcpy(vgmstream->start_ch,vgmstream->ch,sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); /* copy the whole VGMSTREAM */ memcpy(vgmstream->start_vgmstream,vgmstream,sizeof(VGMSTREAM)); return vgmstream; } } return NULL; } /* format detection and VGMSTREAM setup, uses default parameters */ VGMSTREAM * init_vgmstream(const char * const filename) { VGMSTREAM *vgmstream = NULL; STREAMFILE *streamFile = open_stdio_streamfile(filename); if (streamFile) { vgmstream = init_vgmstream_from_STREAMFILE(streamFile); close_streamfile(streamFile); } return vgmstream; } VGMSTREAM * init_vgmstream_from_STREAMFILE(STREAMFILE *streamFile) { return init_vgmstream_internal(streamFile,1); } /* Reset a VGMSTREAM to its state at the start of playback. * Note that this does not reset the constituent STREAMFILES. */ void reset_vgmstream(VGMSTREAM * vgmstream) { /* copy the vgmstream back into itself */ memcpy(vgmstream,vgmstream->start_vgmstream,sizeof(VGMSTREAM)); /* copy the initial channels */ memcpy(vgmstream->ch,vgmstream->start_ch,sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); /* loop_ch is not zeroed here because there is a possibility of the * init_vgmstream_* function doing something tricky and precomputing it. * Otherwise hit_loop will be 0 and it will be copied over anyway when we * really hit the loop start. */ #ifdef VGM_USE_VORBIS if (vgmstream->meta_type==meta_ogg_vorbis) { ogg_vorbis_codec_data *data = vgmstream->codec_data; OggVorbis_File *ogg_vorbis_file = &(data->ogg_vorbis_file); ov_pcm_seek(ogg_vorbis_file, 0); } #endif #ifdef VGM_USE_MPEG if (vgmstream->layout_type==layout_mpeg || vgmstream->layout_type==layout_fake_mpeg) { off_t input_offset; mpeg_codec_data *data = vgmstream->codec_data; /* input_offset is ignored as we can assume it will be 0 for a seek * to sample 0 */ mpg123_feedseek(data->m,0,SEEK_SET,&input_offset); data->buffer_full = data->buffer_used = 0; } #endif if (vgmstream->coding_type==coding_ACM) { mus_acm_codec_data *data = vgmstream->codec_data; int i; data->current_file = 0; for (i=0;ifile_count;i++) { acm_reset(data->files[i]); } } } /* simply allocate memory for the VGMSTREAM and its channels */ VGMSTREAM * allocate_vgmstream(int channel_count, int looped) { VGMSTREAM * vgmstream; VGMSTREAM * start_vgmstream; VGMSTREAMCHANNEL * channels; VGMSTREAMCHANNEL * start_channels; VGMSTREAMCHANNEL * loop_channels; if (channel_count <= 0) return NULL; vgmstream = calloc(1,sizeof(VGMSTREAM)); if (!vgmstream) return NULL; start_vgmstream = calloc(1,sizeof(VGMSTREAM)); if (!start_vgmstream) { free(vgmstream); return NULL; } vgmstream->start_vgmstream = start_vgmstream; start_vgmstream->start_vgmstream = start_vgmstream; channels = calloc(channel_count,sizeof(VGMSTREAMCHANNEL)); if (!channels) { free(vgmstream); free(start_vgmstream); return NULL; } vgmstream->ch = channels; vgmstream->channels = channel_count; start_channels = calloc(channel_count,sizeof(VGMSTREAMCHANNEL)); if (!start_channels) { free(vgmstream); free(start_vgmstream); free(channels); return NULL; } vgmstream->start_ch = start_channels; if (looped) { loop_channels = calloc(channel_count,sizeof(VGMSTREAMCHANNEL)); if (!loop_channels) { free(vgmstream); free(start_vgmstream); free(channels); free(start_channels); return NULL; } vgmstream->loop_ch = loop_channels; } vgmstream->loop_flag = looped; return vgmstream; } void close_vgmstream(VGMSTREAM * vgmstream) { int i,j; if (!vgmstream) return; for (i=0;ichannels;i++) { if (vgmstream->ch[i].streamfile) { close_streamfile(vgmstream->ch[i].streamfile); /* Multiple channels might have the same streamfile. Find the others * that are the same as this and clear them so they won't be closed * again. */ for (j=0;jchannels;j++) { if (i!=j && vgmstream->ch[j].streamfile == vgmstream->ch[i].streamfile) { vgmstream->ch[j].streamfile = NULL; } } vgmstream->ch[i].streamfile = NULL; } } if (vgmstream->loop_ch) free(vgmstream->loop_ch); if (vgmstream->start_ch) free(vgmstream->start_ch); if (vgmstream->ch) free(vgmstream->ch); /* the start_vgmstream is considered just data */ if (vgmstream->start_vgmstream) free(vgmstream->start_vgmstream); #ifdef VGM_USE_VORBIS if (vgmstream->meta_type==meta_ogg_vorbis) { ogg_vorbis_codec_data *data = vgmstream->codec_data; if (vgmstream->codec_data) { OggVorbis_File *ogg_vorbis_file = &(data->ogg_vorbis_file); ov_clear(ogg_vorbis_file); close_streamfile(data->ov_streamfile.streamfile); free(vgmstream->codec_data); vgmstream->codec_data = NULL; } } #endif #ifdef VGM_USE_MPEG if (vgmstream->layout_type==layout_fake_mpeg|| vgmstream->layout_type==layout_mpeg) { mpeg_codec_data *data = vgmstream->codec_data; if (data) { mpg123_delete(data->m); free(vgmstream->codec_data); vgmstream->codec_data = NULL; /* The astute reader will note that a call to mpg123_exit is never * made. While is is evilly breaking our contract with mpg123, it * doesn't actually do anything except set the "initialized" flag * to 0. And if we exit we run the risk of turning it off when * someone else in another thread is using it. */ } } #endif if (vgmstream->coding_type==coding_ACM) { mus_acm_codec_data *data = vgmstream->codec_data; if (data) { if (data->files) { int i; for (i=0; ifile_count; i++) { /* shouldn't be duplicates */ if (data->files[i]) { acm_close(data->files[i]); data->files[i] = NULL; } } free(data->files); data->files = NULL; } free(vgmstream->codec_data); vgmstream->codec_data = NULL; } } free(vgmstream); } int32_t get_vgmstream_play_samples(double looptimes, double fadeseconds, double fadedelayseconds, VGMSTREAM * vgmstream) { if (vgmstream->loop_flag) { return vgmstream->loop_start_sample+(vgmstream->loop_end_sample-vgmstream->loop_start_sample)*looptimes+(fadedelayseconds+fadeseconds)*vgmstream->sample_rate; } else return vgmstream->num_samples; } void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) { switch (vgmstream->layout_type) { case layout_interleave: case layout_interleave_shortblock: render_vgmstream_interleave(buffer,sample_count,vgmstream); break; #ifdef VGM_USE_VORBIS case layout_ogg_vorbis: #endif #ifdef VGM_USE_MPEG case layout_fake_mpeg: case layout_mpeg: #endif case layout_dtk_interleave: case layout_none: render_vgmstream_nolayout(buffer,sample_count,vgmstream); break; case layout_ast_blocked: case layout_halpst_blocked: case layout_xa_blocked: case layout_ea_blocked: case layout_eacs_blocked: case layout_caf_blocked: case layout_wsi_blocked: case layout_str_snds_blocked: case layout_ws_aud_blocked: render_vgmstream_blocked(buffer,sample_count,vgmstream); break; case layout_interleave_byte: render_vgmstream_interleave_byte(buffer,sample_count,vgmstream); break; case layout_acm: render_vgmstream_mus_acm(buffer,sample_count,vgmstream); break; } } int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) { switch (vgmstream->coding_type) { case coding_CRI_ADX: return 32; case coding_NGC_DSP: return 14; case coding_PCM16LE: case coding_PCM16LE_int: case coding_PCM16BE: case coding_PCM8: case coding_PCM8_int: #ifdef VGM_USE_VORBIS case coding_ogg_vorbis: #endif #ifdef VGM_USE_MPEG case coding_fake_MPEG2_L2: case coding_MPEG1_L1: case coding_MPEG1_L2: case coding_MPEG1_L3: case coding_MPEG2_L1: case coding_MPEG2_L2: case coding_MPEG2_L3: case coding_MPEG25_L1: case coding_MPEG25_L2: case coding_MPEG25_L3: #endif case coding_SDX2: case coding_SDX2_int: case coding_ACM: return 1; case coding_NDS_IMA: return (vgmstream->interleave_block_size-4)*2; case coding_NGC_DTK: return 28; case coding_G721: case coding_DVI_IMA: case coding_EACS_IMA: case coding_IMA: return 1; case coding_INT_DVI_IMA: return 2; case coding_NGC_AFC: return 16; case coding_PSX: case coding_invert_PSX: case coding_XA: return 28; case coding_XBOX: return 64; case coding_EAXA: return 28; case coding_EA_ADPCM: return 14*vgmstream->channels; case coding_WS: /* only works if output sample size is 8 bit, which is always is for WS ADPCM */ return vgmstream->ws_output_size; default: return 0; } } int get_vgmstream_samples_per_shortframe(VGMSTREAM * vgmstream) { switch (vgmstream->coding_type) { case coding_NDS_IMA: return (vgmstream->interleave_smallblock_size-4)*2; default: return get_vgmstream_samples_per_frame(vgmstream); } } int get_vgmstream_frame_size(VGMSTREAM * vgmstream) { switch (vgmstream->coding_type) { case coding_CRI_ADX: return 18; case coding_NGC_DSP: return 8; case coding_PCM16LE: case coding_PCM16LE_int: case coding_PCM16BE: return 2; case coding_PCM8: case coding_PCM8_int: case coding_SDX2: case coding_SDX2_int: return 1; case coding_NDS_IMA: return vgmstream->interleave_block_size; case coding_NGC_DTK: return 32; case coding_EACS_IMA: return 1; case coding_DVI_IMA: case coding_IMA: case coding_G721: return 0; case coding_NGC_AFC: return 9; case coding_PSX: case coding_invert_PSX: return 16; case coding_XA: return 14*vgmstream->channels; case coding_XBOX: return 36; case coding_EA_ADPCM: return 30; case coding_EAXA: return 1; // the frame is variant in size case coding_WS: return vgmstream->current_block_size; case coding_INT_DVI_IMA: return 1; default: return 0; } } int get_vgmstream_shortframe_size(VGMSTREAM * vgmstream) { switch (vgmstream->coding_type) { case coding_NDS_IMA: return vgmstream->interleave_smallblock_size; default: return get_vgmstream_frame_size(vgmstream); } } void decode_vgmstream_mem(VGMSTREAM * vgmstream, int samples_written, int samples_to_do, sample * buffer, uint8_t * data, int channel) { switch (vgmstream->coding_type) { case coding_NGC_DSP: decode_ngc_dsp_mem(&vgmstream->ch[channel], buffer+samples_written*vgmstream->channels+channel, vgmstream->channels,vgmstream->samples_into_block, samples_to_do, data); break; default: break; } } void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to_do, sample * buffer) { int chan; switch (vgmstream->coding_type) { case coding_CRI_ADX: for (chan=0;chanchannels;chan++) { decode_adx(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } break; case coding_NGC_DSP: for (chan=0;chanchannels;chan++) { decode_ngc_dsp(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } break; case coding_PCM16LE: for (chan=0;chanchannels;chan++) { decode_pcm16LE(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } break; case coding_PCM16LE_int: for (chan=0;chanchannels;chan++) { decode_pcm16LE_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } break; case coding_PCM16BE: for (chan=0;chanchannels;chan++) { decode_pcm16BE(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } break; case coding_PCM8: for (chan=0;chanchannels;chan++) { decode_pcm8(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } break; case coding_PCM8_int: for (chan=0;chanchannels;chan++) { decode_pcm8_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } break; case coding_NDS_IMA: for (chan=0;chanchannels;chan++) { decode_nds_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } break; case coding_XBOX: for (chan=0;chanchannels;chan++) { decode_xbox_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do,chan); } break; case coding_NGC_DTK: for (chan=0;chanchannels;chan++) { decode_ngc_dtk(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do,chan); } break; case coding_G721: for (chan=0;chanchannels;chan++) { decode_g721(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } break; case coding_NGC_AFC: for (chan=0;chanchannels;chan++) { decode_ngc_afc(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } break; case coding_PSX: for (chan=0;chanchannels;chan++) { decode_psx(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } break; case coding_invert_PSX: for (chan=0;chanchannels;chan++) { decode_invert_psx(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } break; case coding_XA: for (chan=0;chanchannels;chan++) { decode_xa(vgmstream,buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do,chan); } break; case coding_EAXA: for (chan=0;chanchannels;chan++) { decode_eaxa(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do,chan); } break; case coding_EA_ADPCM: for (chan=0;chanchannels;chan++) { decode_ea_adpcm(vgmstream,buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do,chan); } break; #ifdef VGM_USE_VORBIS case coding_ogg_vorbis: decode_ogg_vorbis(vgmstream->codec_data, buffer+samples_written*vgmstream->channels,samples_to_do, vgmstream->channels); break; #endif case coding_SDX2: for (chan=0;chanchannels;chan++) { decode_sdx2(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } break; case coding_SDX2_int: for (chan=0;chanchannels;chan++) { decode_sdx2_int(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } break; case coding_DVI_IMA: case coding_INT_DVI_IMA: for (chan=0;chanchannels;chan++) { decode_dvi_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } break; case coding_EACS_IMA: for (chan=0;chanchannels;chan++) { decode_eacs_ima(vgmstream,buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do,chan); } break; case coding_IMA: for (chan=0;chanchannels;chan++) { decode_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } break; case coding_WS: for (chan=0;chanchannels;chan++) { decode_ws(vgmstream,chan,buffer+samples_written*vgmstream->channels+chan, vgmstream->channels,vgmstream->samples_into_block, samples_to_do); } break; #ifdef VGM_USE_MPEG case coding_fake_MPEG2_L2: decode_fake_mpeg2_l2( &vgmstream->ch[0], vgmstream->codec_data, buffer+samples_written*vgmstream->channels,samples_to_do); break; case coding_MPEG1_L1: case coding_MPEG1_L2: case coding_MPEG1_L3: case coding_MPEG2_L1: case coding_MPEG2_L2: case coding_MPEG2_L3: case coding_MPEG25_L1: case coding_MPEG25_L2: case coding_MPEG25_L3: decode_mpeg( &vgmstream->ch[0], vgmstream->codec_data, buffer+samples_written*vgmstream->channels,samples_to_do, vgmstream->channels); break; case coding_ACM: /* handled in its own layout, here to quiet compiler */ break; #endif } } int vgmstream_samples_to_do(int samples_this_block, int samples_per_frame, VGMSTREAM * vgmstream) { int samples_to_do; int samples_left_this_block; samples_left_this_block = samples_this_block - vgmstream->samples_into_block; samples_to_do = samples_left_this_block; /* fun loopy crap */ /* Why did I think this would be any simpler? */ if (vgmstream->loop_flag) { /* are we going to hit the loop end during this block? */ if (vgmstream->current_sample+samples_left_this_block > vgmstream->loop_end_sample) { /* only do to just before it */ samples_to_do = vgmstream->loop_end_sample-vgmstream->current_sample; } /* are we going to hit the loop start during this block? */ if (!vgmstream->hit_loop && vgmstream->current_sample+samples_left_this_block > vgmstream->loop_start_sample) { /* only do to just before it */ samples_to_do = vgmstream->loop_start_sample-vgmstream->current_sample; } } /* if it's a framed encoding don't do more than one frame */ if (samples_per_frame>1 && (vgmstream->samples_into_block%samples_per_frame)+samples_to_do>samples_per_frame) samples_to_do=samples_per_frame-(vgmstream->samples_into_block%samples_per_frame); return samples_to_do; } /* return 1 if we just looped */ int vgmstream_do_loop(VGMSTREAM * vgmstream) { /* if (vgmstream->loop_flag) {*/ /* is this the loop end? */ if (vgmstream->current_sample==vgmstream->loop_end_sample) { /* 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_CSTR || vgmstream->coding_type == coding_PSX || vgmstream->coding_type == coding_invert_PSX) { 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; vgmstream->loop_ch[i].adpcm_history1_32 = vgmstream->ch[i].adpcm_history1_32; vgmstream->loop_ch[i].adpcm_history2_32 = vgmstream->ch[i].adpcm_history2_32; } } #ifdef 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 offset: %x loop offset: %x\n",i, vgmstream->ch[i].offset, vgmstream->loop_ch[i].offset); } } #endif #ifdef VGM_USE_VORBIS if (vgmstream->meta_type==meta_ogg_vorbis) { ogg_vorbis_codec_data *data = (ogg_vorbis_codec_data *)(vgmstream->codec_data); OggVorbis_File *ogg_vorbis_file = &(data->ogg_vorbis_file); ov_pcm_seek_lap(ogg_vorbis_file, vgmstream->loop_sample); } #endif #ifdef VGM_USE_MPEG /* won't work for fake MPEG */ if (vgmstream->layout_type==layout_mpeg) { off_t input_offset; mpeg_codec_data *data = vgmstream->codec_data; mpg123_feedseek(data->m,vgmstream->loop_sample, SEEK_SET,&input_offset); vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + input_offset; data->buffer_full = data->buffer_used = 0; } #endif /* restore! */ memcpy(vgmstream->ch,vgmstream->loop_ch,sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); vgmstream->current_sample=vgmstream->loop_sample; vgmstream->samples_into_block=vgmstream->loop_samples_into_block; vgmstream->current_block_size=vgmstream->loop_block_size; vgmstream->current_block_offset=vgmstream->loop_block_offset; vgmstream->next_block_offset=vgmstream->loop_next_block_offset; return 1; } /* is this the loop start? */ if (!vgmstream->hit_loop && vgmstream->current_sample==vgmstream->loop_start_sample) { /* save! */ memcpy(vgmstream->loop_ch,vgmstream->ch,sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); vgmstream->loop_sample=vgmstream->current_sample; vgmstream->loop_samples_into_block=vgmstream->samples_into_block; vgmstream->loop_block_size=vgmstream->current_block_size; vgmstream->loop_block_offset=vgmstream->current_block_offset; vgmstream->loop_next_block_offset=vgmstream->next_block_offset; vgmstream->hit_loop=1; } /*}*/ return 0; } /* build a descriptive string */ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) { #define TEMPSIZE 256 char temp[TEMPSIZE]; if (!vgmstream) { snprintf(temp,TEMPSIZE,"NULL VGMSTREAM"); concatn(length,desc,temp); return; } snprintf(temp,TEMPSIZE,"sample rate %d Hz\n" "channels: %d\n", vgmstream->sample_rate,vgmstream->channels); concatn(length,desc,temp); if (vgmstream->loop_flag) { snprintf(temp,TEMPSIZE,"loop start: %d samples (%.2lf seconds)\n" "loop end: %d samples (%.2lf seconds)\n", vgmstream->loop_start_sample, (double)vgmstream->loop_start_sample/vgmstream->sample_rate, vgmstream->loop_end_sample, (double)vgmstream->loop_end_sample/vgmstream->sample_rate); concatn(length,desc,temp); } snprintf(temp,TEMPSIZE,"stream total samples: %d (%.2lf seconds)\n", vgmstream->num_samples, (double)vgmstream->num_samples/vgmstream->sample_rate); concatn(length,desc,temp); snprintf(temp,TEMPSIZE,"encoding: "); concatn(length,desc,temp); switch (vgmstream->coding_type) { case coding_PCM16BE: snprintf(temp,TEMPSIZE,"Big Endian 16-bit PCM"); break; case coding_PCM16LE: snprintf(temp,TEMPSIZE,"Little Endian 16-bit PCM"); break; case coding_PCM16LE_int: snprintf(temp,TEMPSIZE,"Little Endian 16-bit PCM with 2 byte interleave"); break; case coding_PCM8: snprintf(temp,TEMPSIZE,"8-bit PCM"); break; case coding_PCM8_int: snprintf(temp,TEMPSIZE,"8-bit PCM with 1 byte interleave"); break; case coding_NGC_DSP: snprintf(temp,TEMPSIZE,"Gamecube \"DSP\" 4-bit ADPCM"); break; case coding_CRI_ADX: snprintf(temp,TEMPSIZE,"CRI ADX 4-bit ADPCM"); break; case coding_NDS_IMA: snprintf(temp,TEMPSIZE,"NDS-style 4-bit IMA ADPCM"); break; case coding_NGC_DTK: snprintf(temp,TEMPSIZE,"Gamecube \"ADP\"/\"DTK\" 4-bit ADPCM"); break; case coding_G721: snprintf(temp,TEMPSIZE,"CCITT G.721 4-bit ADPCM"); break; case coding_NGC_AFC: snprintf(temp,TEMPSIZE,"Gamecube \"AFC\" 4-bit ADPCM"); break; case coding_PSX: snprintf(temp,TEMPSIZE,"Playstation 4-bit ADPCM"); break; case coding_invert_PSX: snprintf(temp,TEMPSIZE,"Inverted (?) Playstation 4-bit ADPCM"); break; case coding_XA: snprintf(temp,TEMPSIZE,"CD-ROM XA 4-bit ADPCM"); break; case coding_XBOX: snprintf(temp,TEMPSIZE,"XBOX 4-bit IMA ADPCM"); break; case coding_EAXA: snprintf(temp,TEMPSIZE,"Electronic Arts XA Based 4-bit ADPCM"); break; case coding_EA_ADPCM: snprintf(temp,TEMPSIZE,"Electronic Arts XA Based (R1) 4-bit ADPCM"); break; #ifdef VGM_USE_VORBIS case coding_ogg_vorbis: snprintf(temp,TEMPSIZE,"Vorbis"); break; #endif case coding_SDX2: snprintf(temp,TEMPSIZE,"Squareroot-delta-exact (SDX2) 8-bit DPCM"); break; case coding_SDX2_int: snprintf(temp,TEMPSIZE,"Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"); break; case coding_DVI_IMA: snprintf(temp,TEMPSIZE,"Intel DVI 4-bit IMA ADPCM"); break; case coding_INT_DVI_IMA: snprintf(temp,TEMPSIZE,"Interleaved Intel DVI 4-bit IMA ADPCM"); break; case coding_EACS_IMA: snprintf(temp,TEMPSIZE,"EACS 4-bit IMA ADPCM"); break; case coding_IMA: snprintf(temp,TEMPSIZE,"4-bit IMA ADPCM"); break; case coding_WS: snprintf(temp,TEMPSIZE,"Westwood Studios DPCM"); break; #ifdef VGM_USE_MPEG case coding_fake_MPEG2_L2: snprintf(temp,TEMPSIZE,"MPEG-2 Layer II Audio"); break; case coding_MPEG1_L1: snprintf(temp,TEMPSIZE,"MPEG-1 Layer I Audio"); break; case coding_MPEG1_L2: snprintf(temp,TEMPSIZE,"MPEG-1 Layer II Audio"); break; case coding_MPEG1_L3: snprintf(temp,TEMPSIZE,"MPEG-1 Layer III Audio (MP3)"); break; case coding_MPEG2_L1: snprintf(temp,TEMPSIZE,"MPEG-2 Layer I Audio"); break; case coding_MPEG2_L2: snprintf(temp,TEMPSIZE,"MPEG-2 Layer II Audio"); break; case coding_MPEG2_L3: snprintf(temp,TEMPSIZE,"MPEG-2 Layer III Audio (MP3)"); break; case coding_MPEG25_L1: snprintf(temp,TEMPSIZE,"MPEG-2.5 Layer I Audio"); break; case coding_MPEG25_L2: snprintf(temp,TEMPSIZE,"MPEG-2.5 Layer II Audio"); break; case coding_MPEG25_L3: snprintf(temp,TEMPSIZE,"MPEG-2.5 Layer III Audio (MP3)"); break; #endif case coding_ACM: snprintf(temp,TEMPSIZE,"InterPlay ACM"); break; default: snprintf(temp,TEMPSIZE,"CANNOT DECODE"); } concatn(length,desc,temp); snprintf(temp,TEMPSIZE,"\nlayout: "); concatn(length,desc,temp); switch (vgmstream->layout_type) { case layout_none: snprintf(temp,TEMPSIZE,"flat (no layout)"); break; case layout_interleave: snprintf(temp,TEMPSIZE,"interleave"); break; case layout_interleave_shortblock: snprintf(temp,TEMPSIZE,"interleave with short last block"); break; case layout_interleave_byte: snprintf(temp,TEMPSIZE,"sub-frame interleave"); break; case layout_dtk_interleave: snprintf(temp,TEMPSIZE,"ADP/DTK nibble interleave"); break; case layout_ast_blocked: snprintf(temp,TEMPSIZE,"AST blocked"); break; case layout_halpst_blocked: snprintf(temp,TEMPSIZE,"HALPST blocked"); break; case layout_xa_blocked: snprintf(temp,TEMPSIZE,"CD-ROM XA"); break; case layout_ea_blocked: snprintf(temp,TEMPSIZE,"Electronic Arts Audio Blocks"); break; case layout_eacs_blocked: snprintf(temp,TEMPSIZE,"Electronic Arts (Old Version) Audio Blocks"); break; case layout_caf_blocked: snprintf(temp,TEMPSIZE,"CAF blocked"); break; case layout_wsi_blocked: snprintf(temp,TEMPSIZE,".wsi blocked"); break; #ifdef VGM_USE_VORBIS case layout_ogg_vorbis: snprintf(temp,TEMPSIZE,"Ogg"); break; #endif case layout_str_snds_blocked: snprintf(temp,TEMPSIZE,".str SNDS blocked"); break; case layout_ws_aud_blocked: snprintf(temp,TEMPSIZE,"Westwood Studios .aud blocked"); break; #ifdef VGM_USE_MPEG case layout_fake_mpeg: snprintf(temp,TEMPSIZE,"MPEG Audio stream with incorrect frame headers"); break; case layout_mpeg: snprintf(temp,TEMPSIZE,"MPEG Audio stream"); break; #endif case layout_acm: snprintf(temp,TEMPSIZE,"However ACM Does That"); break; default: snprintf(temp,TEMPSIZE,"INCONCEIVABLE"); } concatn(length,desc,temp); snprintf(temp,TEMPSIZE,"\n"); concatn(length,desc,temp); if (vgmstream->layout_type == layout_interleave || vgmstream->layout_type == layout_interleave_shortblock || vgmstream->layout_type == layout_interleave_byte) { snprintf(temp,TEMPSIZE,"interleave: %#x bytes\n", (int32_t)vgmstream->interleave_block_size); concatn(length,desc,temp); if (vgmstream->layout_type == layout_interleave_shortblock) { snprintf(temp,TEMPSIZE,"last block interleave: %#x bytes\n", (int32_t)vgmstream->interleave_smallblock_size); concatn(length,desc,temp); } } snprintf(temp,TEMPSIZE,"metadata from: "); concatn(length,desc,temp); switch (vgmstream->meta_type) { case meta_RSTM: snprintf(temp,TEMPSIZE,"Nintendo RSTM header"); break; case meta_STRM: snprintf(temp,TEMPSIZE,"Nintendo STRM header"); break; case meta_ADX_03: snprintf(temp,TEMPSIZE,"CRI ADX header type 03"); break; case meta_ADX_04: snprintf(temp,TEMPSIZE,"CRI ADX header type 04"); break; case meta_ADX_05: snprintf(temp,TEMPSIZE,"CRI ADX header type 05"); break; case meta_DSP_AGSC: snprintf(temp,TEMPSIZE,"Retro Studios AGSC header"); break; case meta_NGC_ADPDTK: 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 and valid first bytes"); break; case meta_AFC: snprintf(temp,TEMPSIZE,"Nintendo AFC header"); break; case meta_AST: snprintf(temp,TEMPSIZE,"Nintendo AST header"); break; case meta_HALPST: 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 Nintendo DSP header"); break; case meta_DSP_CSTR: snprintf(temp,TEMPSIZE,"Namco Cstr header"); break; case meta_GCSW: snprintf(temp,TEMPSIZE,"GCSW header"); break; case meta_PS2_SShd: snprintf(temp,TEMPSIZE,"SShd header"); break; case meta_PS2_NPSF: snprintf(temp,TEMPSIZE,"Namco Production Sound File (NPSF) header"); break; case meta_RWSD: snprintf(temp,TEMPSIZE,"Nintendo RWSD header (single stream)"); break; case meta_PSX_XA: snprintf(temp,TEMPSIZE,"RIFF/CDXA header"); break; case meta_PS2_RXW: snprintf(temp,TEMPSIZE,"RXWS header)"); break; case meta_PS2_RAW: snprintf(temp,TEMPSIZE,"assumed RAW Interleaved PCM by .int extension"); break; case meta_DSP_STM: snprintf(temp,TEMPSIZE,"Nintendo STM header"); break; case meta_PS2_EXST: snprintf(temp,TEMPSIZE,"EXST header"); break; case meta_PS2_SVAG: snprintf(temp,TEMPSIZE,"Konami SVAG header"); break; case meta_PS2_MIB: snprintf(temp,TEMPSIZE,"assumed MIB Interleaved file by .mib extension"); break; case meta_PS2_MIB_MIH: snprintf(temp,TEMPSIZE,"assumed MIB with MIH Info Header file by .mib+.mih extension"); break; case meta_DSP_MPDSP: snprintf(temp,TEMPSIZE,"Single DSP header stereo by .mpdsp extension"); break; case meta_PS2_MIC: snprintf(temp,TEMPSIZE,"assume KOEI MIC file by .mic extension"); break; case meta_DSP_JETTERS: snprintf(temp,TEMPSIZE,"Double DSP header stereo by _lr.dsp extension"); break; case meta_DSP_MSS: snprintf(temp,TEMPSIZE,"Double DSP header stereo by .mss extension"); break; case meta_DSP_GCM: snprintf(temp,TEMPSIZE,"Double DSP header stereo by .gcm extension"); break; case meta_DSP_IDSP: snprintf(temp,TEMPSIZE,"GCM file with IDSP Header"); break; case meta_RSTM_SPM: snprintf(temp,TEMPSIZE,"Nintendo RSTM header and .brstmspm extension"); break; case meta_RAW: snprintf(temp,TEMPSIZE,"assumed RAW PCM file by .raw extension"); break; case meta_PS2_VAGi: snprintf(temp,TEMPSIZE,"Sony VAG Interleaved header (VAGi)"); break; case meta_PS2_VAGp: snprintf(temp,TEMPSIZE,"Sony VAG Mono header (VAGp)"); break; case meta_PS2_VAGs: snprintf(temp,TEMPSIZE,"Sony VAG Stereo header (VAGp)"); break; case meta_PS2_VAGm: snprintf(temp,TEMPSIZE,"Sony VAG Mono header (VAGm)"); break; case meta_PS2_pGAV: snprintf(temp,TEMPSIZE,"Sony VAG Stereo Little Endian header (pGAV)"); break; case meta_PSX_GMS: snprintf(temp,TEMPSIZE,"assumed Grandia GMS file by .gms extension"); break; case meta_PS2_STR: snprintf(temp,TEMPSIZE,"assumed STR + STH File by .str & .sth extension"); break; case meta_PS2_ILD: snprintf(temp,TEMPSIZE,"ILD header"); break; case meta_PS2_PNB: snprintf(temp,TEMPSIZE,"assumed PNB (PsychoNauts Bgm File) by .pnb extension"); break; case meta_XBOX_WAVM: snprintf(temp,TEMPSIZE,"assumed Xbox WAVM file by .wavm extension"); break; case meta_XBOX_RIFF: snprintf(temp,TEMPSIZE,"Xbox RIFF/WAVE file with 0x0069 Codec ID"); break; case meta_DSP_STR: snprintf(temp,TEMPSIZE,"assumed Conan Gamecube STR File by .str extension"); break; case meta_EAXA_R2: snprintf(temp,TEMPSIZE,"Electronic Arts XA R2"); break; case meta_EAXA_R3: snprintf(temp,TEMPSIZE,"Electronic Arts XA R3"); break; case meta_EA_ADPCM: snprintf(temp,TEMPSIZE,"Electronic Arts XA R1"); break; case meta_EAXA_PSX: snprintf(temp,TEMPSIZE,"Electronic Arts With PSX ADPCM"); break; case meta_EA_PCM: snprintf(temp,TEMPSIZE,"Electronic Arts With PCM"); break; case meta_CFN: snprintf(temp,TEMPSIZE,"Namco CAF Header"); break; case meta_PS2_VPK: snprintf(temp,TEMPSIZE,"VPK Header"); break; case meta_GENH: snprintf(temp,TEMPSIZE,"GENH Generic Header"); break; #ifdef VGM_USE_VORBIS case meta_ogg_vorbis: snprintf(temp,TEMPSIZE,"Ogg Vorbis"); break; #endif case meta_DSP_SADB: snprintf(temp,TEMPSIZE,"sadb header"); break; case meta_PS2_BMDX: snprintf(temp,TEMPSIZE,"Beatmania .bmdx header"); break; case meta_DSP_WSI: snprintf(temp,TEMPSIZE,".wsi header"); break; case meta_AIFC: snprintf(temp,TEMPSIZE,"Audio Interchange File Format AIFF-C"); break; case meta_AIFF: snprintf(temp,TEMPSIZE,"Audio Interchange File Format"); break; case meta_STR_SNDS: snprintf(temp,TEMPSIZE,".str SNDS SHDR chunk"); break; case meta_WS_AUD: snprintf(temp,TEMPSIZE,"Westwood Studios .aud header"); break; case meta_WS_AUD_old: snprintf(temp,TEMPSIZE,"Westwood Studios .aud (old) header"); break; #ifdef VGM_USE_MPEG case meta_AHX: snprintf(temp,TEMPSIZE,"CRI AHX header"); break; #endif case meta_PS2_IVB: snprintf(temp,TEMPSIZE,"IVB/BVII header"); break; case meta_PS2_SVS: snprintf(temp,TEMPSIZE,"Square SVS header"); break; case meta_RIFF_WAVE: snprintf(temp,TEMPSIZE,"RIFF WAVE header"); break; case meta_RIFF_WAVE_POS: snprintf(temp,TEMPSIZE,"RIFF WAVE header and .pos for looping"); break; case meta_NWA: snprintf(temp,TEMPSIZE,"Visual Art's NWA header"); break; case meta_NWA_NWAINFOINI: snprintf(temp,TEMPSIZE,"Visual Art's NWA header and NWAINFO.INI for looping"); break; case meta_NWA_GAMEEXEINI: snprintf(temp,TEMPSIZE,"Visual Art's NWA header and Gameexe.ini for looping"); break; case meta_XSS: snprintf(temp,TEMPSIZE,"Dino Crisis 3 XSS File"); break; case meta_HGC1: snprintf(temp,TEMPSIZE,"Knights of the Temple 2 hgC1 Header"); break; case meta_AUS: snprintf(temp,TEMPSIZE,"Capcom AUS Header"); break; case meta_RWS: snprintf(temp,TEMPSIZE,"RWS Header"); break; case meta_EACS_PC: snprintf(temp,TEMPSIZE,"EACS Header (PC)"); break; case meta_EACS_PSX: snprintf(temp,TEMPSIZE,"EACS Header (PSX)"); break; case meta_EACS_SAT: snprintf(temp,TEMPSIZE,"EACS Header (SATURN)"); break; case meta_RSD: snprintf(temp,TEMPSIZE,"RSD4 or RSD6 Header"); break; case meta_SL3: snprintf(temp,TEMPSIZE,"SL3 Header"); break; case meta_FSB3: snprintf(temp,TEMPSIZE,"FMOD Sample Bank (FSB3) Header"); break; case meta_RWX: snprintf(temp,TEMPSIZE,"RWX Header"); break; case meta_XWB: snprintf(temp,TEMPSIZE,"XWB WBND Header"); break; case meta_XA30: snprintf(temp,TEMPSIZE,"XA30 Header"); break; case meta_MUSC: snprintf(temp,TEMPSIZE,"MUSC Header"); break; case meta_MUSX: snprintf(temp,TEMPSIZE,"MUSX Header"); break; case meta_LEG: snprintf(temp,TEMPSIZE,"Legaia 2 - Duel Saga LEG Header"); break; case meta_FILP: snprintf(temp,TEMPSIZE,"Bio Hazard - Gun Survivor FILp/GEMp Header"); break; case meta_IKM: snprintf(temp,TEMPSIZE,"Zwei!! IKM Header"); break; case meta_SFS: snprintf(temp,TEMPSIZE,"Baroque SFS Header"); break; case meta_DVI: snprintf(temp,TEMPSIZE,"DVI Header"); break; case meta_KCEY: snprintf(temp,TEMPSIZE,"KCEYCOMP Header"); break; case meta_BG00: snprintf(temp,TEMPSIZE,"Falcom BG00 Header"); break; case meta_PS2_RSTM: snprintf(temp,TEMPSIZE,"Rockstar Games RSTM Header"); break; case meta_ACM: snprintf(temp,TEMPSIZE,"InterPlay ACM Header"); break; case meta_PS2_KCES: snprintf(temp,TEMPSIZE,"Konami KCES Header"); break; case meta_PS2_DXH: snprintf(temp,TEMPSIZE,"Tokobot Plus DXH Header"); break; default: snprintf(temp,TEMPSIZE,"THEY SHOULD HAVE SENT A POET"); } concatn(length,desc,temp); } /* */ #define DFS_PAIR_COUNT 4 const char * const dfs_pairs[DFS_PAIR_COUNT][2] = { {"L","R"}, {"l","r"}, {"_0","_1"}, {"left","right"}, }; void try_dual_file_stereo(VGMSTREAM * opened_stream, STREAMFILE *streamFile) { char filename[260]; char filename2[260]; char * ext; int dfs_name= -1; /*-1=no stereo, 0=opened_stream is left, 1=opened_stream is right */ VGMSTREAM * new_stream = NULL; STREAMFILE *dual_stream = NULL; int i,j; if (opened_stream->channels != 1) return; streamFile->get_name(streamFile,filename,sizeof(filename)); /* vgmstream's layout stuff currently assumes a single file */ // fastelbja : no need ... this one works ok with dual file //if (opened_stream->layout != layout_none) return; /* we need at least a base and a name ending to replace */ if (strlen(filename)<2) return; strcpy(filename2,filename); /* look relative to the extension; */ ext = (char *)filename_extension(filename2); /* we treat the . as part of the extension */ if (ext-filename2 >= 1 && ext[-1]=='.') ext--; for (i=0; dfs_name==-1 && iopen(streamFile,filename2,STREAMFILE_DEFAULT_BUFFER_SIZE); if (!dual_stream) goto fail; new_stream = init_vgmstream_internal(dual_stream, 0 /* don't do dual file on this, to prevent recursion */ ); close_streamfile(dual_stream); /* see if we were able to open the file, and if everything matched nicely */ if (new_stream && new_stream->channels == 1 && /* we have seen legitimate pairs where these are off by one... */ /* but leaving it commented out until I can find those and recheck */ /* abs(new_stream->num_samples-opened_stream->num_samples <= 1) && */ new_stream->num_samples == opened_stream->num_samples && new_stream->sample_rate == opened_stream->sample_rate && new_stream->meta_type == opened_stream->meta_type && new_stream->coding_type == opened_stream->coding_type && new_stream->layout_type == opened_stream->layout_type && new_stream->loop_flag == opened_stream->loop_flag && /* check these even if there is no loop, because they should then * be zero in both */ new_stream->loop_start_sample == opened_stream->loop_start_sample && new_stream->loop_end_sample == opened_stream->loop_end_sample && /* check even if the layout doesn't use them, because it is * difficult to determine when it does, and they should be zero * otherwise, anyway */ new_stream->interleave_block_size == opened_stream->interleave_block_size && new_stream->interleave_smallblock_size == opened_stream->interleave_smallblock_size) { /* We seem to have a usable, matching file. Merge in the second channel. */ VGMSTREAMCHANNEL * new_chans; VGMSTREAMCHANNEL * new_loop_chans = NULL; VGMSTREAMCHANNEL * new_start_chans = NULL; /* build the channels */ new_chans = calloc(2,sizeof(VGMSTREAMCHANNEL)); if (!new_chans) goto fail; memcpy(&new_chans[dfs_name],&opened_stream->ch[0],sizeof(VGMSTREAMCHANNEL)); memcpy(&new_chans[dfs_name^1],&new_stream->ch[0],sizeof(VGMSTREAMCHANNEL)); /* loop and start will be initialized later, we just need to * allocate them here */ new_start_chans = calloc(2,sizeof(VGMSTREAMCHANNEL)); if (!new_start_chans) { free(new_chans); goto fail; } if (opened_stream->loop_ch) { new_loop_chans = calloc(2,sizeof(VGMSTREAMCHANNEL)); if (!new_loop_chans) { free(new_chans); free(new_start_chans); goto fail; } } /* remove the existing structures */ /* not using close_vgmstream as that would close the file */ free(opened_stream->ch); free(new_stream->ch); free(opened_stream->start_ch); free(new_stream->start_ch); if (opened_stream->loop_ch) { free(opened_stream->loop_ch); free(new_stream->loop_ch); } /* fill in the new structures */ opened_stream->ch = new_chans; opened_stream->start_ch = new_start_chans; opened_stream->loop_ch = new_loop_chans; /* stereo! */ opened_stream->channels = 2; /* discard the second VGMSTREAM */ free(new_stream); } fail: return; }