Redo .rwav/rwar and add .brwav

This commit is contained in:
bnnm 2022-01-30 13:33:21 +01:00
parent f6a48926b7
commit e76ccb6ab7
4 changed files with 271 additions and 251 deletions

View File

@ -3,11 +3,31 @@
#include "../util/endianness.h"
/* FWAV and CWAV are basically identical except always LE */
typedef enum { FWAV, CWAV } bxwav_type_t;
/* RWAV is a bit simpler, while FWAV and CWAV are basically identical except for endianness.
* From SDK info, typically .xxxx is the original NintendoWare format (usually a .xml) and .bxxxx is a
* binary/external form (like .rseq=xml then .brseq=binary, or .wav > brwav). */
typedef enum { RWAV, FWAV, CWAV } bxwav_type_t;
static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type);
/* RWAV - NintendoWare binary Revolution wave (Wii games) */
VGMSTREAM* init_vgmstream_brwav(STREAMFILE* sf) {
/* checks */
if (!is_id32be(0x00, sf, "RWAV"))
goto fail;
/* .brwav: from tools (no games known)
* .rwav: header id */
if (!check_extensions(sf, "brwav,rwav"))
goto fail;
return init_vgmstream_bxwav(sf, RWAV);
fail:
return NULL;
}
/* FWAV - NintendoWare binary caFe wave (WiiU and Switch games) */
VGMSTREAM* init_vgmstream_bfwav(STREAMFILE* sf) {
@ -21,7 +41,6 @@ VGMSTREAM* init_vgmstream_bfwav(STREAMFILE* sf) {
goto fail;
return init_vgmstream_bxwav(sf, FWAV);
fail:
return NULL;
}
@ -43,7 +62,6 @@ VGMSTREAM* init_vgmstream_bcwav(STREAMFILE* sf) {
goto fail;
return init_vgmstream_bxwav(sf, CWAV);
fail:
return NULL;
}
@ -52,7 +70,7 @@ fail:
static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type) {
VGMSTREAM* vgmstream = NULL;
uint32_t info_offset, data_offset, chtb_offset;
uint32_t info_offset, data_offset, chtb_offset, file_size;
int channels, loop_flag, codec, sample_rate;
int big_endian;
int32_t num_samples, loop_start;
@ -63,7 +81,7 @@ static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type) {
read_s16_t read_s16;
/* BOM check */
if (read_u16be(0x04, sf) == 0xFEFF) { /* WiiU */
if (read_u16be(0x04, sf) == 0xFEFF) { /* Wii, WiiU */
big_endian = 1;
read_u32 = read_u32be;
read_s32 = read_s32be;
@ -82,41 +100,95 @@ static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type) {
}
/* header */
/* 0x06(2): header size (0x40) */
/* 0x08: version */
/* - FWAV: 0x00010200 */
/* - CWAV: 0x00000002 (Kirby's Adventure), 0x00000102 (common), 0x00010102 (FE Fates, Hyrule Warriors Legends) */
/* 0x0c: file size */
/* 0x10(2): sections (2) */
switch(type) {
case RWAV:
/* 0x06(2): version (usually 0102, 0103=Let's Tap, no diffs) */
file_size = read_u32(0x08, sf);
/* 0x0c(2): header size (0x20) */
/* 0x0e(2): sections (2) */
/* 0x14(2): info mark (0x7000) */
info_offset = read_u32(0x18, sf);
/* 0x1c: info size */
info_offset = read_u32(0x10, sf);
/* 0x14: info size */
/* 0x20(2): data mark (0x7001) */
data_offset = read_u32(0x24, sf);
/* 0x28: data size */
/* rest: padding */
data_offset = read_u32(0x18, sf);
/* 0x1c: data size */
break;
case FWAV:
case CWAV:
/* 0x06(2): header size (0x40) */
/* 0x08: version */
/* - FWAV: 0x00010200 */
/* - CWAV: 0x00000002 (Kirby's Adventure), 0x00000102 (common), 0x00010102 (FE Fates, Hyrule Warriors Legends) */
file_size = read_u32(0x0c, sf);
/* 0x10(2): sections (2) */
/* 0x14(2): info mark (0x7000) */
info_offset = read_u32(0x18, sf);
/* 0x1c: info size */
/* 0x20(2): data mark (0x7001) */
data_offset = read_u32(0x24, sf);
/* 0x28: data size */
/* rest: padding */
break;
default:
goto fail;
}
if (file_size != get_streamfile_size(sf)) {
vgm_logi("BXWAV: wrong size %x vs %x\n", file_size, (uint32_t)get_streamfile_size(sf));
goto fail;
}
/* INFO section */
if (!is_id32be(info_offset + 0x00, sf, "INFO"))
goto fail;
/* 0x04: size */
codec = read_u8(info_offset + 0x08, sf);
loop_flag = read_u8(info_offset + 0x09, sf);
/* 0x0a: padding */
sample_rate = read_u32(info_offset + 0x0C, sf);
loop_start = read_s32(info_offset + 0x10, sf);
num_samples = read_s32(info_offset + 0x14, sf);
/* 0x18: original loop start? (slightly lower) */
chtb_offset = info_offset + 0x1C;
channels = read_u32(chtb_offset + 0x00, sf);
switch(type) {
case RWAV:
codec = read_u8(info_offset + 0x08, sf);
loop_flag = read_u8(info_offset + 0x09, sf);
channels = read_u8(info_offset + 0x0a, sf);
/* 0x0b: part of rate? */
sample_rate = read_u16(info_offset + 0x0c, sf);
/* 0x0e(2): padding */
loop_start = read_s32(info_offset + 0x10, sf);
num_samples = read_s32(info_offset + 0x14, sf);
chtb_offset = read_u32(info_offset + 0x18, sf) + info_offset + 0x08;
/* 0x1c: channel table size */
loop_start = dsp_nibbles_to_samples(loop_start);
num_samples = dsp_nibbles_to_samples(num_samples);
break;
case FWAV:
case CWAV:
codec = read_u8(info_offset + 0x08, sf);
loop_flag = read_u8(info_offset + 0x09, sf);
/* 0x0a: padding */
sample_rate = read_u32(info_offset + 0x0C, sf);
loop_start = read_s32(info_offset + 0x10, sf);
num_samples = read_s32(info_offset + 0x14, sf);
/* 0x18: original loop start? (slightly lower) */
chtb_offset = info_offset + 0x1C;
channels = read_u32(chtb_offset + 0x00, sf);
break;
default:
goto fail;
}
/* channel table is parsed at the end */
/* DATA section */
if (!is_id32be(data_offset + 0x00, sf, "DATA"))
goto fail;
/* 0x04: size */
/* build the VGMSTREAM */
@ -124,6 +196,7 @@ static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type) {
if (!vgmstream) goto fail;
switch(type) {
case RWAV: vgmstream->meta_type = meta_RWAV; break;
case FWAV: vgmstream->meta_type = meta_FWAV; break;
case CWAV: vgmstream->meta_type = meta_CWAV; break;
default: goto fail;
@ -138,7 +211,7 @@ static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type) {
vgmstream->layout_type = layout_none;
/* only 0x02 is known, other codecs are probably from bxstm that do use them */
/* only 0x02 is known, others can be made with SDK tools */
switch (codec) {
case 0x00:
vgmstream->coding_type = coding_PCM8;
@ -171,25 +244,51 @@ static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type) {
{
int ch, i;
for (ch = 0; ch < channels; ch++) {
uint32_t chnf_offset, chdt_offset;
/* channel entry: */
/* - 0x00: mark (0x7100) */
/* - 0x02: padding */
/* - 0x04: channel info offset (from channel table offset) */
chnf_offset = read_u32(chtb_offset + 0x04 + ch * 0x08 + 0x04, sf) + chtb_offset;
uint32_t chnf_offset, chdt_offset, coef_offset;
switch(type) {
case RWAV:
/* channel entry: */
/* - 0x04: channel info offset (from INFO offset after size) */
/* channel info: */
/* 0x00: mark (0x1F00) */
/* 0x02: padding */
/* 0x04: offset to channel data (from DATA offset after size ) */
/* 0x08: ADPCM mark (0x0300=DSP, 0x0301=IMA, 0x0000=none) */
/* 0x0a: padding */
/* 0x0c: ADPCM offset (from channel info offset), 0xFFFFFFFF otherwise */
/* 0x10: null? */
chnf_offset = read_u32(chtb_offset + ch * 0x04 + 0x00, sf) + info_offset + 0x08;
if (read_u16(chnf_offset + 0x00, sf) != 0x1F00)
goto fail;
chdt_offset = read_u32(chnf_offset + 0x04, sf) + data_offset + 0x08;
/* channel info: */
/* 0x00: offset to channel data (from DATA offset after size) */
/* 0x04: ADPCM offset (from INFO offset after size), 0xFFFFFFFF otherwise? */
/* 0x08: volumes x4? */
/* 0x18: padding */
chdt_offset = read_u32(chnf_offset + 0x00, sf) + data_offset + 0x08;
coef_offset = read_u32(chnf_offset + 0x04, sf) + info_offset + 0x08;
break;
case FWAV:
case CWAV:
/* channel entry: */
/* - 0x00: mark (0x7100) */
/* - 0x02: padding */
/* - 0x04: channel info offset (from channel table offset) */
chnf_offset = read_u32(chtb_offset + 0x04 + ch * 0x08 + 0x04, sf) + chtb_offset;
/* channel info: */
/* 0x00: mark (0x1F00) */
/* 0x02: padding */
/* 0x04: offset to channel data (from DATA offset after size) */
/* 0x08: ADPCM mark (0x0300=DSP, 0x0301=IMA, 0x0000=none) */
/* 0x0a: padding */
/* 0x0c: ADPCM offset (from channel info offset), 0xFFFFFFFF otherwise */
/* 0x10: padding */
if (read_u16(chnf_offset + 0x00, sf) != 0x1F00)
goto fail;
chdt_offset = read_u32(chnf_offset + 0x04, sf) + data_offset + 0x08;
coef_offset = read_u32(chnf_offset + 0x0c, sf) + chnf_offset;
break;
default:
goto fail;
}
vgmstream->ch[ch].channel_start_offset = chdt_offset;
vgmstream->ch[ch].offset = chdt_offset;
@ -197,20 +296,16 @@ static VGMSTREAM* init_vgmstream_bxwav(STREAMFILE* sf, bxwav_type_t type) {
switch(codec) {
case 0x02: {
/* standard DSP coef + predictor + hists + loop predictor + loop hists */
uint32_t coef_offset = read_u32(chnf_offset + 0x0c, sf) + chnf_offset;
for (i = 0; i < 16; i++) {
vgmstream->ch[ch].adpcm_coef[i] = read_s16(coef_offset + 0x00 + i*0x02, sf);
}
vgmstream->ch[ch].adpcm_history1_16 = read_s16(coef_offset + 0x22, sf);
vgmstream->ch[ch].adpcm_history2_16 = read_s16(coef_offset + 0x24, sf);
//vgmstream->ch[ch].adpcm_history1_16 = read_s16(coef_offset + 0x22, sf);
//vgmstream->ch[ch].adpcm_history2_16 = read_s16(coef_offset + 0x24, sf);
break;
}
case 0x03: {
/* hist + step */
uint32_t coef_offset = read_u32(chnf_offset + 0x0c, sf) + chnf_offset;
vgmstream->ch[ch].adpcm_history1_16 = read_s16(coef_offset + 0x00, sf);
vgmstream->ch[ch].adpcm_step_index = read_s16(coef_offset + 0x02, sf);
break;
@ -228,3 +323,73 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
/* ************************************************************************* */
/* RWAR - NintendoWare container [BIT.TRIP BEAT (Wii), Dance Dance Revolution Hottest Party 2 (Wii)] */
VGMSTREAM* init_vgmstream_brwar(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
STREAMFILE* temp_sf = NULL;
uint32_t tabl_offset, data_offset;
uint32_t subfile_offset, subfile_size;
int total_subsongs, target_subsong = sf->stream_index;
/* checks */
if (!is_id32be(0x00, sf, "RWAR"))
goto fail;
if (!check_extensions(sf,"rwar"))
goto fail;
/* simple container of .rwavs (inside .brsar), rarely used with single siles (DDR) */
/* abridged, see RWAV (same header) */
/* 0x04(2): BOM */
/* 0x06(2): version (usually 0100) */
/* 0x08: file size */
/* 0x0c(2): header size (0x20) */
/* 0x0e(2): sections (2) */
tabl_offset = read_u32be(0x10, sf);
/* 0x14: tabl size */
data_offset = read_u32be(0x18, sf);
/* 0x1c: data size */
/* TABL section */
if (!is_id32be(tabl_offset + 0x00, sf, "TABL"))
goto fail;
/* 0x04: size */
total_subsongs = read_u32be(tabl_offset + 0x08,sf);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
/* 0x00: always 0x01000000? */
subfile_offset = read_u32be(tabl_offset + 0x0c + (target_subsong-1) * 0x0c + 0x04,sf) + data_offset;
subfile_size = read_u32be(tabl_offset + 0x0c + (target_subsong-1) * 0x0c + 0x08,sf);
/* DATA section */
if (!is_id32be(data_offset + 0x00, sf, "DATA"))
goto fail;
/* 0x04: size */
//VGM_LOG("BRWAR: of=%x, sz=%x\n", subfile_offset, subfile_size);
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, "rwav");
if (!temp_sf) goto fail;
vgmstream = init_vgmstream_brwav(temp_sf);
if (!vgmstream) goto fail;
vgmstream->num_streams = total_subsongs;
close_streamfile(temp_sf);
return vgmstream;
fail:
close_streamfile(temp_sf);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -564,8 +564,10 @@ VGMSTREAM * init_vgmstream_bcstm(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_bfstm(STREAMFILE* streamFile);
VGMSTREAM* init_vgmstream_brwav(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_bfwav(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_bcwav(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_brwar(STREAMFILE* sf);
VGMSTREAM * init_vgmstream_kt_g1l(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_kt_wiibgm(STREAMFILE* streamFile);

View File

@ -2,177 +2,64 @@
#include "../coding/coding.h"
#include "../util.h"
/* Wii RWAV */
typedef struct {
// in
off_t offset;
STREAMFILE *sf;
int32_t (*read_32bit)(off_t,STREAMFILE*);
// out
int version;
off_t start_offset;
off_t info_chunk;
off_t wave_offset;
} rwav_data_t;
static void read_rwav(rwav_data_t* rd) {
off_t chunk_table_offset;
off_t chunk_table_step;
off_t info_chunk;
off_t data_chunk;
if (!is_id32be(rd->offset, rd->sf, "RWAV"))
return;
/* big endian, version 2 */
if (read_u32be(rd->offset+4,rd->sf) != 0xFEFF0102)
return;
chunk_table_offset = rd->offset + 0x10;
chunk_table_step = 0x08;
info_chunk = rd->offset + rd->read_32bit(chunk_table_offset, rd->sf);
if (!is_id32be(info_chunk, rd->sf, "INFO"))
return;
data_chunk = rd->offset + rd->read_32bit(chunk_table_offset + chunk_table_step, rd->sf);
if (!is_id32be(data_chunk, rd->sf, "DATA"))
return;
rd->start_offset = data_chunk + 0x08;
rd->info_chunk = info_chunk + 0x08;
rd->version = 2;
rd->wave_offset = info_chunk - 0x08; /* pretend to have a WAVE */
return;
}
static void read_rwar(rwav_data_t* rd) {
if (!is_id32be(rd->offset, rd->sf, "RWAR"))
return;
if (read_u32be(rd->offset + 0x04, rd->sf) != 0xFEFF0100) /* version 0 */
return;
rd->offset += 0x60;
read_rwav(rd);
rd->version = 0;
return;
}
/* RWSD is quite similar to BRSTM, but can contain several streams.
* Still, some games use it for single streams. We only support the
* single stream form here */
* Still, some games use it for single streams. We only support the single stream form here */
//TODO this meta is a hack as WSD is just note info, and data offsets are elsewhere,
// while this assumes whatever data follows RWSD must belong to it; rework for Wii Sports
VGMSTREAM* init_vgmstream_rwsd(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
char filename[PATH_LIMIT];
size_t wave_length;
int codec;
int channels;
int loop_flag;
int rwar = 0;
int rwav = 0;
rwav_data_t rwav_data;
int codec, channels, loop_flag;
size_t stream_size;
off_t start_offset, wave_offset = 0;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
rwav_data.version = -1;
rwav_data.start_offset = 0;
rwav_data.info_chunk = -1;
rwav_data.wave_offset = -1;
/* check extension, case insensitive */
sf->get_name(sf,filename,sizeof(filename));
if (check_extensions(sf, "rwsd")) {
;
}
else if (check_extensions(sf, "rwar")) {
rwar = 1;
}
else if (check_extensions(sf, "rwav")) {
rwav = 1;
}
else {
if (!is_id32be(0x00, sf, "RWSD"))
goto fail;
}
read_16bit = read_16bitBE;
read_32bit = read_32bitBE;
if (!check_extensions(sf, "rwsd"))
goto fail;
/* check header */
if (rwar || rwav) {
rwav_data.offset = 0;
rwav_data.sf = sf;
rwav_data.read_32bit = read_32bit;
switch (read_u32be(0x04, sf)) {
case 0xFEFF0102:
/* ideally we would look through the chunk list for a WAVE chunk,
* but it's always in the same order */
if (rwar) read_rwar(&rwav_data);
if (rwav) read_rwav(&rwav_data);
if (rwav_data.wave_offset < 0) goto fail;
}
else {
if (!is_id32be(0x00, sf, "RWSD"))
goto fail;
switch (read_u32be(0x04, sf)) {
case 0xFEFF0102:
/* ideally we would look through the chunk list for a WAVE chunk,
* but it's always in the same order */
/* get WAVE offset, check */
rwav_data.wave_offset = read_32bit(0x18,sf);
if (!is_id32be(rwav_data.wave_offset + 0x00, sf, "WAVE"))
goto fail;
/* get WAVE size, check */
wave_length = read_32bit(0x1c,sf);
if (read_32bit(rwav_data.wave_offset + 0x04,sf) != wave_length)
goto fail;
/* check wave count */
if (read_32bit(rwav_data.wave_offset + 0x08,sf) != 1)
goto fail; /* only support 1 */
rwav_data.version = 2;
break;
case 0xFEFF0103:
rwav_data.offset = 0xe0;
rwav_data.sf = sf;
rwav_data.read_32bit = read_32bit;
read_rwar(&rwav_data);
if (rwav_data.wave_offset < 0) goto fail;
rwar = 1;
break;
default:
/* get WAVE offset, check */
wave_offset = read_32bitBE(0x18,sf);
if (!is_id32be(wave_offset + 0x00, sf, "WAVE"))
goto fail;
}
/* get WAVE size, check */
wave_length = read_32bitBE(0x1c,sf);
if (read_32bitBE(wave_offset + 0x04,sf) != wave_length)
goto fail;
/* check wave count */
if (read_32bitBE(wave_offset + 0x08,sf) != 1)
goto fail; /* only support 1 */
break;
case 0xFEFF0103: /* followed by RWAR, extract that or use .txth subfile */
goto fail;
}
/* get type details */
codec = read_u8(rwav_data.wave_offset+0x10,sf);
loop_flag = read_u8(rwav_data.wave_offset+0x11,sf);
channels = read_u8(rwav_data.wave_offset+0x12,sf);
codec = read_u8(wave_offset+0x10,sf);
loop_flag = read_u8(wave_offset+0x11,sf);
channels = read_u8(wave_offset+0x12,sf);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels,loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->num_samples = dsp_nibbles_to_samples(read_32bit(rwav_data.wave_offset+0x1c,sf));
vgmstream->loop_start_sample = dsp_nibbles_to_samples(read_32bit(rwav_data.wave_offset+0x18,sf));
vgmstream->num_samples = dsp_nibbles_to_samples(read_32bitBE(wave_offset+0x1c,sf));
vgmstream->loop_start_sample = dsp_nibbles_to_samples(read_32bitBE(wave_offset+0x18,sf));
vgmstream->sample_rate = (uint16_t)read_16bit(rwav_data.wave_offset + 0x14,sf);
vgmstream->sample_rate = (uint16_t)read_16bitBE(wave_offset + 0x14,sf);
vgmstream->loop_end_sample = vgmstream->num_samples;
switch (codec) {
@ -190,84 +77,48 @@ VGMSTREAM* init_vgmstream_rwsd(STREAMFILE* sf) {
}
vgmstream->layout_type = layout_none;
if (rwar) {
vgmstream->meta_type = meta_RWAR;
}
else if (rwav) {
vgmstream->meta_type = meta_RWAV;
}
else {
vgmstream->meta_type = meta_RWSD;
}
vgmstream->meta_type = meta_RWSD;
{
off_t data_start_offset;
off_t codec_info_offset;
int i, j;
for (j = 0 ; j < vgmstream->channels; j++) {
if (rwar || rwav) {
/* This is pretty nasty, so an explaination is in order.
* At 0x10 in the info_chunk is the offset of a table with
* one entry per channel. Each entry in this table is itself
* an offset to a set of information for the channel. The
* first element in the set is the offset into DATA of the channel.
* The second element is the offset of the codec-specific setup for the channel. */
off_t channel_info_offset = rwav_data.info_chunk +
read_32bit(rwav_data.info_chunk +
read_32bit(rwav_data.info_chunk + 0x10,sf) + j*0x04, sf);
data_start_offset = rwav_data.start_offset +
read_32bit(channel_info_offset + 0x00, sf);
codec_info_offset = rwav_data.info_chunk +
read_32bit(channel_info_offset + 0x04, sf);
vgmstream->ch[j].channel_start_offset =
vgmstream->ch[j].offset = data_start_offset;
} else {
// dummy for RWSD, must be a proper way to work this out
codec_info_offset = rwav_data.wave_offset + 0x6c + j*0x30;
}
// dummy for RWSD, must be a proper way to work this out
codec_info_offset = wave_offset + 0x6c + j*0x30;
if (vgmstream->coding_type == coding_NGC_DSP) {
for (i = 0; i < 16; i++) {
vgmstream->ch[j].adpcm_coef[i] = read_16bit(codec_info_offset + i*0x2, sf);
vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(codec_info_offset + i*0x2, sf);
}
}
}
}
if (rwar || rwav) {
/* */
}
else {
if (rwav_data.version == 2)
rwav_data.start_offset = read_32bit(0x08, sf);
}
/* this is just data size and following data may or may not be from this RWSD */
start_offset = read_32bitBE(0x08, sf);
stream_size = read_32bit(rwav_data.wave_offset + 0x50,sf);
stream_size = read_32bitBE(wave_offset + 0x50,sf);
/* open the file for reading by each channel */
{
int i;
for (i=0;i<channels;i++) {
char filename[PATH_LIMIT];
sf->get_name(sf,filename,sizeof(filename));
for (i = 0; i < channels; i++) {
vgmstream->ch[i].streamfile = sf->open(sf,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!vgmstream->ch[i].streamfile) goto fail;
if (!(rwar || rwav)) {
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=
rwav_data.start_offset + i*stream_size;
}
vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset =
start_offset + i*stream_size;
}
}
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;

View File

@ -20,8 +20,10 @@ static void try_dual_file_stereo(VGMSTREAM* opened_vgmstream, STREAMFILE* sf, VG
VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_adx,
init_vgmstream_brstm,
init_vgmstream_brwav,
init_vgmstream_bfwav,
init_vgmstream_bcwav,
init_vgmstream_brwar,
init_vgmstream_nds_strm,
init_vgmstream_afc,
init_vgmstream_ast,