Merge branch 'master' of github.com:kode54/vgmstream

This commit is contained in:
Christopher Snowhill 2017-08-25 19:52:00 -07:00
commit 3f39c1ab23
4 changed files with 87 additions and 59 deletions

View File

@ -96,7 +96,7 @@ typedef struct {
meta_t meta_type;
off_t name_offset;
size_t name_size;
} FSB_HEADER;
} fsb_header;
/* ********************************************************************************** */
@ -121,7 +121,7 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
int loop_flag = 0;
int target_stream = streamFile->stream_index;
FSB_HEADER fsbh;
fsb_header fsbh;
/* check extensions (.bnk = Hard Corps Uprising PS3) */
if ( !check_extensions(streamFile, "fsb,wii,bnk") )
@ -250,18 +250,24 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
/* XOR encryption for some FSB4, though the flag is only seen after decrypting */
VGM_ASSERT(fsbh.flags & FMOD_FSB_SOURCE_ENCRYPTED, "FSB ENCRYPTED found\n");
//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 */
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). */
loop_flag = !(fsbh.mode & FSOUND_LOOP_OFF); /* (fsbh.mode & FSOUND_LOOP_NORMAL) */
/* Loops unless disabled. FMOD default seems full loops (0/num_samples-1) without flags, for repeating tracks
* that should loop and jingles/sfx that shouldn't. We'll try to disable looping is it looks jingly enough. */
loop_flag = !(fsbh.mode & FSOUND_LOOP_OFF);
if(!(fsbh.mode & FSOUND_LOOP_NORMAL) /* rarely set */
&& fsbh.loopstart+fsbh.loopend+1 == fsbh.lengthsamples /* full loop */
&& fsbh.lengthsamples < 20*fsbh.deffreq) /* seconds, lame but no other way to know */
loop_flag = 0;
/* ping-pong looping = no looping? (forward > reverse > forward) */
VGM_ASSERT(fsbh.mode & FSOUND_LOOP_BIDI, "FSB BIDI looping found\n");
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(fsbh.numchannels,loop_flag);
if (!vgmstream) goto fail;
@ -302,7 +308,6 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
#endif
}
else if (fsbh.mode & FSOUND_IMAADPCM) { /* (codec 0x69, Voxware Byte Aligned) */
//VGM_ASSERT(fsbh.mode & FSOUND_IMAADPCMSTEREO, "FSB FSOUND_IMAADPCMSTEREO found\n");
/* FSOUND_IMAADPCMSTEREO is "noninterleaved, true stereo IMA", but doesn't seem to be any different
* (found in FSB4: Shatter, Blade Kitten (PC), Hard Corps: Uprising (PS3)) */
@ -316,7 +321,6 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
else if (fsbh.mode & FSOUND_VAG) {
/* FSB1: Jurassic Park Operation Genesis
* FSB3: ?; FSB4: Spider Man Web of Shadows, Speed Racer, Silent Hill: Shattered Memories (PS2) */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10;
@ -348,37 +352,26 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
}
else if (fsbh.mode & FSOUND_GCADPCM) {
/* FSB3: ?; FSB4: de Blob (Wii), Night at the Museum, M. Night Shyamalan Avatar: The Last Airbender */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave_byte;
vgmstream->interleave_block_size = 0x2;
dsp_read_coefs_be(vgmstream, streamFile, custom_data_offset, 0x2e);
}
else if (fsbh.mode & FSOUND_CELT) { /* || fsbh.mode & FSOUND_OGG (same flag) */
else if (fsbh.mode & FSOUND_CELT) { /* || fsbh.mode & FSOUND_OGG (same flag, unused) */
/* FSB4: War Thunder (PC), The Witcher 2 (PC) */
VGM_LOG("FSB4 FSOUND_CELT found\n");
goto fail;
}
else { /* PCM */
if (fsbh.mode & FSOUND_8BITS) {
VGM_LOG("FSB FSOUND_8BITS found\n");
if (fsbh.mode & FSOUND_UNSIGNED) {
vgmstream->coding_type = coding_PCM8_U; /* ? coding_PCM8_U_int */
} else { /* FSOUND_SIGNED */
vgmstream->coding_type = coding_PCM8; /* ? coding_PCM8_int / coding_PCM8_SB_int */
}
vgmstream->coding_type = (fsbh.mode & FSOUND_UNSIGNED) ? coding_PCM8_U : coding_PCM8;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x1;
}
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) {
vgmstream->coding_type = coding_PCM16BE;
} else {
vgmstream->coding_type = coding_PCM16LE; /* ? coding_PCM16LE_int ? */
}
vgmstream->coding_type = (fsbh.flags & FMOD_FSB_SOURCE_BIGENDIANPCM) ? coding_PCM16BE : coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x2;
}

View File

@ -42,23 +42,23 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
for (i = 1; i <= TotalStreams; i++) {
off_t DataStart = 0;
size_t StreamHeaderLength = 0;
uint32_t SampleMode, SampleMode2;
uint32_t SampleMode1, SampleMode2;
/* seems ok but could use some testing against FMOD's SDK */
SampleMode = (uint32_t)read_32bitLE(SampleHeaderStart+0x00,streamFile);
SampleMode1 = (uint32_t)read_32bitLE(SampleHeaderStart+0x00,streamFile);
SampleMode2 = (uint32_t)read_32bitLE(SampleHeaderStart+0x04,streamFile);
StreamHeaderLength += 0x08;
/* get samples */
NumSamples = ((SampleMode2 >> 2) & 0x3FFFFFFF); /* bits 31..2 (30) */
// bits 1..0 part of DataStart?
/* get offset inside data section */
DataStart = ((SampleMode >> 7) & 0x0FFFFFF) << 5; /* bits 31..8 (24) * 0x20 */
DataStart = ((SampleMode1 >> 7) & 0x1FFFFFF) << 5; /* bits 31..8 (25) * 0x20 */
//SampleMode2 bits 1..0 part of DataStart for files larger than 0x3FFFFFE0?
/* get channels (from tests seems correct, but multichannel isn't very common, ex. no 4ch mode?) */
switch ((SampleMode >> 5) & 0x03) { /* bits 7..6 (2) */
switch ((SampleMode1 >> 5) & 0x03) { /* bits 7..6 (2) */
case 0: ChannelCount = 1; break;
case 1: ChannelCount = 2; break;
case 2: ChannelCount = 6; break;/* some Dark Souls 2 MPEG; some IMA ADPCM */
@ -68,7 +68,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
}
/* get sample rate */
switch ((SampleMode >> 1) & 0x0f) { /* bits 5..1 (4) */
switch ((SampleMode1 >> 1) & 0x0f) { /* bits 5..1 (4) */
case 0: SampleRate = 4000; break; //???
case 1: SampleRate = 8000; break;
case 2: SampleRate = 11000; break;
@ -86,7 +86,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
}
/* get extra flags */
if (SampleMode & 0x01) { /* bit 0 (1) */
if (SampleMode1 & 0x01) { /* bit 0 (1) */
uint32_t ExtraFlag, ExtraFlagStart, ExtraFlagType, ExtraFlagSize, ExtraFlagEnd;
ExtraFlagStart = SampleHeaderStart+0x08;
@ -192,8 +192,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
case 0x00: /* FMOD_SOUND_FORMAT_NONE */
goto fail;
case 0x01: /* FMOD_SOUND_FORMAT_PCM8 */
goto fail;
case 0x01: /* FMOD_SOUND_FORMAT_PCM8 [Anima - Gate of Memories (PC)] */
vgmstream->layout_type = ChannelCount == 1 ? layout_none : layout_interleave;
vgmstream->interleave_block_size = 0x01;
vgmstream->coding_type = coding_PCM8_U;
break;
case 0x02: /* FMOD_SOUND_FORMAT_PCM16 */
if (ChannelCount == 1) {

View File

@ -11,7 +11,8 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
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;
size_t file_size, header_size, data_size, stream_size_full = 0, stream_size = 0, stream_size_expected = 0;
size_t block_size = 0, block_size_total = 0;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int i, total_segments;
int total_streams, target_stream = streamFile->stream_index;
@ -43,50 +44,64 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
/* inside header chunk (many unknown fields are probably IDs/config, as two same-sized files vary a lot) */
off = 0x0c + 0x0c;
/* 0x00: actual header size (less than chunk size), useful to check endianness (GC/Wii/X360 = BE) */
read_32bit = (read_32bitLE(off+0x00,streamFile) > header_size) ? read_32bitBE : read_32bitLE;
/* 0x04-14: sizes of various sections?, others: ? */
/* get base header */
/* 0x00: actual header size (less than chunk size), 0x04/08/10: sizes of various sections?, 0x14/18/24/2C: commands?
* 0x1c: null? 0x30: 0x800?, 0x34: block_size_total?, 0x38: data offset, 0x3c: 0?, 0x40-50: file uuid */
read_32bit = (read_32bitLE(off+0x00,streamFile) > header_size) ? read_32bitBE : read_32bitLE; /* GC/Wii/X360 = BE */
total_segments = read_32bit(off+0x20,streamFile);
total_streams = read_32bit(off+0x28,streamFile);
/* 0x2c: unk, 0x30: 0x800?, 0x34: max block size?, 0x38: data offset, 0x3c: 0?, 0x40-50: file uuid? */
off += 0x50 + get_rws_string_size(off+0x50, streamFile); /* skip audio file name */
/* check streams/segments */
/* Data can be divided into segments (cues/divisions within data, ex. intro+main, voice1+2+..N) or
* tracks/streams in interleaved blocks; last track (only?) has some padding. Tracks seems to be used for multichannel.
* ex.- 0x1800 data + 0 pad of stream_0 2ch, 0x1800 data + 0x200 pad of stream1 2ch (xN) */
/* skip audio file name */
off += 0x50 + get_rws_string_size(off+0x50, streamFile);
/* Data is divided into "segments" (cues/divisions within data, ex. intro+main, voice1+2+..N) and "streams"
* of interleaved blocks (for multichannel?). last stream (only?) has padding. Segments divide all streams.
* ex.- 0x1800 data + 0 pad of stream_0 2ch, 0x1800 data + 0x200 pad of stream1 2ch (xN). */
if (target_stream == 0) target_stream = 1;
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
/* skip segment stuff and get stream size (from sizes for all segments, repeated per track) */
off += 0x20 * total_segments; /* segment data (mostly unknown except @ 0x18: full data size, 0x1c: offset) */
/* get segment info, for all streams */
/* 0x00/04/0c: command?, 0x18: full segment size (including all streams), 0x1c: offset, others: ?) */
for (i = 0; i < total_segments; i++) {
stream_size_full += read_32bit(off + 0x20*i + 0x18,streamFile);
}
off += 0x20 * total_segments;
/* get usable segment sizes (usually ok but sometimes > stream_size), per stream */
for (i = 0; i < total_segments; i++) { /* sum usable segment sizes (no padding) */
stream_size += read_32bit(off + 0x04*i + 0x04*total_segments*(target_stream-1),streamFile);
}
off += 0x04 * (total_segments * total_streams);
off += 0x10 * total_segments; /* segment uuids? */
for (i = 0; i < total_segments; i++) { /* skip segments names */
/* skip segment uuids */
off += 0x10 * total_segments;
/* skip segment names */
for (i = 0; i < total_segments; i++) {
off += get_rws_string_size(off, streamFile);
}
/* get stream layout: 0xc: samples per frame (ex. 28 in VAG), 0x24: offset within data chunk, others: ? */
/* get block_size for our target stream and from all streams, to skip their blocks during decode */
/* get stream layout */
/* 0x00/04/14: command?, 0x08: null? 0x0c: spf related? (XADPCM=07, VAG=1C, DSP=0E, PCM=01)
* 0x24: offset within data chunk, 0x1c: codec related?, others: ?) */
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 */
block_size_total += read_32bit(off + 0x10 + 0x28*i, streamFile); /* for all streeams, to skip during decode */
if (i+1 == target_stream) {
block_size = read_32bit(off+0x20 + 0x28*i,streamFile); /* actual size */
//block_size_full = read_32bit(off + 0x10 + 0x28*i, streamFile); /* with padding, can be different per stream */
block_size = read_32bit(off + 0x20 + 0x28*i, streamFile); /* without padding */
stream_offset = read_32bit(off + 0x24 + 0x28*i, streamFile); /* within data */
}
}
off += 0x28 * total_streams;
/* get stream config: 0x0c(1): bits per sample, others: ? */
/* get stream config */
/* 0x04: command?, 0x0c(1): bits per sample, others: null? */
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? */
//unk_size = read_32bit(off+0x08, streamFile); /* segment size again? loop-related? */
channel_count = read_8bit(off+0x0d, streamFile);
codec = read_32bitBE(off+0x1c, streamFile); /* uuid of 128b but first 32b is enough */
}
@ -104,7 +119,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
off += 0x04; /* padding/garbage */
}
/* skip uuid? per stream */
/* skip stream uuids */
off += 0x10 * total_streams;
/* get stream name */
@ -116,8 +131,17 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *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 */
/* usually 0x800 but not always */
start_offset = 0x0c + 0x0c + header_size + 0x0c + stream_offset;
/* sometimes it's wrong for no apparent reason (probably a bug in RWS) */
stream_size_expected = (stream_size_full / block_size_total) * (block_size * total_streams) / total_streams;
if (stream_size > stream_size_expected) {
VGM_LOG("RWS: readjusting wrong stream size %x vs expected %x\n", stream_size, stream_size_expected);
stream_size = stream_size_expected;
}
/* build the VGMSTREAM */
@ -132,7 +156,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
vgmstream->layout_type = layout_rws_blocked;
vgmstream->current_block_size = block_size / vgmstream->channels;
vgmstream->full_block_size = block_size_max;
vgmstream->full_block_size = block_size_total;
switch(codec) {
case 0xD01BD217: /* PCM X360 (D01BD217 35874EED B9D9B8E8 6EA9B995) */
@ -168,7 +192,7 @@ VGMSTREAM * init_vgmstream_rws(STREAMFILE *streamFile) {
vgmstream->coding_type = coding_XBOX;
vgmstream->interleave_block_size = 0; /* uses regular XBOX/MS-IMA interleave */
vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, 0x48, channel_count);
vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, 0x24 * channel_count, channel_count);
break;
default:

View File

@ -624,8 +624,12 @@ fail:
}
static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
if (!txth->channels)
return 0; /* div-by-zero is no fun */
switch(txth->codec) {
case MS_IMA:
if (!txth->interleave) return 0;
return ms_ima_bytes_to_samples(bytes, txth->interleave, txth->channels);
case XBOX:
return ms_ima_bytes_to_samples(bytes, 0x24 * txth->channels, txth->channels);
@ -642,10 +646,13 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
case PCM8_U:
return pcm_bytes_to_samples(bytes, txth->channels, 8);
case MSADPCM:
if (!txth->interleave) return 0;
return msadpcm_bytes_to_samples(bytes, txth->interleave, txth->channels);
case ATRAC3:
if (!txth->interleave) return 0;
return atrac3_bytes_to_samples(bytes, txth->interleave);
case ATRAC3PLUS:
if (!txth->interleave) return 0;
return atrac3plus_bytes_to_samples(bytes, txth->interleave);
/* XMA bytes-to-samples is done at the end as the value meanings are a bit different */
@ -663,6 +670,7 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
case NGC_DTK:
return bytes / 32 * 28; /* always stereo? */
case APPLE_IMA4:
if (!txth->interleave) return 0;
return (bytes / txth->interleave) * (txth->interleave - 2) * 2;
case MPEG: /* a bit complex */