mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-17 23:36:41 +01:00
Add stream selection and/or stream name reading for some formats
This commit is contained in:
parent
b7c807a85c
commit
d219804b99
@ -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 */
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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) )
|
||||
|
@ -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;
|
||||
|
@ -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) */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 */
|
||||
|
331
src/meta/xwb.c
331
src/meta/xwb.c
@ -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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user