mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-19 08:07:23 +01:00
Fix .xws with multibanks [Ninja Gaiden 3 RE (PS3), DOA 5 LR (PC)]
This commit is contained in:
parent
3b487ab7ba
commit
f4a5125440
438
src/meta/kwb.c
438
src/meta/kwb.c
@ -7,6 +7,7 @@ typedef struct {
|
|||||||
int big_endian;
|
int big_endian;
|
||||||
int total_subsongs;
|
int total_subsongs;
|
||||||
int target_subsong;
|
int target_subsong;
|
||||||
|
int found;
|
||||||
kwb_codec codec;
|
kwb_codec codec;
|
||||||
|
|
||||||
int channels;
|
int channels;
|
||||||
@ -26,6 +27,7 @@ typedef struct {
|
|||||||
|
|
||||||
static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b);
|
static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b);
|
||||||
static int parse_xws(kwb_header* kwb, STREAMFILE* sf);
|
static int parse_xws(kwb_header* kwb, STREAMFILE* sf);
|
||||||
|
static VGMSTREAM* init_vgmstream_koei_wavebank(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b);
|
||||||
|
|
||||||
|
|
||||||
/* KWB - WaveBank from Koei games */
|
/* KWB - WaveBank from Koei games */
|
||||||
@ -33,7 +35,6 @@ VGMSTREAM* init_vgmstream_kwb(STREAMFILE* sf) {
|
|||||||
VGMSTREAM* vgmstream = NULL;
|
VGMSTREAM* vgmstream = NULL;
|
||||||
STREAMFILE *sf_h = NULL, *sf_b = NULL;
|
STREAMFILE *sf_h = NULL, *sf_b = NULL;
|
||||||
kwb_header kwb = {0};
|
kwb_header kwb = {0};
|
||||||
int32_t (*read_s32)(off_t,STREAMFILE*) = NULL;
|
|
||||||
int target_subsong = sf->stream_index;
|
int target_subsong = sf->stream_index;
|
||||||
|
|
||||||
|
|
||||||
@ -70,89 +71,11 @@ VGMSTREAM* init_vgmstream_kwb(STREAMFILE* sf) {
|
|||||||
|
|
||||||
if (!parse_kwb(&kwb, sf_h, sf_b))
|
if (!parse_kwb(&kwb, sf_h, sf_b))
|
||||||
goto fail;
|
goto fail;
|
||||||
read_s32 = kwb.big_endian ? read_s32be : read_s32le;
|
|
||||||
|
|
||||||
|
vgmstream = init_vgmstream_koei_wavebank(&kwb, sf_h, sf_b);
|
||||||
/* build the VGMSTREAM */
|
|
||||||
vgmstream = allocate_vgmstream(kwb.channels, kwb.loop_flag);
|
|
||||||
if (!vgmstream) goto fail;
|
if (!vgmstream) goto fail;
|
||||||
|
|
||||||
vgmstream->meta_type = meta_KWB;
|
|
||||||
vgmstream->sample_rate = kwb.sample_rate;
|
|
||||||
vgmstream->num_samples = kwb.num_samples;
|
|
||||||
vgmstream->stream_size = kwb.stream_size;
|
|
||||||
vgmstream->num_streams = kwb.total_subsongs;
|
|
||||||
|
|
||||||
switch(kwb.codec) {
|
|
||||||
case PCM16: /* PCM */
|
|
||||||
vgmstream->coding_type = coding_PCM16LE;
|
|
||||||
vgmstream->layout_type = layout_interleave;
|
|
||||||
vgmstream->interleave_block_size = 0x02;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MSADPCM:
|
|
||||||
vgmstream->coding_type = coding_MSADPCM;
|
|
||||||
vgmstream->layout_type = layout_none;
|
|
||||||
vgmstream->frame_size = kwb.block_size;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DSP_HEAD:
|
|
||||||
case DSP_BODY:
|
|
||||||
if (kwb.channels > 1) goto fail;
|
|
||||||
vgmstream->coding_type = coding_NGC_DSP; /* subinterleave? */
|
|
||||||
vgmstream->layout_type = layout_interleave;
|
|
||||||
vgmstream->interleave_block_size = 0x08;
|
|
||||||
if (kwb.codec == DSP_HEAD) {
|
|
||||||
dsp_read_coefs(vgmstream, sf_h, kwb.dsp_offset + 0x1c, 0x60, kwb.big_endian);
|
|
||||||
dsp_read_hist (vgmstream, sf_h, kwb.dsp_offset + 0x40, 0x60, kwb.big_endian);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* typical DSP header + data */
|
|
||||||
vgmstream->num_samples = read_s32(kwb.stream_offset + 0x00, sf_b);
|
|
||||||
dsp_read_coefs(vgmstream, sf_b, kwb.stream_offset + 0x1c, 0x60, kwb.big_endian);
|
|
||||||
dsp_read_hist (vgmstream, sf_b, kwb.stream_offset + 0x40, 0x60, kwb.big_endian);
|
|
||||||
kwb.stream_offset += 0x60;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
#ifdef VGM_USE_ATRAC9
|
|
||||||
case AT9: {
|
|
||||||
atrac9_config cfg = {0};
|
|
||||||
|
|
||||||
{
|
|
||||||
size_t extra_size = read_u32le(kwb.stream_offset + 0x00, sf_b);
|
|
||||||
uint32_t config_data = read_u32be(kwb.stream_offset + 0x04, sf_b);
|
|
||||||
/* 0x0c: encoder delay? */
|
|
||||||
/* 0x0e: encoder padding? */
|
|
||||||
/* 0x10: samples per frame */
|
|
||||||
/* 0x12: frame size */
|
|
||||||
|
|
||||||
cfg.channels = vgmstream->channels;
|
|
||||||
cfg.config_data = config_data;
|
|
||||||
|
|
||||||
kwb.stream_offset += extra_size;
|
|
||||||
kwb.stream_size -= extra_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
vgmstream->codec_data = init_atrac9(&cfg);
|
|
||||||
if (!vgmstream->codec_data) goto fail;
|
|
||||||
vgmstream->coding_type = coding_ATRAC9;
|
|
||||||
vgmstream->layout_type = layout_none;
|
|
||||||
|
|
||||||
//TODO: check encoder delay
|
|
||||||
vgmstream->num_samples = atrac9_bytes_to_samples_cfg(kwb.stream_size, cfg.config_data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sf_h != sf) close_streamfile(sf_h);
|
if (sf_h != sf) close_streamfile(sf_h);
|
||||||
|
|
||||||
if (!vgmstream_open_stream(vgmstream, sf_b, kwb.stream_offset))
|
|
||||||
goto fail;
|
|
||||||
return vgmstream;
|
return vgmstream;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
@ -164,7 +87,6 @@ fail:
|
|||||||
/* XWS - WaveStream? from Koei games */
|
/* XWS - WaveStream? from Koei games */
|
||||||
VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf) {
|
VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf) {
|
||||||
VGMSTREAM* vgmstream = NULL;
|
VGMSTREAM* vgmstream = NULL;
|
||||||
STREAMFILE* temp_sf = NULL;
|
|
||||||
kwb_header kwb = {0};
|
kwb_header kwb = {0};
|
||||||
int target_subsong = sf->stream_index;
|
int target_subsong = sf->stream_index;
|
||||||
|
|
||||||
@ -179,37 +101,138 @@ VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf) {
|
|||||||
if (!parse_xws(&kwb, sf))
|
if (!parse_xws(&kwb, sf))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (kwb.codec == MSF) {
|
vgmstream = init_vgmstream_koei_wavebank(&kwb, sf, sf);
|
||||||
if (kwb.stream_offset == 0) {
|
if (!vgmstream) goto fail;
|
||||||
vgmstream = init_vgmstream_silence(0,0,0); /* dummy, whatevs */
|
|
||||||
if (!vgmstream) goto fail;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
kwb.stream_size = read_u32be(kwb.stream_offset + 0x0c, sf) + 0x40;
|
|
||||||
|
|
||||||
temp_sf = setup_subfile_streamfile(sf, kwb.stream_offset, kwb.stream_size, "msf");
|
|
||||||
if (!temp_sf) goto fail;
|
|
||||||
|
|
||||||
vgmstream = init_vgmstream_msf(temp_sf);
|
|
||||||
if (!vgmstream) goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
vgmstream->num_streams = kwb.total_subsongs;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
close_streamfile(temp_sf);
|
|
||||||
return vgmstream;
|
return vgmstream;
|
||||||
fail:
|
fail:
|
||||||
close_streamfile(temp_sf);
|
close_vgmstream(vgmstream);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static VGMSTREAM* init_vgmstream_koei_wavebank(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) {
|
||||||
|
VGMSTREAM* vgmstream = NULL;
|
||||||
|
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
|
||||||
|
int32_t (*read_s32)(off_t,STREAMFILE*) = NULL;
|
||||||
|
|
||||||
static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
|
|
||||||
|
read_u32 = kwb->big_endian ? read_u32be : read_u32le;
|
||||||
|
read_s32 = kwb->big_endian ? read_s32be : read_s32le;
|
||||||
|
|
||||||
|
/* container */
|
||||||
|
if (kwb->codec == MSF) {
|
||||||
|
if (kwb->stream_offset == 0) {
|
||||||
|
vgmstream = init_vgmstream_silence(0,0,0); /* dummy, whatevs */
|
||||||
|
if (!vgmstream) goto fail;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
STREAMFILE* temp_sf = NULL;
|
||||||
|
|
||||||
|
kwb->stream_size = read_u32(kwb->stream_offset + 0x0c, sf_h) + 0x40;
|
||||||
|
|
||||||
|
temp_sf = setup_subfile_streamfile(sf_h, kwb->stream_offset, kwb->stream_size, "msf");
|
||||||
|
if (!temp_sf) goto fail;
|
||||||
|
|
||||||
|
vgmstream = init_vgmstream_msf(temp_sf);
|
||||||
|
close_streamfile(temp_sf);
|
||||||
|
if (!vgmstream) goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
vgmstream->num_streams = kwb->total_subsongs;
|
||||||
|
return vgmstream;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* build the VGMSTREAM */
|
||||||
|
vgmstream = allocate_vgmstream(kwb->channels, kwb->loop_flag);
|
||||||
|
if (!vgmstream) goto fail;
|
||||||
|
|
||||||
|
vgmstream->meta_type = meta_KWB;
|
||||||
|
vgmstream->sample_rate = kwb->sample_rate;
|
||||||
|
vgmstream->num_samples = kwb->num_samples;
|
||||||
|
vgmstream->stream_size = kwb->stream_size;
|
||||||
|
vgmstream->num_streams = kwb->total_subsongs;
|
||||||
|
|
||||||
|
switch(kwb->codec) {
|
||||||
|
case PCM16: /* PCM */
|
||||||
|
vgmstream->coding_type = coding_PCM16LE;
|
||||||
|
vgmstream->layout_type = layout_interleave;
|
||||||
|
vgmstream->interleave_block_size = 0x02;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MSADPCM:
|
||||||
|
vgmstream->coding_type = coding_MSADPCM;
|
||||||
|
vgmstream->layout_type = layout_none;
|
||||||
|
vgmstream->frame_size = kwb->block_size;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DSP_HEAD:
|
||||||
|
case DSP_BODY:
|
||||||
|
if (kwb->channels > 1) goto fail;
|
||||||
|
vgmstream->coding_type = coding_NGC_DSP; /* subinterleave? */
|
||||||
|
vgmstream->layout_type = layout_interleave;
|
||||||
|
vgmstream->interleave_block_size = 0x08;
|
||||||
|
if (kwb->codec == DSP_HEAD) {
|
||||||
|
dsp_read_coefs(vgmstream, sf_h, kwb->dsp_offset + 0x1c, 0x60, kwb->big_endian);
|
||||||
|
dsp_read_hist (vgmstream, sf_h, kwb->dsp_offset + 0x40, 0x60, kwb->big_endian);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* typical DSP header + data */
|
||||||
|
vgmstream->num_samples = read_s32(kwb->stream_offset + 0x00, sf_b);
|
||||||
|
dsp_read_coefs(vgmstream, sf_b, kwb->stream_offset + 0x1c, 0x60, kwb->big_endian);
|
||||||
|
dsp_read_hist (vgmstream, sf_b, kwb->stream_offset + 0x40, 0x60, kwb->big_endian);
|
||||||
|
kwb->stream_offset += 0x60;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
#ifdef VGM_USE_ATRAC9
|
||||||
|
case AT9: {
|
||||||
|
atrac9_config cfg = {0};
|
||||||
|
|
||||||
|
{
|
||||||
|
size_t extra_size = read_u32le(kwb->stream_offset + 0x00, sf_b);
|
||||||
|
uint32_t config_data = read_u32be(kwb->stream_offset + 0x04, sf_b);
|
||||||
|
/* 0x0c: encoder delay? */
|
||||||
|
/* 0x0e: encoder padding? */
|
||||||
|
/* 0x10: samples per frame */
|
||||||
|
/* 0x12: frame size */
|
||||||
|
|
||||||
|
cfg.channels = vgmstream->channels;
|
||||||
|
cfg.config_data = config_data;
|
||||||
|
|
||||||
|
kwb->stream_offset += extra_size;
|
||||||
|
kwb->stream_size -= extra_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
vgmstream->codec_data = init_atrac9(&cfg);
|
||||||
|
if (!vgmstream->codec_data) goto fail;
|
||||||
|
vgmstream->coding_type = coding_ATRAC9;
|
||||||
|
vgmstream->layout_type = layout_none;
|
||||||
|
|
||||||
|
//TODO: check encoder delay
|
||||||
|
vgmstream->num_samples = atrac9_bytes_to_samples_cfg(kwb->stream_size, cfg.config_data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!vgmstream_open_stream(vgmstream, sf_b, kwb->stream_offset))
|
||||||
|
goto fail;
|
||||||
|
return vgmstream;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
close_vgmstream(vgmstream);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ************************************************************************* */
|
||||||
|
|
||||||
|
static int parse_type_kwb2(kwb_header* kwb, off_t offset, off_t body_offset, STREAMFILE* sf_h) {
|
||||||
int i, j, sounds;
|
int i, j, sounds;
|
||||||
|
|
||||||
/* 00: KWB2/KWBN id */
|
/* 00: KWB2/KWBN id */
|
||||||
@ -220,12 +243,15 @@ static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
|
|||||||
/* 10: null or 1 */
|
/* 10: null or 1 */
|
||||||
/* 14: offset to HDDB table (from type), can be null */
|
/* 14: offset to HDDB table (from type), can be null */
|
||||||
|
|
||||||
|
//;VGM_LOG("KWB2: sounds %i, o=%lx\n", sounds, offset);
|
||||||
|
|
||||||
/* offset table to entries */
|
/* offset table to entries */
|
||||||
for (i = 0; i < sounds; i++) {
|
for (i = 0; i < sounds; i++) {
|
||||||
off_t sound_offset = read_u32le(offset + 0x18 + i*0x04, sf_h);
|
off_t sound_offset = read_u32le(offset + 0x18 + i*0x04, sf_h);
|
||||||
int subsounds, subsound_start, subsound_size;
|
int subsounds, subsound_start, subsound_size;
|
||||||
uint16_t version;
|
uint16_t version;
|
||||||
|
|
||||||
|
//;VGM_LOG("KWB2: entry %i, o=%lx, so=%lx\n", i, offset + 0x18 + i*0x04, sound_offset);
|
||||||
|
|
||||||
if (sound_offset == 0) /* common... */
|
if (sound_offset == 0) /* common... */
|
||||||
continue;
|
continue;
|
||||||
@ -258,6 +284,8 @@ static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
|
|||||||
kwb->total_subsongs++;
|
kwb->total_subsongs++;
|
||||||
if (kwb->total_subsongs != kwb->target_subsong)
|
if (kwb->total_subsongs != kwb->target_subsong)
|
||||||
continue;
|
continue;
|
||||||
|
kwb->found = 1;
|
||||||
|
|
||||||
subsound_offset = subsound_start + j*subsound_size;
|
subsound_offset = subsound_start + j*subsound_size;
|
||||||
|
|
||||||
kwb->sample_rate = read_u16le(subsound_offset + 0x00, sf_h);
|
kwb->sample_rate = read_u16le(subsound_offset + 0x00, sf_h);
|
||||||
@ -272,6 +300,8 @@ static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
|
|||||||
/* when size > 0x48 */
|
/* when size > 0x48 */
|
||||||
/* 0x48: subsound entry size */
|
/* 0x48: subsound entry size */
|
||||||
/* rest: reserved per codec? (usually null) */
|
/* rest: reserved per codec? (usually null) */
|
||||||
|
|
||||||
|
kwb->stream_offset += body_offset;
|
||||||
|
|
||||||
switch(codec) {
|
switch(codec) {
|
||||||
case 0x00:
|
case 0x00:
|
||||||
@ -304,16 +334,14 @@ static int parse_type_kwb2(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
|
|||||||
then name table (null terminated and one after other)
|
then name table (null terminated and one after other)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (kwb->target_subsong < 0 || kwb->target_subsong > kwb->total_subsongs || kwb->total_subsongs < 1) goto fail;
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
fail:
|
fail:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_type_k4hd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
|
static int parse_type_k4hd(kwb_header* kwb, off_t offset, off_t body_offset, STREAMFILE* sf_h) {
|
||||||
off_t ppva_offset, header_offset;
|
off_t ppva_offset, header_offset;
|
||||||
int entries;
|
int entries, current_subsongs, relative_subsong;
|
||||||
size_t entry_size;
|
size_t entry_size;
|
||||||
|
|
||||||
|
|
||||||
@ -344,10 +372,14 @@ static int parse_type_k4hd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
kwb->total_subsongs = entries;
|
current_subsongs = kwb->total_subsongs;
|
||||||
if (kwb->target_subsong < 0 || kwb->target_subsong > kwb->total_subsongs || kwb->total_subsongs < 1) goto fail;
|
kwb->total_subsongs += entries;
|
||||||
|
if (kwb->target_subsong - 1 < current_subsongs || kwb->target_subsong > kwb->total_subsongs)
|
||||||
|
return 1;
|
||||||
|
kwb->found = 1;
|
||||||
|
|
||||||
header_offset = ppva_offset + 0x20 + (kwb->target_subsong-1) * entry_size;
|
relative_subsong = kwb->target_subsong - current_subsongs;
|
||||||
|
header_offset = ppva_offset + 0x20 + (relative_subsong-1) * entry_size;
|
||||||
|
|
||||||
kwb->stream_offset = read_u32le(header_offset + 0x00, sf_h);
|
kwb->stream_offset = read_u32le(header_offset + 0x00, sf_h);
|
||||||
kwb->sample_rate = read_u32le(header_offset + 0x04, sf_h);
|
kwb->sample_rate = read_u32le(header_offset + 0x04, sf_h);
|
||||||
@ -363,21 +395,22 @@ static int parse_type_k4hd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
|
|||||||
kwb->codec = AT9;
|
kwb->codec = AT9;
|
||||||
kwb->channels = 1; /* always, devs use dual subsongs to fake stereo (like as hd3+bd3) */
|
kwb->channels = 1; /* always, devs use dual subsongs to fake stereo (like as hd3+bd3) */
|
||||||
|
|
||||||
|
kwb->stream_offset += body_offset;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
fail:
|
fail:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_type_sdsd(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
|
static int parse_type_sdsd(kwb_header* kwb, off_t offset, off_t body_offset, STREAMFILE* sf_h) {
|
||||||
/* has Vers, Head, Prog, Smpl sections (like Sony VABs)
|
/* has Vers, Head, Prog, Smpl sections (like Sony VABs)
|
||||||
unknown codec, blocked with some common start, variable sized */
|
unknown codec, blocked with some common start, variable sized */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_type_sdwi(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
|
static int parse_type_sdwi(kwb_header* kwb, off_t offset, off_t body_offset, STREAMFILE* sf_h) {
|
||||||
off_t smpl_offset, header_offset;
|
off_t smpl_offset, header_offset;
|
||||||
int entries;
|
int entries, current_subsongs, relative_subsong;
|
||||||
size_t entry_size;
|
size_t entry_size;
|
||||||
|
|
||||||
|
|
||||||
@ -404,10 +437,14 @@ static int parse_type_sdwi(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
|
|||||||
entries = read_u32le(smpl_offset + 0x0c, sf_h); /* LE! */
|
entries = read_u32le(smpl_offset + 0x0c, sf_h); /* LE! */
|
||||||
entry_size = 0x40;
|
entry_size = 0x40;
|
||||||
|
|
||||||
kwb->total_subsongs = entries;
|
current_subsongs = kwb->total_subsongs;
|
||||||
if (kwb->target_subsong < 0 || kwb->target_subsong > kwb->total_subsongs || kwb->total_subsongs < 1) goto fail;
|
kwb->total_subsongs += entries;
|
||||||
|
if (kwb->target_subsong - 1 < current_subsongs || kwb->target_subsong > kwb->total_subsongs)
|
||||||
|
return 1;
|
||||||
|
kwb->found = 1;
|
||||||
|
|
||||||
header_offset = smpl_offset + 0x10 + (kwb->target_subsong-1) * entry_size;
|
relative_subsong = kwb->target_subsong - current_subsongs;
|
||||||
|
header_offset = smpl_offset + 0x10 + (relative_subsong-1) * entry_size;
|
||||||
|
|
||||||
/* 00: "SS" + ID (0..N) */
|
/* 00: "SS" + ID (0..N) */
|
||||||
kwb->stream_offset = read_u32be(header_offset + 0x04, sf_h);
|
kwb->stream_offset = read_u32be(header_offset + 0x04, sf_h);
|
||||||
@ -427,11 +464,14 @@ static int parse_type_sdwi(kwb_header* kwb, off_t offset, STREAMFILE* sf_h) {
|
|||||||
kwb->codec = DSP_BODY;
|
kwb->codec = DSP_BODY;
|
||||||
kwb->channels = 1;
|
kwb->channels = 1;
|
||||||
|
|
||||||
|
kwb->stream_offset += body_offset;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
fail:
|
fail:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) {
|
static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) {
|
||||||
off_t head_offset, body_offset, start;
|
off_t head_offset, body_offset, start;
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
@ -485,22 +525,22 @@ static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) {
|
|||||||
switch(type) {
|
switch(type) {
|
||||||
case 0x4B574232: /* "KWB2" [Bladestorm Nightmare (PC), Dissidia NT (PC)] */
|
case 0x4B574232: /* "KWB2" [Bladestorm Nightmare (PC), Dissidia NT (PC)] */
|
||||||
case 0x4B57424E: /* "KWBN" [Fire Emblem Warriors (Switch)] */
|
case 0x4B57424E: /* "KWBN" [Fire Emblem Warriors (Switch)] */
|
||||||
if (!parse_type_kwb2(kwb, head_offset, sf_h))
|
if (!parse_type_kwb2(kwb, head_offset, body_offset, sf_h))
|
||||||
goto fail;
|
goto fail;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x4B344844: /* "K4HD" [Dissidia NT (PS4), (Vita) */
|
case 0x4B344844: /* "K4HD" [Dissidia NT (PS4), (Vita) */
|
||||||
if (!parse_type_k4hd(kwb, head_offset, sf_h))
|
if (!parse_type_k4hd(kwb, head_offset, body_offset, sf_h))
|
||||||
goto fail;
|
goto fail;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x53447364: /* "SDsd" (PS3? leftover files) */
|
case 0x53447364: /* "SDsd" (PS3? leftover files) */
|
||||||
if (!parse_type_sdsd(kwb, head_offset, sf_h))
|
if (!parse_type_sdsd(kwb, head_offset, body_offset, sf_h))
|
||||||
goto fail;
|
goto fail;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x53445769: /* "SDWi" [Fatal Frame 5 (WiiU)] */
|
case 0x53445769: /* "SDWi" [Fatal Frame 5 (WiiU)] */
|
||||||
if (!parse_type_sdwi(kwb, head_offset, sf_h))
|
if (!parse_type_sdwi(kwb, head_offset, body_offset, sf_h))
|
||||||
goto fail;
|
goto fail;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -508,7 +548,8 @@ static int parse_kwb(kwb_header* kwb, STREAMFILE* sf_h, STREAMFILE* sf_b) {
|
|||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
kwb->stream_offset += body_offset;
|
if (!kwb->found)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
fail:
|
fail:
|
||||||
@ -517,17 +558,112 @@ fail:
|
|||||||
|
|
||||||
static int parse_type_msfbank(kwb_header* kwb, off_t offset, STREAMFILE* sf) {
|
static int parse_type_msfbank(kwb_header* kwb, off_t offset, STREAMFILE* sf) {
|
||||||
/* this is just like XWSF, abridged: */
|
/* this is just like XWSF, abridged: */
|
||||||
|
int entries, current_subsongs, relative_subsong;
|
||||||
off_t header_offset;
|
off_t header_offset;
|
||||||
|
|
||||||
|
entries = read_u32be(offset + 0x14, sf);
|
||||||
|
|
||||||
kwb->total_subsongs = read_u32be(offset + 0x14, sf);
|
current_subsongs = kwb->total_subsongs;
|
||||||
if (kwb->target_subsong < 0 || kwb->target_subsong > kwb->total_subsongs || kwb->total_subsongs < 1) goto fail;
|
kwb->total_subsongs += entries;
|
||||||
|
if (kwb->target_subsong - 1 < current_subsongs || kwb->target_subsong > kwb->total_subsongs)
|
||||||
|
return 1;
|
||||||
|
kwb->found = 1;
|
||||||
|
|
||||||
header_offset = offset + 0x30 + (kwb->target_subsong-1) * 0x04;
|
relative_subsong = kwb->target_subsong - current_subsongs;
|
||||||
|
header_offset = offset + 0x30 + (relative_subsong-1) * 0x04;
|
||||||
|
|
||||||
/* just a dumb table pointing to MSF, entries can be dummy */
|
/* just a dumb table pointing to MSF, entries can be dummy */
|
||||||
kwb->stream_offset = read_u32be(header_offset, sf);
|
kwb->stream_offset = read_u32be(header_offset, sf);
|
||||||
kwb->codec = MSF;
|
kwb->codec = MSF;
|
||||||
|
|
||||||
|
kwb->stream_offset += offset;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
//fail:
|
||||||
|
// return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_type_xwsfile(kwb_header* kwb, off_t offset, STREAMFILE* sf) {
|
||||||
|
off_t table1_offset, table2_offset;
|
||||||
|
int i, chunks, chunks2;
|
||||||
|
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
if (!(is_id32be(offset + 0x00, sf, "XWSF") && is_id32be(offset + 0x04, sf, "ILE\0")) &&
|
||||||
|
!(is_id32be(offset + 0x00, sf, "tdpa") && is_id32be(offset + 0x04, sf, "ck\0\0")))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
kwb->big_endian = read_u8(offset + 0x08, sf) == 0xFF;
|
||||||
|
/* 0x0a: version? (0100: NG2/NG3 PS3, 0101: DoA LR PC) */
|
||||||
|
|
||||||
|
read_u32 = kwb->big_endian ? read_u32be : read_u32le;
|
||||||
|
|
||||||
|
/* 0x0c: tables start */
|
||||||
|
/* 0x10: file size */
|
||||||
|
chunks = read_u32(offset + 0x14, sf);
|
||||||
|
chunks2 = read_u32(offset + 0x18, sf);
|
||||||
|
/* 0x1c: null */
|
||||||
|
if (chunks != chunks2)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
table1_offset = read_u32(offset + 0x20, sf); /* offsets */
|
||||||
|
table2_offset = read_u32(offset + 0x24, sf); /* sizes */
|
||||||
|
/* 0x28: null */
|
||||||
|
/* 0x2c: null */
|
||||||
|
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (i < chunks) {
|
||||||
|
uint32_t entry_type, head_offset, body_offset, head_size;
|
||||||
|
//;VGM_LOG("XWS: entry %i/%i\n", i, chunks);
|
||||||
|
|
||||||
|
/* NG2/NG3 PS3 have table1+2, DoA LR PC removes table2 and includes body offset in entries */
|
||||||
|
if (table2_offset) {
|
||||||
|
head_offset = read_u32(offset + table1_offset + i * 0x04 + 0x00, sf);
|
||||||
|
head_size = read_u32(offset + table2_offset + i * 0x04 + 0x00, sf);
|
||||||
|
body_offset = head_offset;
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
/* sometimes has file end offset as entry with no size*/
|
||||||
|
if (!head_size)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
head_offset = read_u32(offset + table1_offset + i * 0x04 + 0x00, sf);
|
||||||
|
body_offset = read_u32(offset + table1_offset + i * 0x04 + 0x04, sf);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!head_offset) /* just in case */
|
||||||
|
continue;
|
||||||
|
|
||||||
|
|
||||||
|
head_offset += offset;
|
||||||
|
body_offset += offset;
|
||||||
|
entry_type = read_u32be(head_offset + 0x00, sf);
|
||||||
|
//;VGM_LOG("XWS: head=%x, body=%x\n", head_offset, body_offset);
|
||||||
|
|
||||||
|
if (entry_type == get_id32be("XWSF")) { /* + "ILE\0" */
|
||||||
|
if (!parse_type_xwsfile(kwb, head_offset, sf))
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
else if (entry_type == get_id32be("CUEB") || entry_type < 0x100) {
|
||||||
|
; /* CUE-like info (may start with 0 or a low number instead) */
|
||||||
|
}
|
||||||
|
else if (entry_type == get_id32be("MSFB")) { /* + "ANK\0" */
|
||||||
|
if (!parse_type_msfbank(kwb, head_offset, sf))
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
else if (entry_type == get_id32be("KWB2")) {
|
||||||
|
if (!parse_type_kwb2(kwb, head_offset, body_offset, sf))
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
VGM_LOG("XWS: unknown type %x at head=%x, body=%x\n", entry_type, head_offset, body_offset);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
fail:
|
fail:
|
||||||
return 0;
|
return 0;
|
||||||
@ -535,59 +671,23 @@ fail:
|
|||||||
|
|
||||||
|
|
||||||
static int parse_xws(kwb_header* kwb, STREAMFILE* sf) {
|
static int parse_xws(kwb_header* kwb, STREAMFILE* sf) {
|
||||||
off_t head_offset, body_offset, start;
|
|
||||||
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
|
|
||||||
int chunks, chunks2;
|
|
||||||
off_t msfb_offset;
|
|
||||||
|
|
||||||
/* format is similar to WHD1 with some annoyances of its own
|
/* Format is similar to WHD1 with some annoyances of its own. Variations:
|
||||||
* variations:
|
* - XWSFILE w/ N chunks: CUE offsets + 1 MSFBANK offset
|
||||||
* - tdpack: points to N XWSFILE
|
|
||||||
* - XWSFILE w/ 4 chunks: CUEBANK offset, ? offset, MSFBANK offset, end offset (PS3)
|
|
||||||
* [Ninja Gaiden Sigma 2 (PS3), Ninja Gaiden 3 Razor's Edge (PS3)]
|
* [Ninja Gaiden Sigma 2 (PS3), Ninja Gaiden 3 Razor's Edge (PS3)]
|
||||||
* - XWSFILE w/ 2*N chunks: KWB2 offset + data offset * N (ex. 3 pairs = 6 chunks)
|
* - XWSFILE w/ 2*N chunks: KWB2 offset + data offset * N (ex. 3 pairs = 6 chunks)
|
||||||
* [Dead or Alive 5 Last Round (PC)]
|
* [Dead or Alive 5 Last Round (PC)]
|
||||||
|
* - tdpack: same but points to N XWSFILE
|
||||||
|
* [Ninja Gaiden 3 Razor's Edge (PS3)]
|
||||||
*
|
*
|
||||||
* for now basic support for the second case, others we'd have to map subsong N to internal bank M
|
* Needs to call sub-parts multiple times to fill total subsongs when parsing xwsfile.
|
||||||
*/
|
*/
|
||||||
|
if (!parse_type_xwsfile(kwb, 0x00, sf))
|
||||||
if (read_u32be(0x00, sf) != 0x58575346 || /* "XWSF" */
|
|
||||||
read_u32be(0x04, sf) != 0x494C4500) /* "ILE\0" */
|
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
kwb->big_endian = read_u8(0x08, sf) == 0xFF;
|
if (!kwb->found)
|
||||||
/* 0x0a: version? */
|
|
||||||
|
|
||||||
read_u32 = kwb->big_endian ? read_u32be : read_u32le;
|
|
||||||
|
|
||||||
start = read_u32(0x0c, sf);
|
|
||||||
/* 0x10: file size */
|
|
||||||
chunks = read_u32(0x14, sf);
|
|
||||||
chunks2 = read_u32(0x18, sf);
|
|
||||||
/* 0x1c: null */
|
|
||||||
/* 0x20: some size? */
|
|
||||||
/* 0x24: some size? */
|
|
||||||
if (chunks != chunks2)
|
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
if (chunks != 4)
|
|
||||||
goto fail;
|
|
||||||
|
|
||||||
msfb_offset = read_u32(start + 0x08, sf);
|
|
||||||
if (read_u32be(msfb_offset, sf) == 0x4D534642) { /* "MSFB" + "ANK\0" */
|
|
||||||
head_offset = msfb_offset;
|
|
||||||
body_offset = msfb_offset; /* relative to start */
|
|
||||||
|
|
||||||
if (!parse_type_msfbank(kwb, head_offset, sf))
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
kwb->stream_offset += body_offset;
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
fail:
|
fail:
|
||||||
return 0;
|
return 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user