Add stream selection and/or stream name reading for some formats

This commit is contained in:
bnnm 2017-08-12 11:46:28 +02:00
parent b7c807a85c
commit d219804b99
14 changed files with 504 additions and 130 deletions

View File

@ -122,7 +122,7 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
int i, bnk_version;
int target_stream = 0, total_streams;
int total_streams, target_stream = streamFile->stream_index;
/* check extension */

View File

@ -92,7 +92,10 @@ typedef struct {
/* extra */
uint32_t hdrsize;
uint32_t shdrsize_min;
meta_t meta_type;
off_t name_offset;
size_t name_size;
} FSB_HEADER;
/* ********************************************************************************** */
@ -116,12 +119,12 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
off_t start_offset;
size_t custom_data_offset;
int loop_flag = 0;
int target_stream = 0;
int target_stream = streamFile->stream_index;
FSB_HEADER fsbh;
/* check extensions */
if ( !check_extensions(streamFile, "fsb,wii") )
/* check extensions (.bnk = Hard Corps Uprising PS3) */
if ( !check_extensions(streamFile, "fsb,wii,bnk") )
goto fail;
/* check header */
@ -144,7 +147,8 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
{
off_t s_off = offset+fsbh.hdrsize;
/* 0x00:name(len=0x20) */
fsbh.name_offset = s_off;
fsbh.name_size = 0x20;
fsbh.lengthsamples = read_32bitLE(s_off+0x20,streamFile);
fsbh.lengthcompressedbytes = read_32bitLE(s_off+0x24,streamFile);
fsbh.deffreq = read_32bitLE(s_off+0x28,streamFile);
@ -209,9 +213,11 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
off_t s_off = offset + fsbh.hdrsize;
off_t d_off = offset + fsbh.hdrsize + fsbh.shdrsize;
/* find target_stream data offset, reading each header */
for(i=1; i <= fsbh.numsamples; i++) {
/* 0x00:size 0x02:name(len=size) */
/* find target_stream header (variable sized) */
for(i = 0; i < fsbh.numsamples; i++) {
size_t stream_header_size = (uint16_t)read_16bitLE(s_off+0x00,streamFile);
fsbh.name_offset = s_off+0x02;
fsbh.name_size = 0x20-0x02;
fsbh.lengthsamples = read_32bitLE(s_off+0x20,streamFile);
fsbh.lengthcompressedbytes = read_32bitLE(s_off+0x24,streamFile);
fsbh.loopstart = read_32bitLE(s_off+0x28,streamFile);
@ -221,20 +227,19 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
/* 0x38:defvol 0x3a:defpan 0x3c:defpri */
fsbh.numchannels = read_16bitLE(s_off+0x3e,streamFile);
/* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsbh.varpan */
/* FSB3/4: 0x50:extended_data size_32bits (not always given) */
if (target_stream == i) /* d_off found */
if (i+1 == target_stream) /* d_off found */
break;
s_off += fsbh.shdrsize_min; /* default size */
if (fsbh.version == FMOD_FSB_VERSION_4_0) {
uint32_t extended_data = read_32bitLE(s_off+0x48,streamFile); /* +0x50:extended_data of size_32bits */
if (extended_data > fsbh.shdrsize_min)
s_off += extended_data;
}
s_off += stream_header_size;
d_off += fsbh.lengthcompressedbytes; /* there is no offset so manually count */
//d_off += d_off % 0x30; /*todo some formats need padding, not sure when/how */
/* IMAs streams have weird end padding (maybe: FSB3=no padding, FSB4=always padding) */
if ((fsbh.mode & FSOUND_IMAADPCM) && (fsbh.flags & FMOD_FSB_SOURCE_MPEG_PADDED4)) {
if (d_off % 0x20)
d_off += 0x20 - (d_off % 0x20);
}
}
if (i > fsbh.numsamples) goto fail; /* not found */
@ -243,19 +248,13 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
}
}
#if 0
/* XOR encryption for some FSB4, though the flag is only seen after decrypting */
if (fsbh.flags & FMOD_FSB_SOURCE_ENCRYPTED) {
VGM_LOG("FSB ENCRYPTED found\n");
goto fail;
}
VGM_ASSERT(fsbh.flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n");
/* sometimes there is garbage at the end or missing bytes due to improper demuxing */
if (fsbh.hdrsize + fsbh.shdrsize + fsbh.datasize != streamFile->get_size(streamFile) - offset) {
VGM_LOG("FSB wrong head/datasize found\n");
goto fail;
}
#endif
VGM_ASSERT(fsbh.hdrsize + fsbh.shdrsize + fsbh.datasize != streamFile->get_size(streamFile) - offset,
"FSB wrong head/datasize found\n");
/* Loops by default unless disabled (sometimes may add FSOUND_LOOP_NORMAL). Often streams
* repeat over and over (some tracks that shouldn't do this based on the flags, no real way to identify them). */
@ -273,6 +272,9 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
vgmstream->loop_end_sample = fsbh.loopend;
vgmstream->num_streams = fsbh.numsamples;
vgmstream->meta_type = fsbh.meta_type;
if (fsbh.name_offset)
read_string(vgmstream->stream_name,fsbh.name_size+1, fsbh.name_offset,streamFile);
/* parse format */
if (fsbh.mode & FSOUND_MPEG) {
@ -320,8 +322,8 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
vgmstream->interleave_block_size = 0x10;
}
else if (fsbh.mode & FSOUND_XMA) {
/* FSB4: Xbox360 Armored Core V, Hard Corps, Halo Anniversary */
#if defined(VGM_USE_FFMPEG)
/* FSB4: Xbox360 Armored Core V, Hard Corps, Halo Anniversary */
ffmpeg_codec_data *ffmpeg_data = NULL;
uint8_t buf[FAKE_RIFF_BUFFER_SIZE];
size_t bytes, block_size, block_count;
@ -368,7 +370,8 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x1;
} else { /* Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */
}
else { /* Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */
/* sometimes FSOUND_STEREO/FSOUND_MONO is not set (ex. Dead Space iOS),
* or only STEREO/MONO but not FSOUND_8BITS/FSOUND_16BITS is set */
if (fsbh.flags & FMOD_FSB_SOURCE_BIGENDIANPCM) {

View File

@ -5,17 +5,16 @@
/* FSB5 - FMOD Studio multiplatform format */
VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t StartOffset = 0;
off_t StartOffset = 0, NameOffset = 0;
off_t SampleHeaderStart = 0, DSPInfoStart = 0;
size_t SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength, StreamSize = 0;
uint32_t NumSamples = 0, LoopStart = 0, LoopEnd = 0;
int LoopFlag = 0, ChannelCount = 0, SampleRate = 0, CodingID;
int TotalStreams, TargetStream = 0;
int TotalStreams, TargetStream = streamFile->stream_index;
uint32_t VorbisSetupId = 0;
int i;
/* check extension, case insensitive */
if (!check_extensions(streamFile,"fsb")) goto fail;
@ -163,16 +162,19 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
/* continue searching */
SampleHeaderStart += StreamHeaderLength;
}
/* target stream not found*/
if (!StartOffset || !StreamSize) goto fail;
/* get stream name */
if (NameTableLength) {
NameOffset = BaseHeaderLength + SampleHeaderLength + read_32bitLE(BaseHeaderLength + SampleHeaderLength + 0x04*(TargetStream-1),streamFile);
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(ChannelCount,LoopFlag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->sample_rate = SampleRate;
vgmstream->num_streams = TotalStreams;
vgmstream->num_samples = NumSamples;
@ -181,7 +183,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = LoopEnd;
}
vgmstream->meta_type = meta_FSB5;
if (NameOffset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, NameOffset,streamFile);
/* parse codec */
switch (CodingID) {
case 0x00: /* FMOD_SOUND_FORMAT_NONE */
goto fail;
@ -306,7 +312,6 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,StartOffset))
goto fail;

View File

@ -9,7 +9,7 @@ static VGMSTREAM * init_vgmstream_kt_wiibgm_offset(STREAMFILE *streamFile, off_t
* It probably makes more sense to extract it externally, it's here mainly for Hyrule Warriors */
VGMSTREAM * init_vgmstream_kt_g1l(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
int type, num_streams, target_stream = 1;
int type, num_streams, target_stream = streamFile->stream_index;
off_t stream_offset;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;

View File

@ -6,7 +6,7 @@ typedef enum { XMA2 } gtd_codec;
/* GTD - found in Knights Contract (X360, PS3), Valhalla Knights 3 (PSV) */
VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, chunk_offset;
off_t start_offset, chunk_offset, stpr_offset, name_offset = 0;
size_t data_size, chunk_size;
int loop_flag, channel_count, sample_rate;
int num_samples, loop_start_sample, loop_end_sample;
@ -32,14 +32,22 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) {
start_offset = read_32bitBE(0x58,streamFile); /* always 0x800 */
data_size = read_32bitBE(0x5c,streamFile);
/* 0x40(18): null, 0x60(4): header size (0x70), 0x64(4): seek table size again, 0x68(8): null */
/* 0x70: seek table; then a "STPR" chunk with the file ID and filename */
/* 0x34(18): null, 0x54(4): seek table offset, 0x58(4): seek table size, 0x5c(8): null, 0x64: seek table */
stpr_offset = read_32bitBE(chunk_offset+0x54,streamFile) + read_32bitBE(chunk_offset+0x58,streamFile);;
if (read_32bitBE(stpr_offset,streamFile) == 0x53545052) { /* "STPR" */
name_offset = stpr_offset + 0xB8; /* there are offsets fields but seems to work */
}
codec = XMA2;
}
else {
/* there are PSV (LE, ATRAC9) and PS3 (MSF inside?) variations, somewhat-but-not-quite similar
* (contain the "STPR" chunk but the rest is mostly different) */
/* there are PSV (LE, ATRAC9 data) and PS3 (MSF inside?) variations, somewhat-but-not-quite similar */
/* for PSV: */
/* 0x0c: data_size, 0x10: channles, 0x14: sample rate, 0x18-0x2c: fixed and unknown values */
/* 0x2c: STPR chunk, with name_offset at + 0xE8 */
goto fail;
}
@ -53,6 +61,8 @@ VGMSTREAM * init_vgmstream_gtd(STREAMFILE *streamFile) {
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
vgmstream->meta_type = meta_GTD;
if (name_offset) //encoding is Shift-Jis in some PSV files
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);
switch(codec) {
#ifdef VGM_USE_FFMPEG

View File

@ -4,7 +4,7 @@
/* P3D - from Radical's Prototype 1/2 (PC/PS3/X360) */
VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, parse_offset;
off_t start_offset, parse_offset, name_offset = 0;
size_t header_size, file_size, data_size;
int loop_flag = 0, channel_count, sample_rate, codec;
int i, name_count, text_len, block_size = 0, block_count = 0, num_samples;
@ -49,9 +49,12 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
if (name_count != 2 && name_count != 3) goto fail; /* 2: Prototype1, 3: Prototype2 */
parse_offset += 4;
for (i = 0; i < 2; i++) { /* skip names */
text_len = read_32bit(parse_offset,streamFile);
parse_offset += 4 + text_len + 1;
/* skip names */
for (i = 0; i < 2; i++) {
if (!name_offset)
name_offset = parse_offset + 4;
text_len = read_32bit(parse_offset,streamFile) + 1; /* null-terminated */
parse_offset += 4 + text_len;
}
/* info count? */
@ -65,8 +68,8 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
/* extra "Music" string in Prototype 2 */
if (name_count == 3) {
text_len = read_32bit(parse_offset,streamFile);
parse_offset += 4 + text_len + 1;
text_len = read_32bit(parse_offset,streamFile) + 1; /* null-terminated */
parse_offset += 4 + text_len;
}
@ -129,6 +132,8 @@ VGMSTREAM * init_vgmstream_p3d(STREAMFILE *streamFile) {
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->meta_type = meta_P3D;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);
/* codec init */
switch(codec) {

View File

@ -1,77 +1,49 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* NPFS - found in Namco PS2/PSP games:
* Tekken 5, Ace Combat 5, Yumeria, Venus & Braves (.nps), Ridge Racer PSP, etc
*/
/* NPFS - found in Namco PS2/PSP games (Tekken 5, Ace Combat 5, Yumeria, Venus & Braves, Ridge Racer PSP) */
VGMSTREAM * init_vgmstream_ps2_npsf(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int loop_flag=0;
int channel_count;
off_t start_offset;
int i;
int loop_flag, channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("npsf",filename_extension(filename)) &&
strcasecmp("nps",filename_extension(filename)))
/* check extension, case insensitive (should be .nps as per Venus & Braves data files) */
if ( !check_extensions(streamFile,"nps,npsf"))
goto fail;
/* check NPSF Header */
if (read_32bitBE(0x00,streamFile) != 0x4E505346)
if (read_32bitBE(0x00,streamFile) != 0x4E505346) /* "NPSF" */
goto fail;
/* check loop */
loop_flag = (read_32bitLE(0x14,streamFile)!=0xFFFFFFFF);
channel_count=read_32bitLE(0x0C,streamFile);
loop_flag = (read_32bitLE(0x14,streamFile) != 0xFFFFFFFF);
channel_count = read_32bitLE(0x0C,streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = read_32bitLE(0x0C,streamFile);
vgmstream->sample_rate = read_32bitLE(0x18,streamFile);
/* Check for Compression Scheme */
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = read_32bitLE(0x08,streamFile)*28/16;
/* Get loop point values */
vgmstream->num_samples = ps_bytes_to_samples(read_32bitLE(0x08,streamFile), 1); /* single channel data */
if(vgmstream->loop_flag) {
vgmstream->loop_start_sample = read_32bitLE(0x14,streamFile);
vgmstream->loop_end_sample = read_32bitLE(0x08,streamFile)*28/16;
vgmstream->loop_end_sample = ps_bytes_to_samples(read_32bitLE(0x08,streamFile), 1);
}
vgmstream->interleave_block_size = read_32bitLE(0x04,streamFile)/2;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitLE(0x04,streamFile) / 2;
vgmstream->meta_type = meta_PS2_NPSF;
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, 0x34,streamFile);
start_offset = (off_t)read_32bitLE(0x10,streamFile);
if (vgmstream->channels == 1) {
vgmstream->layout_type = layout_none;
} else {
vgmstream->layout_type = layout_interleave;
}
/* open the file for reading by each channel */
{
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,0x8000);
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=
(off_t)(start_offset+vgmstream->interleave_block_size*i);
}
}
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -6,14 +6,14 @@
VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamHeader = NULL;
off_t start_offset, chunk_offset;
size_t data_size;
off_t start_offset, chunk_offset, name_offset = 0;
size_t data_size, chunk_size;
int loop_flag = 0, channel_count, is_separate, type, sample_rate;
int32_t loop_start, loop_end;
int target_stream = 0, total_streams;
int total_streams, target_stream = streamFile->stream_index;
/* check extensions */
/* .xws: header and data, .xwh+xwb: header + data */
/* .xws: header and data, .xwh+xwb: header + data (.bin+dat are also found in Wild Arms 4/5) */
if (!check_extensions(streamFile,"xws,xwb")) goto fail;
is_separate = check_extensions(streamFile,"xwb");
@ -35,14 +35,16 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
/* file size (just the .xwh/xws) */
if (read_32bitLE(0x04,streamHeader)+0x10 != get_streamfile_size(streamHeader))
goto fail;
/* 0x08(4): version/type? (0x200), 0x0C: null */
/* 0x08(4): version (0x100/0x200), 0x0C: null */
/* typical chunks: FORM, FTXT, MARK, BODY (for .xws) */
if (read_32bitBE(0x10,streamHeader) != 0x464F524D) /* "FORM", main header (always first) */
goto fail;
/* 0x04: chunk size (-0x10), 0x08 version/type? (0x100), 0x0c: null */
chunk_size = read_32bitLE(0x10+0x04,streamHeader); /* size - 0x10 */
/* 0x08 version (0x100), 0x0c: null */
chunk_offset = 0x20;
/* check multi-streams */
total_streams = read_32bitLE(chunk_offset+0x00,streamHeader);
if (target_stream == 0) target_stream = 1;
@ -92,6 +94,14 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
start_offset = data_offset + stream_offset;
}
/* get stream name (always follows FORM) */
if (read_32bitBE(0x10+0x10 + chunk_size,streamHeader) == 0x46545854) { /* "FTXT" */
chunk_offset = 0x10+0x10 + chunk_size + 0x10;
if (read_32bitLE(chunk_offset+0x00,streamHeader) == total_streams) {
name_offset = chunk_offset + read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x04,streamHeader);
}
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
@ -100,6 +110,8 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
vgmstream->sample_rate = sample_rate;
vgmstream->num_streams = total_streams;
vgmstream->meta_type = meta_PS2_RXWS;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
switch (type) {
case 0x00: /* PS-ADPCM */
@ -107,9 +119,9 @@ VGMSTREAM * init_vgmstream_ps2_rxws(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10;
vgmstream->num_samples = ps_bytes_to_samples(loop_end, channel_count); //todo (read_32bitLE(0x38,streamFile)*28/16)/2;
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channel_count); //todo read_32bitLE(0x3C,streamFile)/16*14;
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channel_count); //todo read_32bitLE(0x38,streamFile)/16*14;
vgmstream->num_samples = ps_bytes_to_samples(loop_end, channel_count);
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channel_count);
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channel_count);
break;
case 0x01: /* PCM */

View File

@ -212,6 +212,9 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile) {
vgmstream->loop_end_sample += (int32_t)(loopEnd%(interleave*channel_count))/16*28;
}
/* always, but can be null or used as special string */
read_string(vgmstream->stream_name,0x10+1, 0x20,streamFile);
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )

View File

@ -9,11 +9,12 @@ static off_t get_rws_string_size(off_t off, STREAMFILE *streamFile);
/* RWS - RenderWare Stream (from games using RenderWare Audio middleware) */
VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, off, coefs_offset = 0, stream_offset = 0;
off_t start_offset, off, coefs_offset = 0, stream_offset = 0, name_offset = 0;
int loop_flag = 0, channel_count = 0, codec = 0, sample_rate = 0;
size_t file_size, header_size, data_size, stream_size = 0, block_size_max = 0, block_size = 0;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int i, total_segments, total_streams, target_stream = 0;
int i, total_segments;
int total_streams, target_stream = streamFile->stream_index;
if (!check_extensions(streamFile,"rws"))
@ -73,7 +74,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
/* get block_size for our target stream and from all streams, to skip their blocks during decode */
for (i = 0; i < total_streams; i++) { /* get block_sizes */
block_size_max += read_32bit(off+0x10 + 0x28*i,streamFile); /* includes padding and can be different per stream */
if (target_stream-1 == i) {
if (i+1 == target_stream) {
block_size = read_32bit(off+0x20 + 0x28*i,streamFile); /* actual size */
stream_offset = read_32bit(off+0x24 + 0x28*i,streamFile); /* within data */
}
@ -81,27 +82,40 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
off += 0x28 * total_streams;
/* get stream config: 0x0c(1): bits per sample, others: ? */
for (i = 0; i < total_streams; i++) {/* size depends on codec so we must parse it */
sample_rate = read_32bit(off+0x00, streamFile);
//unk_size = read_32bit(off+0x08, streamFile); /* segment size? loop-related? */
channel_count = read_8bit(off+0x0d, streamFile);
codec = read_32bitBE(off+0x1c, streamFile); /* uuid of 128b but first 32b is enough */
for (i = 0; i < total_streams; i++) { /* size depends on codec so we must parse it */
int prev_codec = 0;
if (i+1 == target_stream) {
sample_rate = read_32bit(off+0x00, streamFile);
//unk_size = read_32bit(off+0x08, streamFile); /* segment size? loop-related? */
channel_count = read_8bit(off+0x0d, streamFile);
codec = read_32bitBE(off+0x1c, streamFile); /* uuid of 128b but first 32b is enough */
}
prev_codec = read_32bitBE(off+0x1c, streamFile);
off += 0x2c;
if (codec == 0xF86215B0) { /* if codec is DSP there is an extra field per stream */
if (prev_codec == 0xF86215B0) { /* if codec is DSP there is an extra field per stream */
/* 0x00: approx num samples? 0x04: approx size/loop related? (can be 0) */
coefs_offset = off + 0x1c;
if (i+1 == target_stream) {
coefs_offset = off + 0x1c;
}
off += 0x60;
}
if (total_streams > 1) /* multitracks have an unknown field */
off += 0x04;
if (i == target_stream-1)
break;
off += 0x04; /* padding/garbage */
}
/* next is 0x14 * streams = ?(4) + uuid? (header ends), rest is garbage/padding until chunk end (may contain strings and weird stuff) */
/* skip uuid? per stream */
off += 0x10 * total_streams;
/* get stream name */
for (i = 0; i < total_streams; i++) {
if (i+1 == target_stream) {
name_offset = off;
}
off += get_rws_string_size(off, streamFile);
}
/* rest is padding/garbage until chunk end (may contain strings and weird stuff) */
start_offset = 0x0c + 0x0c + header_size + 0x0c + stream_offset; /* usually 0x800 but not always */
@ -110,9 +124,11 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_RWS;
vgmstream->sample_rate = sample_rate;
vgmstream->num_streams = total_streams;
vgmstream->meta_type = meta_RWS;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamFile);
vgmstream->layout_type = layout_rws_blocked;
vgmstream->current_block_size = block_size / vgmstream->channels;

View File

@ -8,13 +8,13 @@
VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamHeader = NULL;
off_t start_offset, data_offset, chunk_offset;
off_t start_offset, data_offset, chunk_offset, name_offset = 0;
size_t data_size;
int is_sgx, is_sgb;
int loop_flag, channels, type;
int sample_rate, num_samples, loop_start_sample, loop_end_sample;
int target_stream = 0, total_streams;
int total_streams, target_stream = streamFile->stream_index;
/* check extension, case insensitive */
@ -48,7 +48,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
}
/* typical chunks: WAVE, NAME (strings), RGND, SEQD (related to SFX), WSUR, WMKR, BUSS */
/* typical chunks: WAVE, RGND, NAME (strings for WAVE or RGND), SEQD (related to SFX), WSUR, WMKR, BUSS */
/* WAVE chunk (size 0x10 + files * 0x38 + optional padding) */
if (is_sgx) { /* position after chunk+size */
if (read_32bitBE(0x10,streamHeader) != 0x57415645) goto fail; /* "WAVE" */
@ -69,7 +69,8 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
chunk_offset += 0x08 + 0x38 * (target_stream-1); /* position in target header*/
/* 0x00 ? (00/01/02) */
/* 0x04 sometimes global offset to wave_name */
if (!is_sgx) /* meaning unknown in .sgx; offset 0 = not a stream (a RGND sample) */
name_offset = read_32bitLE(chunk_offset+0x04,streamHeader);
type = read_8bit(chunk_offset+0x08,streamHeader);
channels = read_8bit(chunk_offset+0x09,streamHeader);
/* 0x0a null */
@ -97,6 +98,7 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
start_offset = data_offset + stream_offset;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels,loop_flag);
if (!vgmstream) goto fail;
@ -107,6 +109,8 @@ VGMSTREAM * init_vgmstream_sgxd(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = loop_end_sample;
vgmstream->num_streams = total_streams;
vgmstream->meta_type = meta_SGXD;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
/* needs -1 to match RIFF AT3's loop chunk
* (maybe SGXD = "loop before this sample" rather than "loop after this sample" as expected by vgmstream) */

View File

@ -53,7 +53,7 @@ VGMSTREAM * init_vgmstream_sqex_scd(STREAMFILE *streamFile) {
int headers_entries;
int32_t loop_start, loop_end;
int target_stream = 1; /* N=Nth stream, 0=auto (first) */
int target_stream = streamFile->stream_index;
int loop_flag = 0, channel_count, codec_id;
int aux_chunk_count;

View File

@ -6,12 +6,12 @@
VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamHeader = NULL;
off_t start_offset, chunk_offset, first_offset = 0x60;
off_t start_offset, chunk_offset, first_offset = 0x60, name_offset = 0;
int is_separate;
int loop_flag, channels, type;
int sample_rate, num_samples, loop_start_sample, loop_end_sample;
int target_stream = 0, total_streams;
int total_streams, target_stream = streamFile->stream_index;
/* check extension, case insensitive */
@ -85,6 +85,20 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
start_offset = data_offset + stream_offset;
}
/* get stream name (NAME is tied to REQD/cues, and SFX cues repeat WAVEs, but should work ok for streams) */
if (is_separate && find_chunk_le(streamHeader, 0x4E414D45,first_offset,0, &chunk_offset,NULL)) { /* "NAME" */
/* table: relative offset (32b) + hash? (32b) + cue index (32b) */
int i;
int num_entries = read_16bitLE(chunk_offset+0x04,streamHeader); /*can be more than streams */
for (i = 0; i < num_entries; i++) {
uint32_t index = (uint32_t)read_32bitLE(chunk_offset+0x08 + 0x08 + i*0x0c,streamHeader);
if (index+1 == target_stream) {
name_offset = chunk_offset+0x08 + 0x00 + i*0x0c + read_32bitLE(chunk_offset+0x08 + 0x00 + i*0x0c,streamHeader);
break;
}
}
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channels,loop_flag);
@ -96,7 +110,8 @@ VGMSTREAM * init_vgmstream_sxd(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = loop_end_sample;
vgmstream->num_streams = total_streams;
vgmstream->meta_type = meta_SXD;
if (name_offset)
read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,streamHeader);
switch (type) {
case 0x01: /* HEVAG */

View File

@ -63,13 +63,15 @@ typedef struct {
uint32_t loop_end_sample;
} xwb_header;
static void get_xsb_name(char * buf, size_t maxsize, int target_stream, xwb_header * xwb, STREAMFILE *streamFile);
/* XWB - XACT Wave Bank (Microsoft SDK format for XBOX/XBOX360/Windows) */
VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, off, suboff;
xwb_header xwb;
int target_stream = 0;
int target_stream = streamFile->stream_index;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
@ -336,6 +338,7 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = xwb.loop_end_sample;
vgmstream->num_streams = xwb.streams;
vgmstream->meta_type = meta_XWB;
get_xsb_name(vgmstream->stream_name,STREAM_NAME_SIZE, target_stream, &xwb, streamFile);
switch(xwb.codec) {
case PCM:
@ -462,3 +465,329 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
/* ****************************************************************************** */
/* XSB parsing from xwb_split (mostly untouched), could be improved */
#define XSB_XACT1_MAX 11
#define XSB_XACT2_MAX 41
/**
* XWB contain stream info (channels, loop, data etc), often from multiple streams.
* XSBs contain info about how to play sounds (volume, pitch, name, etc) from XWBs (music or SFX).
* We only need to parse the XSB for the stream names.
*/
typedef struct {
int sound_count;
} xsb_wavebank;
typedef struct {
int stream_index; /* stream id in the xwb (doesn't need to match xsb sound order) */
int wavebank; /* xwb id, if the xsb has multiple wavebanks */
off_t name_index; /* name order */
off_t name_offset; /* global offset to the name string */
off_t sound_offset; /* global offset to the xsb sound */
off_t unk_index; /* some kind of number up to sound_count or 0xffff */
} xsb_sound;
typedef struct {
/* XSB header info */
xsb_sound * xsb_sounds; /* array of sounds info from the xsb, simplified */
xsb_wavebank * xsb_wavebanks; /* array of wavebank info from the xsb, simplified */
off_t xsb_sounds_offset;
size_t xsb_sounds_count;
size_t xsb_simple_sounds_offset; /* sound cues */
size_t xsb_simple_sounds_count;
size_t xsb_complex_sounds_offset;
size_t xsb_complex_sounds_count;
size_t xsb_wavebanks_count;
off_t xsb_nameoffsets_offset;
} xsb_header;
/* try to find the stream name in a companion XSB file, a comically complex cue format. */
static void get_xsb_name(char * buf, size_t maxsize, int target_stream, xwb_header * xwb, STREAMFILE *streamXwb) {
STREAMFILE *streamFile;
int i,j, start_sound, cfg__start_sound = 0, cfg__selected_wavebank = 0;
int xsb_version;
off_t off, suboff, name_offset = 0;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
xsb_header xsb;
memset(&xsb,0,sizeof(xsb_header)); /* before any "fail"! */
streamFile = open_stream_ext(streamXwb, "xsb");
if (!streamFile) goto fail;
//todo try common names (xwb and xsb often are named slightly differently using a common convention)
/* check header */
if ((read_32bitBE(0x00,streamFile) != 0x5344424B) && /* "SDBK" (LE) */
(read_32bitBE(0x00,streamFile) != 0x4B424453)) /* "KBDS" (BE) */
goto fail;
if (read_32bitBE(0x00,streamFile) == 0x5344424B) { /* SDBK */
read_32bit = read_32bitLE;
read_16bit = read_16bitLE;
} else {
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
}
/* read main header (SoundBankHeader) */
xsb_version = read_16bit(0x04, streamFile);
if ((xwb->version <= XACT1_1_MAX && xsb_version > XSB_XACT1_MAX) || (xwb->version <= XACT2_2_MAX && xsb_version > XSB_XACT2_MAX)) {
VGM_LOG("XSB: xsb and xwb are from different XACT versions (xsb v%i vs xwb v%i)", xsb_version, xwb->version);
goto fail;
}
off = 0;
if (xsb_version <= XSB_XACT1_MAX) {
xsb.xsb_wavebanks_count = 1; //read_8bit(0x22, streamFile);
xsb.xsb_sounds_count = read_16bit(0x1e, streamFile);//@ 0x1a? 0x1c?
//xsb.xsb_names_size = 0;
//xsb.xsb_names_offset = 0;
xsb.xsb_nameoffsets_offset = 0;
xsb.xsb_sounds_offset = 0x38;
} else if (xsb_version <= XSB_XACT2_MAX) {
xsb.xsb_simple_sounds_count = read_16bit(0x09, streamFile);
xsb.xsb_complex_sounds_count = read_16bit(0x0B, streamFile);
xsb.xsb_wavebanks_count = read_8bit(0x11, streamFile);
xsb.xsb_sounds_count = read_16bit(0x12, streamFile);
//0x14: 16b unk
//xsb.xsb_names_size = read_32bit(0x16, streamFile);
xsb.xsb_simple_sounds_offset = read_32bit(0x1a, streamFile);
xsb.xsb_complex_sounds_offset = read_32bit(0x1e, streamFile); //todo 0x1e?
//xsb.xsb_names_offset = read_32bit(0x22, streamFile);
xsb.xsb_nameoffsets_offset = read_32bit(0x3a, streamFile);
xsb.xsb_sounds_offset = read_32bit(0x3e, streamFile);
} else {
xsb.xsb_simple_sounds_count = read_16bit(0x13, streamFile);
xsb.xsb_complex_sounds_count = read_16bit(0x15, streamFile);
xsb.xsb_wavebanks_count = read_8bit(0x1b, streamFile);
xsb.xsb_sounds_count = read_16bit(0x1c, streamFile);
//xsb.xsb_names_size = read_32bit(0x1e, streamFile);
xsb.xsb_simple_sounds_offset = read_32bit(0x22, streamFile);
xsb.xsb_complex_sounds_offset = read_32bit(0x26, streamFile);
//xsb.xsb_names_offset = read_32bit(0x2a, streamFile);
xsb.xsb_nameoffsets_offset = read_32bit(0x42, streamFile);
xsb.xsb_sounds_offset = read_32bit(0x46, streamFile);
}
VGM_ASSERT(xsb.xsb_sounds_count < xwb->streams,
"XSB: number of streams in xsb lower than xwb (xsb %i vs xwb %i)", xsb.xsb_sounds_count, xwb->streams);
VGM_ASSERT(xsb.xsb_simple_sounds_count + xsb.xsb_complex_sounds_count != xsb.xsb_sounds_count,
"XSB: number of xsb sounds doesn't match simple + complex sounds (simple %i, complex %i, total %i)", xsb.xsb_simple_sounds_count, xsb.xsb_complex_sounds_count, xsb.xsb_sounds_count);
/* init stuff */
xsb.xsb_sounds = calloc(xsb.xsb_sounds_count, sizeof(xsb_sound));
if (!xsb.xsb_sounds) goto fail;
xsb.xsb_wavebanks = calloc(xsb.xsb_wavebanks_count, sizeof(xsb_wavebank));
if (!xsb.xsb_wavebanks) goto fail;
/* The following is a bizarre soup of flags, tables, offsets to offsets and stuff, just to get the actual name.
* info: https://wiki.multimedia.cx/index.php/XACT */
/* parse xsb sounds */
off = xsb.xsb_sounds_offset;
for (i = 0; i < xsb.xsb_sounds_count; i++) {
xsb_sound *s = &(xsb.xsb_sounds[i]);
uint32_t flag;
size_t size;
if (xsb_version <= XSB_XACT1_MAX) {
/* The format seems constant */
flag = read_8bit(off+0x00, streamFile);
size = 0x14;
if (flag != 0x01) {
VGM_LOG("XSB: xsb flag 0x%x at offset 0x%08lx not implemented", flag, off);
goto fail;
}
s->wavebank = 0; //read_8bit(off+suboff + 0x02, streamFile);
s->stream_index = read_16bit(off+0x02, streamFile);
s->sound_offset = off;
s->name_offset = read_16bit(off+0x04, streamFile);
}
else {
/* Each XSB sound has a variable size and somewhere inside is the stream/wavebank index.
* Various flags control the sound layout, but I can't make sense of them so quick hack instead */
flag = read_8bit(off+0x00, streamFile);
//0x01 16b unk, 0x03: 8b unk 04: 16b unk, 06: 8b unk
size = read_16bit(off+0x07, streamFile);
if (!(flag & 0x01)) { /* simple sound */
suboff = 0x09;
} else { /* complex sound */
/* not very exact but seems to work */
if (flag==0x01 || flag==0x03 || flag==0x05 || flag==0x07) {
if (size == 0x49) { //grotesque hack for Eschatos (these flags are way too complex)
suboff = 0x23;
} else if (size % 2 == 1 && read_16bit(off+size-0x2, streamFile)!=0) {
suboff = size - 0x08 - 0x07; //7 unk bytes at the end
} else {
suboff = size - 0x08;
}
} else {
VGM_LOG("XSB: xsb flag 0x%x at offset 0x%08lx not implemented", flag, off);
goto fail;
}
}
s->stream_index = read_16bit(off+suboff + 0x00, streamFile);
s->wavebank = read_8bit(off+suboff + 0x02, streamFile);
s->sound_offset = off;
}
if (s->wavebank+1 > xsb.xsb_wavebanks_count) {
VGM_LOG("XSB: unknown xsb wavebank id %i at offset 0x%lx", s->wavebank, off);
goto fail;
}
xsb.xsb_wavebanks[s->wavebank].sound_count += 1;
off += size;
}
/* parse name offsets */
if (xsb_version > XSB_XACT1_MAX) {
/* "cue" name order: first simple sounds, then complex sounds
* Both aren't ordered like the sound entries, instead use a global offset to the entry
*
* ex. of a possible XSB:
* name 1 = simple sound 1 > sound entry 2 (points to xwb stream 4): stream 4 uses name 1
* name 2 = simple sound 2 > sound entry 1 (points to xwb stream 1): stream 1 uses name 2
* name 3 = complex sound 1 > sound entry 3 (points to xwb stream 3): stream 3 uses name 3
* name 4 = complex sound 2 > sound entry 4 (points to xwb stream 2): stream 2 uses name 4
*
* Multiple cues can point to the same sound entry but we only use the first name (meaning some won't be used) */
off_t n_off = xsb.xsb_nameoffsets_offset;
off = xsb.xsb_simple_sounds_offset;
for (i = 0; i < xsb.xsb_simple_sounds_count; i++) {
off_t sound_offset = read_32bit(off + 0x01, streamFile);
off += 0x05;
/* find sound by offset */
for (j = 0; j < xsb.xsb_sounds_count; j++) {
xsb_sound *s = &(xsb.xsb_sounds[j]);;
/* update with the current name offset */
if (!s->name_offset && sound_offset == s->sound_offset) {
s->name_offset = read_32bit(n_off + 0x00, streamFile);
s->unk_index = read_16bit(n_off + 0x04, streamFile);
n_off += 0x06;
break;
}
}
}
off = xsb.xsb_complex_sounds_offset;
for (i = 0; i < xsb.xsb_complex_sounds_count; i++) {
off_t sound_offset = read_32bit(off + 0x01, streamFile);
off += 0x0f;
/* find sound by offset */
for (j = 0; j < xsb.xsb_sounds_count; j++) {
xsb_sound *s = &(xsb.xsb_sounds[j]);;
/* update with the current name offset */
if (!s->name_offset && sound_offset == s->sound_offset) {
s->name_offset = read_32bit(n_off + 0x00, streamFile);
s->unk_index = read_16bit(n_off + 0x04, streamFile);
n_off += 0x06;
break;
}
}
}
}
// todo: it's possible to find the wavebank using the name
/* try to find correct wavebank, in cases of multiple */
if (!cfg__selected_wavebank) {
for (i = 0; i < xsb.xsb_wavebanks_count; i++) {
xsb_wavebank *w = &(xsb.xsb_wavebanks[i]);
//CHECK_EXIT(w->sound_count == 0, "ERROR: xsb wavebank %i has no sounds", i); //Ikaruga PC
if (w->sound_count == xwb->streams) {
if (!cfg__selected_wavebank) {
VGM_LOG("XSB: multiple xsb wavebanks with the same number of sounds, use -w to specify one of the wavebanks");
goto fail;
}
cfg__selected_wavebank = i+1;
}
}
}
/* banks with different number of sounds but only one wavebank, just select the first */
if (!cfg__selected_wavebank && xsb.xsb_wavebanks_count==1) {
cfg__selected_wavebank = 1;
}
if (!cfg__selected_wavebank) {
VGM_LOG("XSB: multiple xsb wavebanks but autodetect didn't work");
goto fail;
}
if (xsb.xsb_wavebanks[cfg__selected_wavebank-1].sound_count == 0) {
VGM_LOG("XSB: xsb selected wavebank %i has no sounds", cfg__selected_wavebank);
goto fail;
}
if (cfg__start_sound) {
if (xsb.xsb_wavebanks[cfg__selected_wavebank-1].sound_count - (cfg__start_sound-1) < xwb->streams) {
VGM_LOG("XSB: starting sound too high (max in selected wavebank is %i)", xsb.xsb_wavebanks[cfg__selected_wavebank-1].sound_count - xwb->streams + 1);
goto fail;
}
} else {
/*
if (!cfg->ignore_names_not_found)
CHECK_EXIT(xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count > xwb->streams_count, "ERROR: number of streams in xsb wavebank bigger than xwb (xsb %i vs xwb %i), use -s to specify (1=first)", xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count, xwb->streams_count);
if (!cfg->ignore_names_not_found)
CHECK_EXIT(xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count < xwb->streams_count, "ERROR: number of streams in xsb wavebank lower than xwb (xsb %i vs xwb %i), use -n to ignore (some names won't be extracted)", xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count, xwb->streams_count);
*/
//if (!cfg->ignore_names_not_found)
// CHECK_EXIT(xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count != xwb->streams_count, "ERROR: number of streams in xsb wavebank different than xwb (xsb %i vs xwb %i), use -s to specify (1=first)", xwb->xsb_wavebanks[cfg->selected_wavebank-1].sound_count, xwb->streams_count);
}
/* *************************** */
start_sound = cfg__start_sound ? cfg__start_sound-1 : 0;
/* get name offset */
for (i = start_sound; i < xsb.xsb_sounds_count; i++) {
xsb_sound *s = &(xsb.xsb_sounds[i]);
VGM_LOG("wa=%i, sel=%i, si=%i vs ti=%i\n", s->wavebank, cfg__selected_wavebank, s->stream_index, target_stream);
if (s->wavebank == cfg__selected_wavebank-1
&& s->stream_index == target_stream-1){
name_offset = s->name_offset;
break;
}
}
if (name_offset)
read_string(buf,maxsize, name_offset,streamFile);
//return; /* no return, let free */
fail:
free(xsb.xsb_sounds);
free(xsb.xsb_wavebanks);
return;
}