Merge pull request #274 from bnnm/ck-xa-fsb-str

CK, XA, FSB, STR
This commit is contained in:
Christopher Snowhill 2018-08-04 13:58:32 -07:00 committed by GitHub
commit 3a7b590dfe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 1278 additions and 835 deletions

View File

@ -15,22 +15,25 @@ void decode_g721(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
void g72x_init_state(struct g72x_state *state_ptr);
/* ima_decoder */
void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_dat4_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_standard_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo, int is_high_first);
void decode_3ds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_wv6_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel);
void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel);
void decode_xbox_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_xbox_ima_mch(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_xbox_ima_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_standard_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo, int is_high_first);
void decode_3ds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_dat4_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_rad_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel);
void decode_rad_ima_mono(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_apple_ima4(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel);
void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel);
void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
size_t ima_bytes_to_samples(size_t bytes, int channels);
@ -114,7 +117,8 @@ void decode_nwa(NWAData *nwa, sample *outbuf, int32_t samples_to_do);
/* msadpcm_decoder */
void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do);
void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do);
void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
long msadpcm_bytes_to_samples(long bytes, int block_size, int channels);
/* yamaha_decoder */

View File

@ -169,6 +169,25 @@ static void otns_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset,
if (*step_index > 88) *step_index=88;
}
/* Fairly OddParents (PC) .WV6: minor variation, reverse engineered from the .exe */
static void wv6_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) {
int sample_nibble, sample_decoded, step, delta;
sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf;
sample_decoded = *hist1;
step = ADPCMTable[*step_index];
delta = (sample_nibble & 0x7);
delta = ((delta * step) >> 3) + ((delta * step) >> 2);
if (sample_nibble & 8) delta = -delta;
sample_decoded += delta;
*hist1 = clamp16(sample_decoded);
*step_index += IMA_IndexTable[sample_nibble];
if (*step_index < 0) *step_index=0;
if (*step_index > 88) *step_index=88;
}
/* ************************************ */
/* DVI/IMA */
/* ************************************ */
@ -269,6 +288,28 @@ void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample *
stream->adpcm_step_index = step_index;
}
/* WV6 IMA, DVI IMA with custom nibble expand */
void decode_wv6_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int i, sample_count;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
//external interleave
//no header
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
off_t byte_offset = stream->offset + i/2;
int nibble_shift = (i&1?0:4); //high nibble first
wv6_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
outbuf[sample_count] = (short)(hist1);
}
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
/* ************************************ */
/* MS-IMA */
/* ************************************ */

View File

@ -127,12 +127,33 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
goto fail;
current_data_size = info.frame_size;
/* get FSB padding for Layer III or multichannel Layer II (Layer I doesn't seem to be supported)
/* get FSB padding for Layer III or multichannel Layer II (Layer I isn't supported by FMOD).
* Padding sometimes contains garbage like the next frame header so we can't feed it to mpg123 or it gets confused. */
if ((info.layer == 3 && data->config.fsb_padding) || data->config.fsb_padding == 16) {
current_padding = (current_data_size % data->config.fsb_padding)
? data->config.fsb_padding - (current_data_size % data->config.fsb_padding)
: 0;
/* Rare Mafia II (PS3) bug (GP_0701_music multilang only): some frame paddings "4" are incorrect,
* calcs give 0xD0+0x00 but need 0xD0+0x04 (unlike all other fsbs, which never do that).
* FMOD tools decode fine, so they may be doing special detection too, since even
* re-encoding the same file and using the same FSB flags/modes won't trigger the bug. */
if (info.layer == 3 && data->config.fsb_padding == 4 && current_data_size == 0xD0) {
uint32_t next_header;
off_t next_offset;
next_offset = stream->offset + current_data_size + current_padding;
if (current_interleave && ((next_offset - stream->channel_start_offset + current_interleave_pre + current_interleave_post) % current_interleave == 0)) {
next_offset += current_interleave_pre + current_interleave_post;
}
next_header = read_32bitBE(next_offset, stream->streamfile);
if ((next_header & 0xFFE00000) != 0xFFE00000) { /* doesn't land in a proper frame, fix sizes and hope */
VGM_LOG_ONCE("MPEG FSB: stream with wrong padding found\n");
current_padding = 0x04;
}
}
}
VGM_ASSERT(data->streams_size > 1 && current_interleave != current_data_size+current_padding,
@ -267,9 +288,9 @@ int mpeg_get_frame_info(STREAMFILE *streamfile, off_t offset, mpeg_frame_info *
/* calculate frame length (from hcs's fsb_mpeg) */
switch (info->frame_samples) {
case 384: info->frame_size = (12l * info->bit_rate * 1000l / info->sample_rate + padding) * 4; break;
case 576: info->frame_size = (72l * info->bit_rate * 1000l / info->sample_rate + padding); break;
case 1152: info->frame_size = (144l * info->bit_rate * 1000l / info->sample_rate + padding); break;
case 384: info->frame_size = (12l * info->bit_rate * 1000l / info->sample_rate + padding) * 4; break; /* 384/32 = 12 */
case 576: info->frame_size = (72l * info->bit_rate * 1000l / info->sample_rate + padding); break; /* 576/8 = 72 */
case 1152: info->frame_size = (144l * info->bit_rate * 1000l / info->sample_rate + padding); break; /* 1152/8 = 144 */
default: goto fail;
}

View File

@ -1,17 +1,15 @@
#include "../util.h"
#include "coding.h"
/* used to compute next scale */
static const int ADPCMTable[16] =
{
static const int msadpcm_steps[16] = {
230, 230, 230, 230,
307, 409, 512, 614,
768, 614, 512, 409,
307, 230, 230, 230
};
static const int ADPCMCoeffs[7][2] =
{
static const int msadpcm_coefs[7][2] = {
{ 256, 0 },
{ 512, -256 },
{ 0, 0 },
@ -23,138 +21,203 @@ static const int ADPCMCoeffs[7][2] =
void decode_msadpcm_stereo(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do) {
VGMSTREAMCHANNEL *ch1,*ch2;
int i;
int framesin;
STREAMFILE *streamfile;
off_t offset;
framesin = first_sample/get_vgmstream_samples_per_frame(vgmstream);
first_sample = first_sample%get_vgmstream_samples_per_frame(vgmstream);
int i, frames_in;
size_t bytes_per_frame, samples_per_frame;
off_t frame_offset;
ch1 = &vgmstream->ch[0];
ch2 = &vgmstream->ch[1];
streamfile = ch1->streamfile;
offset = ch1->offset+framesin*get_vgmstream_frame_size(vgmstream);
/* external interleave (variable size), stereo */
bytes_per_frame = get_vgmstream_frame_size(vgmstream);
samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
frames_in = first_sample / samples_per_frame;
first_sample = first_sample % samples_per_frame;
frame_offset = ch1->offset + frames_in*bytes_per_frame;
/* parse frame header */
if (first_sample == 0) {
ch1->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][0];
ch1->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,streamfile) & 0x07][1];
ch2->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][0];
ch2->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x01,streamfile)][1];
ch1->adpcm_scale = read_16bitLE(frame_offset+0x02,streamfile);
ch2->adpcm_scale = read_16bitLE(frame_offset+0x04,streamfile);
ch1->adpcm_history1_16 = read_16bitLE(frame_offset+0x06,streamfile);
ch2->adpcm_history1_16 = read_16bitLE(frame_offset+0x08,streamfile);
ch1->adpcm_history2_16 = read_16bitLE(frame_offset+0x0a,streamfile);
ch2->adpcm_history2_16 = read_16bitLE(frame_offset+0x0c,streamfile);
}
/* write header samples (needed) */
if (first_sample==0) {
ch1->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset,streamfile)][0];
ch1->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset,streamfile)][1];
ch2->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset+1,streamfile)][0];
ch2->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset+1,streamfile)][1];
ch1->adpcm_scale = read_16bitLE(offset+2,streamfile);
ch2->adpcm_scale = read_16bitLE(offset+4,streamfile);
ch1->adpcm_history1_16 = read_16bitLE(offset+6,streamfile);
ch2->adpcm_history1_16 = read_16bitLE(offset+8,streamfile);
ch1->adpcm_history2_16 = read_16bitLE(offset+10,streamfile);
ch2->adpcm_history2_16 = read_16bitLE(offset+12,streamfile);
outbuf[0] = ch1->adpcm_history2_16;
outbuf[1] = ch2->adpcm_history2_16;
outbuf+=2;
outbuf += 2;
first_sample++;
samples_to_do--;
}
if (first_sample==1 && samples_to_do > 0) {
if (first_sample == 1 && samples_to_do > 0) {
outbuf[0] = ch1->adpcm_history1_16;
outbuf[1] = ch2->adpcm_history1_16;
outbuf+=2;
outbuf += 2;
first_sample++;
samples_to_do--;
}
for (i=first_sample; i<first_sample+samples_to_do; i++) {
int j;
/* decode nibbles */
for (i = first_sample; i < first_sample+samples_to_do; i++) {
int ch;
for (j=0;j<2;j++)
{
VGMSTREAMCHANNEL *ch = &vgmstream->ch[j];
int sample_nibble =
(j == 0 ?
get_high_nibble_signed(read_8bit(offset+14+i-2,streamfile)) :
get_low_nibble_signed(read_8bit(offset+14+i-2,streamfile))
);
int32_t hist1,hist2;
int32_t predicted;
for (ch = 0; ch < 2; ch++) {
VGMSTREAMCHANNEL *stream = &vgmstream->ch[ch];
int32_t hist1,hist2, predicted;
int sample_nibble = (ch == 0) ? /* L = high nibble first */
get_high_nibble_signed(read_8bit(frame_offset+0x07*2+(i-2),streamfile)) :
get_low_nibble_signed (read_8bit(frame_offset+0x07*2+(i-2),streamfile));
hist1 = ch->adpcm_history1_16;
hist2 = ch->adpcm_history2_16;
predicted = hist1 * ch->adpcm_coef[0] + hist2 * ch->adpcm_coef[1];
predicted /= 256;
predicted += sample_nibble*ch->adpcm_scale;
hist1 = stream->adpcm_history1_16;
hist2 = stream->adpcm_history2_16;
predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1];
predicted = predicted / 256;
predicted = predicted + sample_nibble*stream->adpcm_scale;
outbuf[0] = clamp16(predicted);
ch->adpcm_history2_16 = ch->adpcm_history1_16;
ch->adpcm_history1_16 = outbuf[0];
ch->adpcm_scale = (ADPCMTable[sample_nibble&0xf] *
ch->adpcm_scale) / 256;
if (ch->adpcm_scale < 0x10) ch->adpcm_scale = 0x10;
stream->adpcm_history2_16 = stream->adpcm_history1_16;
stream->adpcm_history1_16 = outbuf[0];
stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256;
if (stream->adpcm_scale < 0x10)
stream->adpcm_scale = 0x10;
outbuf++;
}
}
}
void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int32_t first_sample, int32_t samples_to_do) {
VGMSTREAMCHANNEL *ch1;
int i;
int framesin;
STREAMFILE *streamfile;
off_t offset;
void decode_msadpcm_mono(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel];
int i, frames_in;
size_t bytes_per_frame, samples_per_frame;
off_t frame_offset;
framesin = first_sample/get_vgmstream_samples_per_frame(vgmstream);
first_sample = first_sample%get_vgmstream_samples_per_frame(vgmstream);
/* external interleave (variable size), mono */
bytes_per_frame = get_vgmstream_frame_size(vgmstream);
samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
frames_in = first_sample / samples_per_frame;
first_sample = first_sample % samples_per_frame;
ch1 = &vgmstream->ch[0];
streamfile = ch1->streamfile;
offset = ch1->offset+framesin*get_vgmstream_frame_size(vgmstream);
frame_offset = stream->offset + frames_in*bytes_per_frame;
if (first_sample==0) {
ch1->adpcm_coef[0] = ADPCMCoeffs[read_8bit(offset,streamfile)][0];
ch1->adpcm_coef[1] = ADPCMCoeffs[read_8bit(offset,streamfile)][1];
ch1->adpcm_scale = read_16bitLE(offset+1,streamfile);
ch1->adpcm_history1_16 = read_16bitLE(offset+3,streamfile);
ch1->adpcm_history2_16 = read_16bitLE(offset+5,streamfile);
/* parse frame header */
if (first_sample == 0) {
stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0];
stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1];
stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile);
stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x03,stream->streamfile);
stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x05,stream->streamfile);
}
outbuf[0] = ch1->adpcm_history2_16;
outbuf++;
/* write header samples (needed) */
if (first_sample == 0) {
outbuf[0] = stream->adpcm_history2_16;
outbuf += channelspacing;
first_sample++;
samples_to_do--;
}
if (first_sample==1 && samples_to_do > 0) {
outbuf[0] = ch1->adpcm_history1_16;
outbuf++;
if (first_sample == 1 && samples_to_do > 0) {
outbuf[0] = stream->adpcm_history1_16;
outbuf += channelspacing;
first_sample++;
samples_to_do--;
}
for (i=first_sample; i<first_sample+samples_to_do; i++) {
{
VGMSTREAMCHANNEL *ch = &vgmstream->ch[0];
int sample_nibble =
(i & 1 ?
get_low_nibble_signed(read_8bit(offset+7+(i-2)/2,streamfile)) :
get_high_nibble_signed(read_8bit(offset+7+(i-2)/2,streamfile))
);
int32_t hist1,hist2;
int32_t predicted;
/* decode nibbles */
for (i = first_sample; i < first_sample+samples_to_do; i++) {
int32_t hist1,hist2, predicted;
int sample_nibble = (i & 1) ? /* high nibble first */
get_low_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) :
get_high_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile));
hist1 = ch->adpcm_history1_16;
hist2 = ch->adpcm_history2_16;
predicted = hist1 * ch->adpcm_coef[0] + hist2 * ch->adpcm_coef[1];
predicted /= 256;
predicted += sample_nibble*ch->adpcm_scale;
hist1 = stream->adpcm_history1_16;
hist2 = stream->adpcm_history2_16;
predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1];
predicted = predicted / 256;
predicted = predicted + sample_nibble*stream->adpcm_scale;
outbuf[0] = clamp16(predicted);
ch->adpcm_history2_16 = ch->adpcm_history1_16;
ch->adpcm_history1_16 = outbuf[0];
ch->adpcm_scale = (ADPCMTable[sample_nibble&0xf] *
ch->adpcm_scale) / 256;
if (ch->adpcm_scale < 0x10) ch->adpcm_scale = 0x10;
outbuf++;
stream->adpcm_history2_16 = stream->adpcm_history1_16;
stream->adpcm_history1_16 = outbuf[0];
stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) / 256;
if (stream->adpcm_scale < 0x10)
stream->adpcm_scale = 0x10;
outbuf += channelspacing;
}
}
/* Cricket Audio's MSADPCM, same thing with reversed hist and nibble order
* (their tools may convert to float/others but internally it's all PCM16, from debugging). */
void decode_msadpcm_ck(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
VGMSTREAMCHANNEL *stream = &vgmstream->ch[channel];
int i, frames_in;
size_t bytes_per_frame, samples_per_frame;
off_t frame_offset;
/* external interleave (variable size), mono */
bytes_per_frame = get_vgmstream_frame_size(vgmstream);
samples_per_frame = get_vgmstream_samples_per_frame(vgmstream);
frames_in = first_sample / samples_per_frame;
first_sample = first_sample % samples_per_frame;
frame_offset = stream->offset + frames_in*bytes_per_frame;
/* parse frame header */
if (first_sample == 0) {
stream->adpcm_coef[0] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][0];
stream->adpcm_coef[1] = msadpcm_coefs[read_8bit(frame_offset+0x00,stream->streamfile) & 0x07][1];
stream->adpcm_scale = read_16bitLE(frame_offset+0x01,stream->streamfile);
stream->adpcm_history2_16 = read_16bitLE(frame_offset+0x03,stream->streamfile); /* hist2 first, unlike normal MSADPCM */
stream->adpcm_history1_16 = read_16bitLE(frame_offset+0x05,stream->streamfile);
}
/* write header samples (needed) */
if (first_sample == 0) {
outbuf[0] = stream->adpcm_history2_16;
outbuf += channelspacing;
first_sample++;
samples_to_do--;
}
if (first_sample == 1 && samples_to_do > 0) {
outbuf[0] = stream->adpcm_history1_16;
outbuf += channelspacing;
first_sample++;
samples_to_do--;
}
/* decode nibbles */
for (i = first_sample; i < first_sample+samples_to_do; i++) {
int32_t hist1,hist2, predicted;
int sample_nibble = (i & 1) ? /* low nibble first, unlike normal MSADPCM */
get_high_nibble_signed (read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile)) :
get_low_nibble_signed(read_8bit(frame_offset+0x07+(i-2)/2,stream->streamfile));
hist1 = stream->adpcm_history1_16;
hist2 = stream->adpcm_history2_16;
predicted = hist1*stream->adpcm_coef[0] + hist2*stream->adpcm_coef[1];
predicted = predicted >> 8; /* probably no difference vs MSADPCM */
predicted = predicted + sample_nibble*stream->adpcm_scale;
outbuf[0] = clamp16(predicted);
stream->adpcm_history2_16 = stream->adpcm_history1_16;
stream->adpcm_history1_16 = outbuf[0];
stream->adpcm_scale = (msadpcm_steps[sample_nibble & 0xf] * stream->adpcm_scale) >> 8;
if (stream->adpcm_scale < 0x10)
stream->adpcm_scale = 0x10;
outbuf += channelspacing;
}
}

View File

@ -1,51 +1,65 @@
#include "coding.h"
#include "../util.h"
/* Nintendo GC Disc TracK streaming ADPCM (similar to CD-XA) */
void decode_ngc_dtk(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i=first_sample;
int32_t sample_count;
int framesin = first_sample/28;
uint8_t q = read_8bit(framesin*32+stream->offset+channel,stream->streamfile);
off_t frame_offset;
int i, frames_in, sample_count = 0;
size_t bytes_per_frame, samples_per_frame;
uint8_t coef_index, shift_factor;
int32_t hist1 = stream->adpcm_history1_32;
int32_t hist2 = stream->adpcm_history2_32;
first_sample = first_sample%28;
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
int sample_byte = read_8bit(framesin*32+stream->offset+4+i,stream->streamfile);
/* external interleave (fixed size), stereo */
bytes_per_frame = 0x20;
samples_per_frame = 28;
frames_in = first_sample / samples_per_frame;
first_sample = first_sample % samples_per_frame;
int32_t hist=0;
/* parse frame L/R header (repeated at 0x03/04) */
frame_offset = stream->offset + bytes_per_frame*frames_in;
coef_index = ((uint8_t)read_8bit(frame_offset+channel,stream->streamfile) >> 4) & 0xf;
shift_factor = ((uint8_t)read_8bit(frame_offset+channel,stream->streamfile) >> 0) & 0xf;
/* rare but happens, also repeated headers don't match (ex. Ikaruga (GC) SONG02.adp) */
VGM_ASSERT_ONCE(coef_index > 4 || shift_factor > 12, "DTK: incorrect coefs/shift at %lx\n", frame_offset);
switch (q>>4)
{
/* decode nibbles */
for (i = first_sample; i < first_sample+samples_to_do; i++) {
int32_t hist = 0, new_sample;
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x04+i,stream->streamfile);
/* apply XA filters << 6 */
switch(coef_index) {
case 0:
hist = 0;
hist = 0; // (hist1 * 0) - (hist2 * 0);
break;
case 1:
hist = (hist1 * 0x3c);
hist = (hist1 * 60); // - (hist2 * 0);
break;
case 2:
hist = (hist1 * 0x73) - (hist2 * 0x34);
hist = (hist1 * 115) - (hist2 * 52);
break;
case 3:
hist = (hist1 * 0x62) - (hist2 * 0x37);
hist = (hist1 * 98) - (hist2 * 55);
break;
}
hist = (hist+0x20)>>6;
hist = (hist + 32) >> 6;
if (hist > 0x1fffff) hist = 0x1fffff;
if (hist < -0x200000) hist = -0x200000;
new_sample = (channel==0) ? /* L=low nibble first */
get_low_nibble_signed(nibbles) :
get_high_nibble_signed(nibbles);
new_sample = (new_sample << 12) >> shift_factor;
new_sample = (new_sample << 6) + hist;
hist2 = hist1;
hist1 = new_sample;
hist1 = ((((channel==0?
get_low_nibble_signed(sample_byte):
get_high_nibble_signed(sample_byte)
) << 12) >> (q & 0xf)) << 6) + hist;
outbuf[sample_count] = clamp16(hist1 >> 6);
outbuf[sample_count] = clamp16(new_sample >> 6);
sample_count += channelspacing;
}
stream->adpcm_history1_32 = hist1;

View File

@ -1,7 +1,8 @@
#include "coding.h"
/* Decodes Konami XMD from Xbox games (algorithm info from xmd2wav/xmddecode.dll). */
/* Decodes Konami XMD from Xbox games.
* Algorithm reverse engineered from SH4/CV:CoD's xbe (byte-accurate). */
void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size) {
off_t frame_offset;
int i, frames_in, sample_count = 0, samples_done = 0;
@ -42,10 +43,9 @@ void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
new_sample = i&1 ? /* low nibble first */
get_high_nibble_signed(nibbles):
get_low_nibble_signed(nibbles);
new_sample = (new_sample*(scale<<14) + (hist1*0x7298) - (hist2*0x3350)) >> 14;
/* Coefs are similar to XA's filter 2, but using those creates hissing in some songs.
/* Coefs are based on XA's filter 2 (using those creates hissing in some songs though)
* ex. 1.796875 * (1 << 14) = 0x7300, -0.8125 * (1 << 14) = -0x3400 */
//new_sample = (int32_t)(new_sample*scale + hist1*1.796875 + hist2*-0.8125);
new_sample = (new_sample*(scale<<14) + (hist1*0x7298) - (hist2*0x3350)) >> 14;
//new_sample = clamp16(new_sample); /* not needed */
if (sample_count >= first_sample && samples_done < samples_to_do) {

View File

@ -4,7 +4,7 @@
/* defines the list of accepted extensions. vgmstream doesn't use it internally so it's here
* to inform plugins that need it. Common extensions are commented out to avoid stealing them. */
/* some extensions require external libraries and could be #ifdef, no really needed */
/* some extensions require external libraries and could be #ifdef, not really needed */
/* some formats marked as "not parsed" mean they'll go through FFmpeg, the header/extension is not parsed */
@ -98,7 +98,9 @@ static const char* extension_list[] = {
"ccc",
"cd",
"cfn", //fake extension/header id for .caf (to be removed)
"ckb",
"ckd",
"cks",
"cnk",
"cps",
"cvs",
@ -420,6 +422,7 @@ static const char* extension_list[] = {
"wsd",
"wsi",
"wv2", //txth/reserved [Slave Zero (PC)]
"wv6",
"wve",
"wvs",
@ -526,6 +529,10 @@ static const coding_info coding_info_list[] = {
{coding_DVI_IMA, "Intel DVI 4-bit IMA ADPCM"},
{coding_DVI_IMA_int, "Intel DVI 4-bit IMA ADPCM (mono/interleave)"},
{coding_3DS_IMA, "3DS IMA 4-bit ADPCM"},
{coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"},
{coding_OTNS_IMA, "Omikron: The Nomad Soul 4-bit IMA ADPCM"},
{coding_WV6_IMA, "Gorilla Systems WV6 4-bit IMA ADPCM"},
{coding_MS_IMA, "Microsoft 4-bit IMA ADPCM"},
{coding_XBOX_IMA, "XBOX 4-bit IMA ADPCM"},
{coding_XBOX_IMA_mch, "XBOX 4-bit IMA ADPCM (multichannel)"},
@ -535,8 +542,6 @@ static const coding_info coding_info_list[] = {
{coding_RAD_IMA, "Radical 4-bit IMA ADPCM"},
{coding_RAD_IMA_mono, "Radical 4-bit IMA ADPCM (mono/interleave)"},
{coding_APPLE_IMA4, "Apple Quicktime 4-bit IMA ADPCM"},
{coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"},
{coding_OTNS_IMA, "Omikron: The Nomad Soul 4-bit IMA ADPCM"},
{coding_FSB_IMA, "FSB 4-bit IMA ADPCM"},
{coding_WWISE_IMA, "Audiokinetic Wwise 4-bit IMA ADPCM"},
{coding_REF_IMA, "Reflections 4-bit IMA ADPCM"},
@ -544,6 +549,7 @@ static const coding_info coding_info_list[] = {
{coding_UBI_IMA, "Ubisoft 4-bit IMA ADPCM"},
{coding_MSADPCM, "Microsoft 4-bit ADPCM"},
{coding_MSADPCM_ck, "Microsoft 4-bit ADPCM (Cricket Audio)"},
{coding_WS, "Westwood Studios VBR ADPCM"},
{coding_AICA, "Yamaha AICA 4-bit ADPCM"},
{coding_AICA_int, "Yamaha AICA 4-bit ADPCM (mono/interleave)"},
@ -698,7 +704,7 @@ static const meta_info meta_info_list[] = {
{meta_PS2_VAGm, "Sony VAG Mono header (VAGm)"},
{meta_PS2_pGAV, "Sony VAG Stereo Little Endian header (pGAV)"},
{meta_PSX_GMS, "assumed Grandia GMS file by .gms extension"},
{meta_PS2_STR, "assumed STR + STH File by .str & .sth extension"},
{meta_STR_WAV, "Blitz Games STR+WAV header"},
{meta_PS2_ILD, "ILD header"},
{meta_PS2_PNB, "assumed PNB (PsychoNauts Bgm File) by .pnb extension"},
{meta_XBOX_WAVM, "Xbox WAVM raw header"},
@ -845,7 +851,6 @@ static const meta_info meta_info_list[] = {
{meta_PS2_SND, "Might and Magic SSND Header"},
{meta_PS2_VSF_TTA, "VSF with SMSS Header"},
{meta_ADS, "dhSS Header"},
{meta_WII_STR, "HOTD Overkill - STR+STH WII Header"},
{meta_PS2_MCG, "Gunvari MCG Header"},
{meta_ZSD, "ZSD Header"},
{meta_RedSpark, "RedSpark Header"},
@ -902,7 +907,6 @@ static const meta_info meta_info_list[] = {
{meta_PS2_B1S, "B1S header"},
{meta_PS2_WAD, "WAD header"},
{meta_DSP_XIII, "XIII dsp header"},
{meta_NGC_DSP_STH_STR, "STH dsp header"},
{meta_DSP_CABELAS, "Cabelas games dsp header"},
{meta_PS2_ADM, "Dragon Quest V .ADM raw header"},
{meta_PS2_LPCM, "LPCM header"},
@ -1046,6 +1050,9 @@ static const meta_info meta_info_list[] = {
{meta_OGG_MUS, "Ogg Vorbis (MUS header)"},
{meta_ASF, "Argonaut ASF header"},
{meta_XMD, "Konami XMD header"},
{meta_CKS, "Cricket Audio CKS header"},
{meta_CKB, "Cricket Audio CKB header"},
{meta_WV6, "Gorilla Systems WV6 header"},
#ifdef VGM_USE_FFMPEG
{meta_FFmpeg, "FFmpeg supported file format"},

View File

@ -28,25 +28,33 @@ void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) {
(uint8_t)read_8bit(block_offset + 0x930 + 0x11,streamFile),
"XA block: subchannel change at %lx\n", block_offset);
/* submode flag bits (typical audio value = 0x64)
* - 7: end of file
* - 6: real time mode
* - 5: sector form (0=form1, 1=form2)
* - 4: trigger (for application)
* - 3: data sector
* - 2: audio sector
* - 1: video sector
* - 0: end of audio
/* submode flag bits (typical audio value = 0x64 01100100)
* - 7 (0x80 10000000): end of file
* - 6 (0x40 01000000): real time mode
* - 5 (0x20 00100000): sector form (0=form1, 1=form2)
* - 4 (0x10 00010000): trigger (for application)
* - 3 (0x08 00001000): data sector
* - 2 (0x04 00000100): audio sector
* - 1 (0x02 00000010): video sector
* - 0 (0x01 00000001): end of audio
*/
xa_submode = (uint8_t)read_8bit(block_offset + 0x12,streamFile);
/* audio sector must set/not set certain flags, as per spec */
if ((xa_submode & 0x20) && !(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02) ) {
block_samples = (28*8 / vgmstream->channels) * 18; /* size 0x900, 18 frames of size 0x80 with 8 subframes of 28 samples */
if (!(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02)) {
if (xa_submode & 0x20) {
/* form2 audio: size 0x900, 18 frames of size 0x80 with 8 subframes of 28 samples */
block_samples = (28*8 / vgmstream->channels) * 18;
}
else { /* rare, found with empty audio [Glint Glitters (PS1), Dance! Dance! Dance! (PS1)] */
/* form1 audio: size 0x800, 16 frames of size 0x80 with 8 subframes of 28 samples (rest is garbage/other data) */
block_samples = (28*8 / vgmstream->channels) * 16;
}
}
else {
;VGM_ASSERT_ONCE(block_offset < get_streamfile_size(streamFile),
"XA block: non audio block found at %lx\n", block_offset);
block_samples = 0; /* not an audio sector */
;VGM_LOG("XA block: non audio block found at %lx\n", block_offset);
}
vgmstream->current_block_offset = block_offset;

View File

@ -356,6 +356,10 @@
RelativePath=".\meta\capdsp.c"
>
</File>
<File
RelativePath=".\meta\ck.c"
>
</File>
<File
RelativePath=".\meta\cstr.c"
>
@ -396,10 +400,6 @@
RelativePath=".\meta\dsp_bdsp.c"
>
</File>
<File
RelativePath=".\meta\dsp_sth_str.c"
>
</File>
<File
RelativePath=".\meta\ea_schl.c"
>
@ -1034,10 +1034,6 @@
RelativePath=".\meta\ps2_ster.c"
>
</File>
<File
RelativePath=".\meta\ps2_str.c"
>
</File>
<File
RelativePath=".\meta\ps2_strlr.c"
>
@ -1282,6 +1278,10 @@
RelativePath=".\meta\str_snds.c"
>
</File>
<File
RelativePath=".\meta\str_wav.c"
>
</File>
<File
RelativePath=".\meta\stx.c"
>
@ -1394,10 +1394,6 @@
RelativePath=".\meta\wii_sng.c"
>
</File>
<File
RelativePath=".\meta\wii_str.c"
>
</File>
<File
RelativePath=".\meta\wii_sts.c"
>
@ -1410,6 +1406,10 @@
RelativePath=".\meta\ws_aud.c"
>
</File>
<File
RelativePath=".\meta\wv6.c"
>
</File>
<File
RelativePath=".\meta\wvs.c"
>

View File

@ -207,6 +207,7 @@
<ClCompile Include="meta\brstm.c" />
<ClCompile Include="meta\btsnd.c" />
<ClCompile Include="meta\capdsp.c" />
<ClCompile Include="meta\ck.c" />
<ClCompile Include="meta\cstr.c" />
<ClCompile Include="meta\dc_asd.c" />
<ClCompile Include="meta\dc_dcsw_dcs.c" />
@ -217,7 +218,6 @@
<ClCompile Include="meta\dmsg_segh.c" />
<ClCompile Include="meta\dsp_adx.c" />
<ClCompile Include="meta\dsp_bdsp.c" />
<ClCompile Include="meta\dsp_sth_str.c" />
<ClCompile Include="meta\ea_schl.c" />
<ClCompile Include="meta\ea_schl_fixed.c" />
<ClCompile Include="meta\ea_1snh.c" />
@ -352,7 +352,6 @@
<ClCompile Include="meta\ps2_snd.c" />
<ClCompile Include="meta\ps2_sps.c" />
<ClCompile Include="meta\ps2_ster.c" />
<ClCompile Include="meta\ps2_str.c" />
<ClCompile Include="meta\ps2_svag.c" />
<ClCompile Include="meta\ps2_svag_snk.c" />
<ClCompile Include="meta\ps2_tec.c" />
@ -402,6 +401,7 @@
<ClCompile Include="meta\stm.c" />
<ClCompile Include="meta\str_asr.c" />
<ClCompile Include="meta\str_snds.c" />
<ClCompile Include="meta\str_wav.c" />
<ClCompile Include="meta\stx.c" />
<ClCompile Include="meta\svs.c" />
<ClCompile Include="meta\sxd.c" />
@ -426,9 +426,9 @@
<ClCompile Include="meta\wii_mus.c" />
<ClCompile Include="meta\wii_smp.c" />
<ClCompile Include="meta\wii_sng.c" />
<ClCompile Include="meta\wii_str.c" />
<ClCompile Include="meta\wii_sts.c" />
<ClCompile Include="meta\ws_aud.c" />
<ClCompile Include="meta\wv6.c" />
<ClCompile Include="meta\wvs.c" />
<ClCompile Include="meta\wwise.c" />
<ClCompile Include="meta\xau.c" />

View File

@ -214,6 +214,9 @@
<ClCompile Include="meta\capdsp.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ck.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\cstr.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -241,9 +244,6 @@
<ClCompile Include="meta\dsp_bdsp.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\dsp_sth_str.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ea_schl.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -631,9 +631,6 @@
<ClCompile Include="meta\ps2_ster.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_str.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_svag.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -781,6 +778,9 @@
<ClCompile Include="meta\str_snds.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\str_wav.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\stx.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -853,15 +853,15 @@
<ClCompile Include="meta\wii_sng.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\wii_str.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\wii_sts.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ws_aud.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\wv6.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\wvs.c">
<Filter>meta\Source Files</Filter>
</ClCompile>

177
src/meta/ck.c Normal file
View File

@ -0,0 +1,177 @@
#include "meta.h"
/* .cks - Cricket Audio stream [Part Time UFO (Android), Mega Man 1-6 (Android)] */
VGMSTREAM * init_vgmstream_cks(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, codec, sample_rate;
int32_t num_samples, loop_start, loop_end;
size_t block_size;
/* checks */
if (!check_extensions(streamFile, "cks"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x636B6D6B) /* "ckmk" */
goto fail;
/* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */
if (read_32bitLE(0x08,streamFile) != 0x00) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */
goto fail;
if (read_32bitLE(0x0c,streamFile) != 0x02) /* file version (always 0x02) */
goto fail;
codec = read_8bit(0x10,streamFile);
channel_count = read_8bit(0x11,streamFile);
sample_rate = (uint16_t)read_16bitLE(0x12,streamFile);
num_samples = read_32bitLE(0x14,streamFile) * read_16bitLE(0x1a,streamFile); /* number_of_blocks * samples_per_frame */
block_size = read_16bitLE(0x18,streamFile);
/* 0x1c(2): volume */
/* 0x1e(2): pan */
loop_start = read_32bitLE(0x20,streamFile);
loop_end = read_32bitLE(0x24,streamFile);
loop_flag = read_16bitLE(0x28,streamFile) != 0; /* loop count (-1 = forever) */
/* 0x2a(2): unused? */
start_offset = 0x2c;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
vgmstream->meta_type = meta_CKS;
switch(codec) {
case 0x00: /* pcm16 [from tests] */
vgmstream->coding_type = coding_PCM16LE;
break;
case 0x01: /* pcm8 [from tests] */
vgmstream->coding_type = coding_PCM8;
break;
case 0x02: /* adpcm [Part Time UFO (Android), Mega Man 1-6 (Android)] */
vgmstream->coding_type = coding_MSADPCM_ck;
/* frame_size is always 0x18 */
break;
default:
goto fail;
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = block_size / channel_count;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* .ckb - Cricket Audio bank [Fire Emblem Heroes (Android), Mega Man 1-6 (Android)] */
VGMSTREAM * init_vgmstream_ckb(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, name_offset = 0;
int loop_flag, channel_count, codec, sample_rate;
int32_t num_samples, loop_start, loop_end;
size_t block_size, stream_size;
int total_subsongs, target_subsong = streamFile->stream_index;
/* checks */
if (!check_extensions(streamFile, "ckb"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x636B6D6B) /* "ckmk" */
goto fail;
/* 0x04(4): platform bitflags (from LSB: iOS, Android, OS X, Windows, WP8, Linux, tvOS, undefined/ignored) */
if (read_32bitLE(0x08,streamFile) != 0x01) /* expected file type (0x00: stream, 0x01: bank, 0x02+: unknown) */
goto fail;
if (read_32bitLE(0x0c,streamFile) != 0x02) /* file version (always 0x02) */
goto fail;
/* 0x10: bank name (size 0x1c+1) */
/* 0x30/34: reserved? */
total_subsongs = read_32bitLE(0x38,streamFile);
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
/* 0x3c: total_subsongs again? (ignored) */
/* 0x40/44: unknown (ignored) */
/* get subsong (stream offset isn't given so must calc manually) */
{
int i;
off_t header_offset = 0x48;
off_t stream_offset = 0x48 + total_subsongs*0x48;
for (i = 0; i < total_subsongs; i++) {
name_offset = header_offset+0x00; /* stream name (size 0x1c+1) */
codec = read_8bit(header_offset+0x20,streamFile);
channel_count = read_8bit(header_offset+0x21,streamFile);
sample_rate = (uint16_t)read_16bitLE(header_offset+0x22,streamFile);
num_samples = read_32bitLE(header_offset+0x24,streamFile) * read_16bitLE(header_offset+0x2a,streamFile); /* number_of_blocks * samples_per_frame */
block_size = read_16bitLE(header_offset+0x28,streamFile);
/* 0x2c(2): volume */
/* 0x2e(2): pan */
loop_start = read_32bitLE(header_offset+0x30,streamFile);
loop_end = read_32bitLE(header_offset+0x34,streamFile);
loop_flag = read_16bitLE(header_offset+0x38,streamFile) != 0; /* loop count (-1 = forever) */
/* 0x3a(2): unused? */
stream_size = read_32bitLE(header_offset+0x3c,streamFile);
/* 0x40/44(4): unused? */
if (target_subsong == (i+1))
break;
header_offset += 0x48;
stream_offset += stream_size;
}
start_offset = stream_offset;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
read_string(vgmstream->stream_name,0x1c+1, name_offset,streamFile);
vgmstream->meta_type = meta_CKB;
switch(codec) {
case 0x00: /* pcm16 [Mega Man 1-6 (Android)] */
vgmstream->coding_type = coding_PCM16LE;
break;
case 0x01: /* pcm8 */
vgmstream->coding_type = coding_PCM8;
break;
case 0x02: /* adpcm [Fire Emblem Heroes (Android)] */
vgmstream->coding_type = coding_MSADPCM_ck;
/* frame_size is always 0x18 */
break;
default:
goto fail;
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = block_size / channel_count;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,320 +0,0 @@
#include "meta.h"
#include "../util.h"
/*
STH+STR
found in SpongebobSquarepants: Creature From The Krusty Krab (NGC)
*/
VGMSTREAM * init_vgmstream_ngc_dsp_sth_str1(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamFileSTR = NULL;
char filename[PATH_LIMIT];
char filenameSTR[PATH_LIMIT];
int i, j;
int channel_count;
int loop_flag;
off_t coef_table[8] = {0x12C,0x18C,0x1EC,0x24C,0x2AC,0x30C,0x36C,0x3CC};
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("sth",filename_extension(filename))) goto fail;
strcpy(filenameSTR,filename);
strcpy(filenameSTR+strlen(filenameSTR)-3,"str");
streamFileSTR = streamFile->open(streamFile,filenameSTR,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!streamFileSTR) goto fail;
if (read_32bitBE(0x0,streamFile) != 0x0)
{
goto fail;
}
if (read_32bitBE(0x4,streamFile) != 0x800)
{
goto fail;
}
/* Not really channel_count, just 'included tracks * channels per track */
loop_flag = (read_32bitBE(0xD8,streamFile) != 0xFFFFFFFF);
channel_count = (read_32bitBE(0x70,streamFile)) * (read_32bitBE(0x88,streamFile));
if (channel_count > 8)
{
goto fail;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitBE(0x24,streamFile);
vgmstream->num_samples=get_streamfile_size(streamFileSTR)/8/channel_count*14;
vgmstream->coding_type = coding_NGC_DSP;
if(loop_flag)
{
vgmstream->loop_start_sample = read_32bitBE(0xD8,streamFile);
vgmstream->loop_end_sample = read_32bitBE(0xDC,streamFile);
}
if (channel_count == 1)
{
vgmstream->layout_type = layout_none;
}
else
{
vgmstream->layout_type = layout_interleave;
if (channel_count == 2)
{
vgmstream->interleave_block_size=0x10000;
}
else
{
vgmstream->interleave_block_size=0x8000;
}
}
vgmstream->meta_type = meta_NGC_DSP_STH_STR;
/* open the file for reading */
for (i=0;i<channel_count;i++)
{
vgmstream->ch[i].streamfile = streamFileSTR->open(streamFileSTR,filenameSTR,0x8000);
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset=vgmstream->ch[i].offset=i*vgmstream->interleave_block_size;
}
// COEFFS
for (j=0;j<vgmstream->channels;j++)
{
for (i=0;i<16;i++)
{
vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile);
}
}
close_streamfile(streamFileSTR); streamFileSTR=NULL;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (streamFileSTR) close_streamfile(streamFileSTR);
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}
/*
STH+STR
found in Taz Wanted (NGC), Cubix Robots for Everyone: Showdown (NGC)
*/
VGMSTREAM * init_vgmstream_ngc_dsp_sth_str2(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamFileSTR = NULL;
char filename[PATH_LIMIT];
char filenameSTR[PATH_LIMIT];
int i, j;
int channel_count;
int loop_flag;
off_t coef_table[8] = {0xDC,0x13C,0x19C,0x1FC,0x25C,0x2BC,0x31C,0x37C};
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("sth",filename_extension(filename))) goto fail;
strcpy(filenameSTR,filename);
strcpy(filenameSTR+strlen(filenameSTR)-3,"str");
streamFileSTR = streamFile->open(streamFile,filenameSTR,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!streamFileSTR) goto fail;
if (read_32bitBE(0x0,streamFile) != 0x0)
{
goto fail;
}
if (read_32bitBE(0x4,streamFile) != 0x900)
{
goto fail;
}
/* Not really channel_count, just 'included tracks * channels per track */
loop_flag = (read_32bitBE(0xB8,streamFile) != 0xFFFFFFFF);
channel_count = read_32bitBE(0x50,streamFile)*2;
if (channel_count > 8)
{
goto fail;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitBE(0x24,streamFile);
vgmstream->num_samples=get_streamfile_size(streamFileSTR)/8/channel_count*14;
vgmstream->coding_type = coding_NGC_DSP;
if(loop_flag)
{
vgmstream->loop_start_sample = read_32bitBE(0xB8,streamFile);
vgmstream->loop_end_sample = read_32bitBE(0xBC,streamFile);
}
if (channel_count == 1)
{
vgmstream->layout_type = layout_none;
}
else
{
vgmstream->layout_type = layout_interleave;
if (channel_count == 2)
{
vgmstream->interleave_block_size=0x10000;
}
else
{
vgmstream->interleave_block_size=0x8000;
}
}
vgmstream->meta_type = meta_NGC_DSP_STH_STR;
/* open the file for reading */
for (i=0;i<channel_count;i++)
{
vgmstream->ch[i].streamfile = streamFileSTR->open(streamFileSTR,filenameSTR,0x8000);
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset=vgmstream->ch[i].offset=i*vgmstream->interleave_block_size;
}
// COEFFS
for (j=0;j<vgmstream->channels;j++)
{
for (i=0;i<16;i++)
{
vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile);
}
}
close_streamfile(streamFileSTR); streamFileSTR=NULL;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (streamFileSTR) close_streamfile(streamFileSTR);
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}
/*
STH+STR
found in Tak and the Guardians of Gross (WII)
*/
VGMSTREAM * init_vgmstream_ngc_dsp_sth_str3(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamFileSTR = NULL;
char filename[PATH_LIMIT];
char filenameSTR[PATH_LIMIT];
int i, j;
int channel_count;
int loop_flag;
off_t coef_table[8] = {read_32bitBE(0x7C,streamFile),read_32bitBE(0x80,streamFile),read_32bitBE(0x84,streamFile),read_32bitBE(0x88,streamFile),read_32bitBE(0x8C,streamFile),read_32bitBE(0x90,streamFile),read_32bitBE(0x94,streamFile),read_32bitBE(0x98,streamFile)};
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("sth",filename_extension(filename))) goto fail;
strcpy(filenameSTR,filename);
strcpy(filenameSTR+strlen(filenameSTR)-3,"str");
streamFileSTR = streamFile->open(streamFile,filenameSTR,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!streamFileSTR) goto fail;
if (read_32bitBE(0x0,streamFile) != 0x0)
{
goto fail;
}
if ((read_32bitBE(0x4,streamFile) != 0x700) &&
(read_32bitBE(0x4,streamFile) != 0x800))
{
goto fail;
}
/* Not really channel_count, just 'included tracks * channels per track */
loop_flag = 0;
channel_count = read_32bitBE(0x70,streamFile);
if (channel_count > 8)
{
goto fail;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitBE(0x38,streamFile);
vgmstream->num_samples=get_streamfile_size(streamFileSTR)/8/channel_count*14;
vgmstream->coding_type = coding_NGC_DSP;
if(loop_flag)
{
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = 0;
}
if (channel_count == 1)
{
vgmstream->layout_type = layout_none;
}
else
{
vgmstream->layout_type = layout_interleave;
if (channel_count == 2 || channel_count == 4)
{
vgmstream->interleave_block_size=0x8000;
}
else
{
vgmstream->interleave_block_size=0x4000;
}
}
vgmstream->meta_type = meta_NGC_DSP_STH_STR;
/* open the file for reading */
for (i=0;i<channel_count;i++)
{
vgmstream->ch[i].streamfile = streamFileSTR->open(streamFileSTR,filenameSTR,0x8000);
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset=vgmstream->ch[i].offset=i*vgmstream->interleave_block_size;
}
// COEFFS
for (j=0;j<vgmstream->channels;j++)
{
for (i=0;i<16;i++)
{
vgmstream->ch[j].adpcm_coef[i] = read_16bitBE(coef_table[j]+i*2,streamFile);
}
}
close_streamfile(streamFileSTR); streamFileSTR=NULL;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (streamFileSTR) close_streamfile(streamFileSTR);
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -230,7 +230,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
data_offset += fsb.stream_size; /* there is no offset so manually count */
/* some subsongs offsets need padding (most FSOUND_IMAADPCM, few MPEG too [Hard Reset (PC) subsong 5])
* other PADDED4 may set it (ex. XMA) but don't seem to use it and work fine */
* other codecs may set PADDED4 (ex. XMA) but don't seem to need it and work fine */
if (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED4) {
if (data_offset % 0x20)
data_offset += 0x20 - (data_offset % 0x20);
@ -252,12 +252,12 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
"FSB wrong head/data_size found (expected 0x%x vs 0x%x)\n",
fsb.base_header_size + fsb.sample_header_size + fsb.data_size, streamFile->get_size(streamFile));
/* Loops unless disabled. FMOD default seems full loops (0/num_samples-1) without flags, for repeating tracks
/* Loops unless disabled. FMOD default seems to be 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 if it looks jingly enough. */
loop_flag = !(fsb.mode & FSOUND_LOOP_OFF);
if(!(fsb.mode & FSOUND_LOOP_NORMAL) /* rarely set */
&& fsb.loop_start+fsb.loop_end+1 == fsb.num_samples /* full loop */
&& fsb.num_samples < 20*fsb.sample_rate) /* seconds, lame but no other way to know */
&& fsb.num_samples < 20*fsb.sample_rate) /* in seconds (lame but no other way to know) */
loop_flag = 0;
/* ping-pong looping = no looping? (forward > reverse > forward) [ex. Biker Mice from Mars (PS2)] */
@ -280,7 +280,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
/* parse codec */
if (fsb.mode & FSOUND_MPEG) { /* FSB4: Shatter, Way of the Samurai 3/4 (PS3) */
if (fsb.mode & FSOUND_MPEG) { /* FSB4: Shatter (PS3), Way of the Samurai 3/4 (PS3) */
#if defined(VGM_USE_MPEG)
mpeg_custom_config cfg = {0};
@ -288,33 +288,38 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
(fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED4 ? 4 :
(fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED ? 2 : 0)));
//VGM_ASSERT(fsb.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n");/* not always set anyway */
VGM_ASSERT(fsb.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n");
vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_FSB, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
//VGM_ASSERT(fsb.mode & FSOUND_MPEG_LAYER2, "FSB FSOUND_MPEG_LAYER2 found\n");/* not always set anyway */
VGM_ASSERT(fsb.mode & FSOUND_IGNORETAGS, "FSB FSOUND_IGNORETAGS found\n");
#else
goto fail; /* FFmpeg can't properly read FSB4 or FMOD's 0-padded MPEG data @ start_offset */
#endif
}
else if (fsb.mode & FSOUND_IMAADPCM) { /* FSB3: Bioshock (PC); FSB4: Blade Kitten (PC) */
/* 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)) */
else if (fsb.mode & FSOUND_IMAADPCM) { /* FSB3: Bioshock (PC), FSB4: Blade Kitten (PC) */
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
/* "interleaved header" IMA, only used with >2ch (ex. Blade Kitten 6ch)
* or (seemingly) when flag is used (ex. Dead to Rights 2 (Xbox) 2ch in FSB3.1 */
* or (seemingly) when flag is used (ex. Dead to Rights 2 (Xbox) 2ch in FSB3.1) */
if (vgmstream->channels > 2 || (fsb.mode & FSOUND_MULTICHANNEL))
vgmstream->coding_type = coding_FSB_IMA;
/* 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)) */
}
else if (fsb.mode & FSOUND_VAG) { /* FSB1: Jurassic Park Operation Genesis (PS2), FSB4: Spider Man Web of Shadows (PSP) */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
if (fsb.flags & FMOD_FSB_SOURCE_NOTINTERLEAVED) {
vgmstream->interleave_block_size = fsb.stream_size / fsb.channels;
}
else {
vgmstream->interleave_block_size = 0x10;
}
else if (fsb.mode & FSOUND_XMA) { /* FSB4: Armored Core V (X360), Hard Corps (X360) */
}
else if (fsb.mode & FSOUND_XMA) { /* FSB3: The Bourne Conspiracy 2008 (X360), FSB4: Armored Core V (X360), Hard Corps (X360) */
#if defined(VGM_USE_FFMPEG)
uint8_t buf[0x100];
size_t bytes, block_size, block_count;
@ -323,8 +328,6 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
block_count = fsb.stream_size / block_size; /* not accurate but not needed (custom_data_offset+0x14 -1?) */
bytes = ffmpeg_make_riff_xma2(buf, 0x100, fsb.num_samples, fsb.stream_size, fsb.channels, fsb.sample_rate, block_count, block_size);
if (bytes <= 0) goto fail;
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,fsb.stream_size);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
@ -333,38 +336,35 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
goto fail;
#endif
}
else if (fsb.mode & FSOUND_GCADPCM) {
/* FSB3: ?; FSB4: de Blob (Wii), Night at the Museum, M. Night Shyamalan Avatar: The Last Airbender */
else if (fsb.mode & FSOUND_GCADPCM) { /* FSB3: Metroid Prime 3 (GC), FSB4: de Blob (Wii) */
if (fsb.flags & FMOD_FSB_SOURCE_NOTINTERLEAVED) { /* [de Blob (Wii) sfx)] */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = fsb.stream_size / fsb.channels;
}
else {
vgmstream->coding_type = coding_NGC_DSP_subint;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = 0x2;
}
dsp_read_coefs_be(vgmstream, streamFile, custom_data_offset, 0x2e);
}
else if (fsb.mode & FSOUND_CELT) { /* FSB4: War Thunder (PC), The Witcher 2 (PC) */
VGM_LOG("FSB4 FSOUND_CELT found\n");
VGM_LOG("FSB4: FSOUND_CELT found\n");
goto fail;
}
else { /* PCM */
if (fsb.mode & FSOUND_8BITS) {
else if (fsb.mode & FSOUND_8BITS) { /* assumed, no games known */
vgmstream->coding_type = (fsb.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 */
else { /* (PCM16) FSB4: Rocket Knight (PC), Another Century's Episode R (PS3), Toy Story 3 (Wii) */
vgmstream->coding_type = (fsb.flags & FMOD_FSB_SOURCE_BIGENDIANPCM) ? coding_PCM16BE : coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x2;
}
}
/* full channel interleave, used in short streams (ex. de Blob Wii SFXs) */
if (fsb.channels > 1 && (fsb.flags & FMOD_FSB_SOURCE_NOTINTERLEAVED)) {
if (vgmstream->coding_type == coding_NGC_DSP_subint)
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = fsb.stream_size / fsb.channels;
/* sometimes FSOUND_MONO/FSOUND_STEREO is not set (ex. Dead Space iOS),
* or only STEREO/MONO but not FSOUND_8BITS/FSOUND_16BITS is set */
}

View File

@ -10,7 +10,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
size_t SampleHeaderLength, NameTableLength, SampleDataLength, BaseHeaderLength, StreamSize = 0, ExtraInfoSize = 0;
uint32_t NumSamples = 0, LoopStart = 0, LoopEnd = 0;
int LoopFlag = 0, ChannelCount = 0, Version, SampleRate = 0, CodingID;
int LoopFlag = 0, ChannelCount = 0, Version, SampleRate = 0, Codec, Flags = 0;
int TotalSubsongs, TargetSubsong = streamFile->stream_index;
int i;
@ -29,9 +29,12 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
SampleHeaderLength = read_32bitLE(0x0C,streamFile);
NameTableLength = read_32bitLE(0x10,streamFile);
SampleDataLength = read_32bitLE(0x14,streamFile);
CodingID = read_32bitLE(0x18,streamFile);
/* type 0x01 - 0x1c(8): zero, 0x24(16): hash, 0x34(8): unk
* type 0x00 has an extra field (always 0?) at 0x1c */
Codec = read_32bitLE(0x18,streamFile);
/* version 0x01 - 0x1c(4): zero, 0x24(16): hash, 0x34(8): unk
* version 0x00 has an extra field (always 0?) at 0x1c */
if (Version == 0x01) {
Flags = read_32bitLE(0x20,streamFile); /* found by tests and assumed to be flags, no games known */
}
BaseHeaderLength = (Version==0x00) ? 0x40 : 0x3C;
if ((SampleHeaderLength + NameTableLength + SampleDataLength + BaseHeaderLength) != get_streamfile_size(streamFile)) {
@ -39,7 +42,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
goto fail;
}
if (TargetSubsong == 0) TargetSubsong = 1; /* default to 1 */
if (TargetSubsong == 0) TargetSubsong = 1;
if (TargetSubsong > TotalSubsongs || TotalSubsongs <= 0) goto fail;
SampleHeaderStart = BaseHeaderLength;
@ -49,34 +52,34 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
for (i = 1; i <= TotalSubsongs; i++) {
off_t DataStart = 0;
size_t StreamHeaderLength = 0;
uint32_t SampleMode1, SampleMode2;
uint32_t SampleMode1, SampleMode2; /* maybe one uint64? */
/* seems ok but could use some testing against FMOD's SDK */
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) */
NumSamples = ((SampleMode2 >> 2) & 0x3FFFFFFF); /* bits2: 31..2 (30) */
/* get offset inside data section */
DataStart = ((SampleMode1 >> 7) & 0x1FFFFFF) << 5; /* bits 31..8 (25) * 0x20 */
//SampleMode2 bits 1..0 part of DataStart for files larger than 0x3FFFFFE0?
/* up to 0x07FFFFFF * 0x20 = full 32b offset 0xFFFFFFE0 (recheck, after 0x80000000 some calcs may be off?) */
DataStart = ((SampleMode2 & 0x03) << 25) | ((SampleMode1 >> 7) & 0x1FFFFFF) << 5; /* bits2: 1..0 (2) | bits1: 31..8 (25) */
/* get channels (from tests seems correct, but multichannel isn't very common, ex. no 4ch mode?) */
switch ((SampleMode1 >> 5) & 0x03) { /* bits 7..6 (2) */
/* get channels */
switch ((SampleMode1 >> 5) & 0x03) { /* bits1: 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 */
case 3: ChannelCount = 8; break;/* some IMA ADPCM */
default: /* other values (ex. 10ch) are specified in the extra flags, using 0 here */
/* other channels (ex. 4/10/12ch) use 0 here + set extra flags */
default: /* not possible */
goto fail;
}
/* get sample rate */
switch ((SampleMode1 >> 1) & 0x0f) { /* bits 5..1 (4) */
case 0: SampleRate = 4000; break; //???
switch ((SampleMode1 >> 1) & 0x0f) { /* bits1: 5..1 (4) */
case 0: SampleRate = 4000; break;
case 1: SampleRate = 8000; break;
case 2: SampleRate = 11000; break;
case 3: SampleRate = 11025; break;
@ -86,14 +89,14 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
case 7: SampleRate = 32000; break;
case 8: SampleRate = 44100; break;
case 9: SampleRate = 48000; break;
case 10: SampleRate = 96000; break; //???
default: /* probably specified in the extra flags */
SampleRate = 44100;
break;
case 10: SampleRate = 96000; break;
/* other sample rates (ex. 3000/64000/192000) use 0 here + set extra flags */
default: /* 11-15: rejected (FMOD error) */
goto fail;
}
/* get extra flags */
if (SampleMode1 & 0x01) { /* bit 0 (1) */
if (SampleMode1 & 0x01) { /* bits1: 0 (1) */
uint32_t ExtraFlag, ExtraFlagStart, ExtraFlagType, ExtraFlagSize, ExtraFlagEnd;
ExtraFlagStart = SampleHeaderStart+0x08;
@ -104,13 +107,13 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
ExtraFlagEnd = (ExtraFlag & 0x01); /* bit 0 (1) */
switch(ExtraFlagType) {
case 0x01: /* Channel Info */
case 0x01: /* channels */
ChannelCount = read_8bit(ExtraFlagStart+0x04,streamFile);
break;
case 0x02: /* Sample Rate Info */
case 0x02: /* sample rate */
SampleRate = read_32bitLE(ExtraFlagStart+0x04,streamFile);
break;
case 0x03: /* Loop Info */
case 0x03: /* loop info */
LoopStart = read_32bitLE(ExtraFlagStart+0x04,streamFile);
if (ExtraFlagSize > 0x04) /* probably not needed */
LoopEnd = read_32bitLE(ExtraFlagStart+0x08,streamFile);
@ -126,14 +129,15 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
case 0x06: /* XMA seek table */
/* no need for it */
break;
case 0x07: /* DSP coeffs */
case 0x07: /* DSP coefs */
ExtraInfoStart = ExtraFlagStart + 0x04;
break;
case 0x09: /* ATRAC9 config */
ExtraInfoStart = ExtraFlagStart + 0x04;
ExtraInfoSize = ExtraFlagSize;
break;
case 0x0a: /* XWMA data */
case 0x0a: /* XWMA config */
ExtraInfoStart = ExtraFlagStart + 0x04;
break;
case 0x0b: /* Vorbis setup ID and seek table */
ExtraInfoStart = ExtraFlagStart + 0x04;
@ -202,7 +206,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
/* parse codec */
switch (CodingID) {
switch (Codec) {
case 0x00: /* FMOD_SOUND_FORMAT_NONE */
goto fail;
@ -212,48 +216,65 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
vgmstream->interleave_block_size = 0x01;
break;
case 0x02: /* FMOD_SOUND_FORMAT_PCM16 */
vgmstream->coding_type = coding_PCM16LE;
case 0x02: /* FMOD_SOUND_FORMAT_PCM16 [Shantae Risky's Revenge (PC)] */
vgmstream->coding_type = (Flags & 0x01) ? coding_PCM16BE : coding_PCM16LE;
vgmstream->layout_type = ChannelCount == 1 ? layout_none : layout_interleave;
vgmstream->interleave_block_size = 0x02;
break;
case 0x03: /* FMOD_SOUND_FORMAT_PCM24 */
goto fail; /* not used */
VGM_LOG("FSB5: FMOD_SOUND_FORMAT_PCM24 found\n");
goto fail;
case 0x04: /* FMOD_SOUND_FORMAT_PCM32 */
goto fail; /* not used */
VGM_LOG("FSB5: FMOD_SOUND_FORMAT_PCM32 found\n");
goto fail;
case 0x05: /* FMOD_SOUND_FORMAT_PCMFLOAT [Anima - Gate of Memories (PC)] */
case 0x05: /* FMOD_SOUND_FORMAT_PCMFLOAT [Anima: Gate of Memories (PC)] */
vgmstream->coding_type = coding_PCMFLOAT;
vgmstream->layout_type = (ChannelCount == 1) ? layout_none : layout_interleave;
vgmstream->interleave_block_size = 0x04;
break;
case 0x06: /* FMOD_SOUND_FORMAT_GCADPCM [Sonic Boom - Fire and Ice (3DS)] */
case 0x06: /* FMOD_SOUND_FORMAT_GCADPCM [Sonic Boom: Fire and Ice (3DS)] */
if (Flags & 0x02) { /* non-interleaved mode */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = (StreamSize / ChannelCount);
}
else {
vgmstream->coding_type = coding_NGC_DSP_subint;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = 0x02;
}
dsp_read_coefs_be(vgmstream,streamFile,ExtraInfoStart,0x2E);
break;
case 0x07: /* FMOD_SOUND_FORMAT_IMAADPCM */
case 0x07: /* FMOD_SOUND_FORMAT_IMAADPCM [Skylanders] */
vgmstream->coding_type = (vgmstream->channels > 2) ? coding_FSB_IMA : coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
break;
case 0x08: /* FMOD_SOUND_FORMAT_VAG */
goto fail; /* not used */
case 0x08: /* FMOD_SOUND_FORMAT_VAG [from fsbankex tests, no known games] */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
if (Flags & 0x02) { /* non-interleaved mode */
vgmstream->interleave_block_size = (StreamSize / ChannelCount);
}
else {
vgmstream->interleave_block_size = 0x10;
}
break;
case 0x09: /* FMOD_SOUND_FORMAT_HEVAG */
case 0x09: /* FMOD_SOUND_FORMAT_HEVAG [Guacamelee (Vita)] */
vgmstream->coding_type = coding_HEVAG;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10;
break;
#ifdef VGM_USE_FFMPEG
case 0x0A: {/* FMOD_SOUND_FORMAT_XMA */
case 0x0A: {/* FMOD_SOUND_FORMAT_XMA [Dark Souls 2 (X360)] */
uint8_t buf[0x100];
int bytes, block_size, block_count;
@ -261,8 +282,6 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
block_count = StreamSize / block_size + (StreamSize % block_size ? 1 : 0);
bytes = ffmpeg_make_riff_xma2(buf, 0x100, vgmstream->num_samples, StreamSize, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
if (bytes <= 0) goto fail;
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, StartOffset,StreamSize);
if ( !vgmstream->codec_data ) goto fail;
vgmstream->coding_type = coding_FFmpeg;
@ -272,7 +291,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
#endif
#ifdef VGM_USE_MPEG
case 0x0B: {/* FMOD_SOUND_FORMAT_MPEG */
case 0x0B: {/* FMOD_SOUND_FORMAT_MPEG [Final Fantasy X HD (PS3), Shantae Risky's Revenge (PC)] */
mpeg_custom_config cfg = {0};
cfg.fsb_padding = (vgmstream->channels > 2 ? 16 : 4); /* observed default */
@ -283,7 +302,8 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
break;
}
#endif
case 0x0C: /* FMOD_SOUND_FORMAT_CELT */
case 0x0C: /* FMOD_SOUND_FORMAT_CELT [BIT.TRIP Presents Runner2 (PC), Full Bore (PC)] */
VGM_LOG("FSB5: FMOD_SOUND_FORMAT_CELT found\n");
goto fail;
#ifdef VGM_USE_ATRAC9
@ -317,11 +337,28 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
}
#endif
case 0x0E: /* FMOD_SOUND_FORMAT_XWMA */
goto fail;
#ifdef VGM_USE_FFMPEG
case 0x0E: { /* FMOD_SOUND_FORMAT_XWMA [from fsbankex tests, no known games] */
uint8_t buf[0x100];
int bytes, format, average_bps, block_align;
format = read_16bitBE(ExtraInfoStart+0x00,streamFile);
block_align = (uint16_t)read_16bitBE(ExtraInfoStart+0x02,streamFile);
average_bps = (uint32_t)read_32bitBE(ExtraInfoStart+0x04,streamFile);
/* rest: seek entries + mini seek table? */
/* XWMA encoder only does up to 6ch (doesn't use FSB multistreams for more) */
bytes = ffmpeg_make_riff_xwma(buf,0x100, format, StreamSize, vgmstream->channels, vgmstream->sample_rate, average_bps, block_align);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, StartOffset,StreamSize);
if ( !vgmstream->codec_data ) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
break;
}
#endif
#ifdef VGM_USE_VORBIS
case 0x0F: {/* FMOD_SOUND_FORMAT_VORBIS */
case 0x0F: {/* FMOD_SOUND_FORMAT_VORBIS [Shantae Half Genie Hero (PC), Pokemon Go (iOS)] */
vorbis_custom_config cfg = {0};
cfg.channels = vgmstream->channels;
@ -337,13 +374,14 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
}
#endif
case 0x10: /* FMOD_SOUND_FORMAT_FADPCM */
case 0x10: /* FMOD_SOUND_FORMAT_FADPCM [Dead Rising 4 (PC), Sine Mora Ex (Switch)] */
vgmstream->coding_type = coding_FADPCM;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x8c;
break;
default:
VGM_LOG("FSB5: unknown codec %x found\n", Codec);
goto fail;
}

View File

@ -69,8 +69,6 @@ VGMSTREAM * init_vgmstream_ps2_vag(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_psx_gms(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_str(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_ild(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_pnb(STREAMFILE *streamFile);
@ -385,8 +383,6 @@ VGMSTREAM * init_vgmstream_ps2_vsf_tta(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ads(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_wii_str(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_mcg(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_zsd(STREAMFILE *streamFile);
@ -490,10 +486,6 @@ VGMSTREAM * init_vgmstream_dsp_str_ig(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ea_swvr(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ngc_dsp_sth_str1(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ngc_dsp_sth_str2(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ngc_dsp_sth_str3(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_b1s(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_ps2_wad(STREAMFILE* streamFile);
@ -768,4 +760,11 @@ VGMSTREAM * init_vgmstream_asf(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_xmd(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_cks(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ckb(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_wv6(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile);
#endif /*_META_H*/

View File

@ -2,40 +2,45 @@
#include "meta.h"
#include "../util.h"
/* DTK - headerless Nintendo DTK file [Harvest Moon - Another Wonderful Life (GC), XGRA (GC)] */
/* DTK - headerless Nintendo GC DTK file [Harvest Moon: Another Wonderful Life (GC), XGRA (GC)] */
VGMSTREAM * init_vgmstream_ngc_adpdtk(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset = 0;
int channel_count = 2, loop_flag = 0; /* always stereo, no loop */
off_t start_offset;
int channel_count, loop_flag;
/* checks */
/* dtk: standard [XGRA (GC)], adp: standard [Harvest Moon AWL (GC)], wav/lwav: Alien Hominid (GC) */
/* .dtk: standard [XGRA (GC)], .adp: standard [Harvest Moon AWL (GC)], .wav/lwav: Alien Hominid (GC) */
if ( !check_extensions(streamFile,"dtk,adp,wav,lwav"))
goto fail;
/* files have no header, and the ext is common, so all we can do is look for valid first frames */
if (check_extensions(streamFile,"adp,wav,lwav")) {
/* check valid frames as files have no header, and .adp/wav are common */
{
int i;
for (i = 0; i < 10; i++) { /* try a bunch of frames */
if (read_8bit(0x00 + i*0x20,streamFile) != read_8bit(0x02 + i*0x20,streamFile) ||
read_8bit(0x01 + i*0x20,streamFile) != read_8bit(0x03 + i*0x20,streamFile))
goto fail;
/* header 0x00/01 are repeated in 0x02/03 (for error correction?),
* could also test header values (upper nibble should be 0..3, and lower nibble 0..C) */
}
}
/* always stereo, no loop (since it's hardware-decoded and streamed) */
channel_count = 2;
loop_flag = 0;
start_offset = 0x00;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->num_samples = get_streamfile_size(streamFile) / 32 * 28;
vgmstream->sample_rate = 48000;
vgmstream->num_samples = get_streamfile_size(streamFile) / 0x20 * 28;
vgmstream->sample_rate = 48000; /* due to a GC hardware defect this may be closer to 48043 */
vgmstream->coding_type = coding_NGC_DTK;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_NGC_ADPDTK;
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
@ -45,4 +50,3 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,96 +0,0 @@
#include "meta.h"
#include "../util.h"
/* STR
2008-05-19 - Fastelbja : Test version ...
*/
VGMSTREAM * init_vgmstream_ps2_str(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * infileSTH = NULL;
char filename[PATH_LIMIT];
char * filenameSTH = NULL;
int i, channel_count, loop_flag;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("str",filename_extension(filename))) goto fail;
/* check for .MIH file */
filenameSTH=(char *)malloc(strlen(filename)+1);
if (!filenameSTH) goto fail;
strcpy(filenameSTH,filename);
strcpy(filenameSTH+strlen(filenameSTH)-3,"STH");
infileSTH = streamFile->open(streamFile,filenameSTH,STREAMFILE_DEFAULT_BUFFER_SIZE);
/* STH File is necessary, so we can't confuse those file */
/* with others .STR file as it is a very common extension */
if (!infileSTH) goto fail;
if(read_32bitLE(0x2C,infileSTH)==0)
goto fail;
if((read_32bitLE(0x2C,infileSTH)==0x07) ||
(read_32bitLE(0x2C,infileSTH)==0x06))
channel_count=2;
if(read_32bitLE(0x2C,infileSTH)==0x05)
channel_count=1;
loop_flag = read_32bitLE(0x2C,infileSTH) & 0x01;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x24,infileSTH);
vgmstream->interleave_block_size=0x4000;
if(read_32bitLE(0x40,infileSTH)==0x01)
vgmstream->interleave_block_size = 0x8000;
vgmstream->num_samples=read_32bitLE(0x20,infileSTH);
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->meta_type = meta_PS2_STR;
if(loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = read_32bitLE(0x20,infileSTH);
}
close_streamfile(infileSTH); infileSTH=NULL;
/* open the file for reading by each channel */
{
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size);
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset+=(off_t)(vgmstream->interleave_block_size*i);
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (infileSTH) close_streamfile(infileSTH);
if (filenameSTH) {free(filenameSTH); filenameSTH=NULL;}
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -63,14 +63,18 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) {
if (read_32bitBE(test_offset+0x00,streamFile) != read_32bitBE(test_offset+0x04,streamFile) ||
read_32bitBE(test_offset+0x08,streamFile) != read_32bitBE(test_offset+0x0c,streamFile))
goto fail;
/* blank frames should always use 0x0c0c0c0c (due to how shift works) */
if (read_32bitBE(test_offset+0x00,streamFile) == 0 &&
read_32bitBE(test_offset+0x04,streamFile) == 0 &&
read_32bitBE(test_offset+0x08,streamFile) == 0 &&
read_32bitBE(test_offset+0x0c,streamFile) == 0)
goto fail;
test_offset += 0x80;
}
test_offset += (is_blocked ? 0x18 : 0x00); /* footer */
}
/* (the above could get fooled by files with many 0s at the beginning;
* this could be detected as blank XA frames should have have 0c0c0c0c... headers */
}
@ -140,8 +144,6 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) {
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
//todo do block_updates to find num_samples? (to skip non-audio blocks)
vgmstream->num_samples = xa_bytes_to_samples(file_size - start_offset, channel_count, is_blocked);
vgmstream->meta_type = meta_PSX_XA;
vgmstream->coding_type = coding_XA;
@ -151,6 +153,21 @@ VGMSTREAM * init_vgmstream_cdxa(STREAMFILE *streamFile) {
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
if (is_blocked) {
/* calc num_samples as blocks may be empty or smaller than usual depending on flags */
vgmstream->next_block_offset = start_offset;
do {
block_update_xa(vgmstream->next_block_offset,vgmstream);
vgmstream->num_samples += vgmstream->current_block_samples;
}
while (vgmstream->next_block_offset < get_streamfile_size(streamFile));
}
else {
vgmstream->num_samples = xa_bytes_to_samples(file_size - start_offset, channel_count, is_blocked);
}
if (vgmstream->layout_type == layout_blocked_xa)
block_update_xa(start_offset,vgmstream);
return vgmstream;

452
src/meta/str_wav.c Normal file
View File

@ -0,0 +1,452 @@
#include "meta.h"
#include "../coding/coding.h"
typedef enum { PSX, DSP, XBOX, WMA } strwav_codec;
typedef struct {
int32_t channels;
int32_t sample_rate;
int32_t num_samples;
int32_t loop_start;
int32_t loop_end;
int32_t loop_flag;
size_t interleave;
off_t coefs_offset;
off_t dsps_table;
off_t coefs_table;
uint32_t flags;
strwav_codec codec;
} strwav_header;
static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav);
/* STR+WAV - Blitz Games streams+header */
VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamHeader = NULL;
strwav_header strwav = {0};
off_t start_offset;
/* checks */
if (!check_extensions(streamFile, "str"))
goto fail;
/* get external header (extracted with filenames from bigfiles) */
{
/* try with standard file.wav.str=body + file.wav=header (or file.wma.str + file.wma for Fuzion Frenzy (Xbox)) */
char basename[PATH_LIMIT];
get_streamfile_basename(streamFile,basename,PATH_LIMIT);
streamHeader = open_streamfile_by_filename(streamFile, basename);
if (!streamHeader) {
/* try again with file.str=body + file.wav=header [Bad Boys II (PS2), Zapper (PS2)] */
streamHeader = open_streamfile_by_ext(streamFile, "wav");
if (!streamHeader) {
/* try again with file.str=body + file.sth=header (renamed/fake extension) */
streamHeader = open_streamfile_by_ext(streamFile, "sth");
if (!streamHeader) goto fail;
}
}
else {
if (!check_extensions(streamHeader, "wav,wma"))
goto fail;
}
}
/* detect version */
if (!parse_header(streamHeader, &strwav))
goto fail;
if (strwav.flags == 0)
goto fail;
if (strwav.channels > 8)
goto fail;
/* &0x01: loop?, &0x02: non-mono?, &0x04: stream???, &0x08: unused? */
if (strwav.flags != 0x07 && strwav.flags != 0x06 && strwav.flags != 0x05 && strwav.flags != 0x04 && strwav.flags != 0x02) {
VGM_LOG("STR+WAV: unknown flags\n");
goto fail;
}
start_offset = 0x00;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(strwav.channels,strwav.loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = strwav.sample_rate;
vgmstream->num_samples = strwav.num_samples;
if (strwav.loop_flag) {
vgmstream->loop_start_sample = strwav.loop_start;
vgmstream->loop_end_sample = strwav.loop_end;
}
vgmstream->meta_type = meta_STR_WAV;
switch(strwav.codec) {
case PSX:
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = strwav.interleave;
break;
case DSP:
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = strwav.interleave;
/* get coefs */
{
int i, ch;
for (ch = 0; ch < vgmstream->channels; ch++) {
for (i = 0; i < 16; i++) {
off_t coef_offset;
if (strwav.dsps_table) /* mini table with offsets to DSP headers */
coef_offset = read_32bitBE(strwav.dsps_table+0x04*ch,streamHeader) + 0x1c;
else if (strwav.coefs_table) /* mini table with offsets to coefs then header */
coef_offset = read_32bitBE(strwav.coefs_table+0x04*ch,streamHeader);
else
coef_offset = strwav.coefs_offset + 0x60*ch;
vgmstream->ch[ch].adpcm_coef[i] = read_16bitBE(coef_offset+i*0x02,streamHeader);
}
}
}
break;
case XBOX:
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
if (strwav.channels > 2) goto fail; //todo multistreams are 2ch*N interleaved using ~0xD000
break;
#ifdef VGM_USE_FFMPEG
case WMA: {
ffmpeg_codec_data *ffmpeg_data = NULL;
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,get_streamfile_size(streamFile));
if (!ffmpeg_data) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = ffmpeg_data->totalSamples;
if (vgmstream->channels != ffmpeg_data->channels)
goto fail;
break;
}
#endif
default:
goto fail;
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
close_streamfile(streamHeader);
return vgmstream;
fail:
close_streamfile(streamHeader);
close_vgmstream(vgmstream);
return NULL;
}
/* Parse header versions. Almost every game uses its own variation (struct serialization?),
* so detection could be improved once enough versions are known. */
static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
size_t header_size;
if (read_32bitBE(0x00,streamHeader) != 0x00000000)
goto fail;
header_size = get_streamfile_size(streamHeader);
//todo loop start/end values may be off for some headers
/* Fuzion Frenzy (Xbox)[2001] wma */
if ( read_32bitBE(0x04,streamHeader) == 0x00000900 &&
read_32bitLE(0x20,streamHeader) == 0 && /* no num_samples */
read_32bitLE(0x24,streamHeader) == read_32bitLE(0x80,streamHeader) && /* sample rate repeat */
read_32bitLE(0x28,streamHeader) == 0x10 &&
header_size == 0x110 /* no value in header */
) {
strwav->num_samples = read_32bitLE(0x20,streamHeader); /* 0 but will be rectified later */
strwav->sample_rate = read_32bitLE(0x24,streamHeader);
strwav->flags = read_32bitLE(0x2c,streamHeader);
strwav->loop_start = 0;
strwav->loop_end = 0;
strwav->channels = read_32bitLE(0x60,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
strwav->loop_flag = strwav->flags & 0x01;
strwav->interleave = 0;
strwav->codec = WMA;
;VGM_LOG("STR+WAV: header Fuzion Frenzy (Xbox)\n");
return 1;
}
/* Taz: Wanted (GC)[2002] */
/* Cubix Robots for Everyone: Showdown (GC)[2003] */
if ( read_32bitBE(0x04,streamHeader) == 0x00000900 &&
read_32bitBE(0x24,streamHeader) == read_32bitBE(0x90,streamHeader) && /* sample rate repeat */
read_32bitBE(0x28,streamHeader) == 0x10 &&
read_32bitBE(0xa0,streamHeader) == header_size /* ~0x3C0 */
) {
strwav->num_samples = read_32bitBE(0x20,streamHeader);
strwav->sample_rate = read_32bitBE(0x24,streamHeader);
strwav->flags = read_32bitBE(0x2c,streamHeader);
strwav->loop_start = read_32bitBE(0xb8,streamHeader);
strwav->loop_end = read_32bitBE(0xbc,streamHeader);
strwav->channels = read_32bitBE(0x50,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
strwav->loop_flag = strwav->flags & 0x01;
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000;
strwav->coefs_offset = 0xdc;
strwav->codec = DSP;
;VGM_LOG("STR+WAV: header Taz: Wanted (GC)\n");
return 1;
}
/* The Fairly OddParents - Breakin' da Rules (Xbox)[2003] */
if ( read_32bitBE(0x04,streamHeader) == 0x00000900 &&
read_32bitLE(0x24,streamHeader) == read_32bitLE(0xb0,streamHeader) && /* sample rate repeat */
read_32bitLE(0x28,streamHeader) == 0x10 &&
read_32bitLE(0xc0,streamHeader)*0x04 + read_32bitLE(0xc4,streamHeader) == header_size /* ~0xe0 + variable */
) {
strwav->num_samples = read_32bitLE(0x20,streamHeader);
strwav->sample_rate = read_32bitLE(0x24,streamHeader);
strwav->flags = read_32bitLE(0x2c,streamHeader);
strwav->loop_start = read_32bitLE(0x38,streamHeader);
strwav->loop_end = strwav->num_samples;
strwav->channels = read_32bitLE(0x70,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
strwav->loop_flag = strwav->flags & 0x01;
strwav->interleave = strwav->channels > 2 ? 0xD000 : 0x0;
strwav->codec = XBOX;
;VGM_LOG("STR+WAV: header The Fairly OddParents (Xbox)\n");
return 1;
}
/* Bad Boys II (GC)[2004] */
if ( read_32bitBE(0x04,streamHeader) == 0x00000800 &&
read_32bitBE(0x24,streamHeader) == read_32bitBE(0xb0,streamHeader) && /* sample rate repeat */
read_32bitBE(0x24,streamHeader) == read_32bitBE(read_32bitBE(0xe0,streamHeader)+0x08,streamHeader) && /* sample rate vs 1st DSP header */
read_32bitBE(0x28,streamHeader) == 0x10 &&
read_32bitBE(0xc0,streamHeader)*0x04 + read_32bitBE(0xc4,streamHeader) == header_size /* variable + variable */
) {
strwav->num_samples = read_32bitBE(0x20,streamHeader);
strwav->sample_rate = read_32bitBE(0x24,streamHeader);
strwav->flags = read_32bitBE(0x2c,streamHeader);
strwav->loop_start = read_32bitBE(0xd8,streamHeader);
strwav->loop_end = read_32bitBE(0xdc,streamHeader);
strwav->channels = read_32bitBE(0x70,streamHeader) * read_32bitBE(0x88,streamHeader); /* tracks of Nch */
strwav->loop_flag = strwav->flags & 0x01;
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000;
strwav->dsps_table = 0xe0;
strwav->codec = DSP;
;VGM_LOG("STR+WAV: header Bad Boys II (GC)\n");
return 1;
}
#if 0
if ( read_32bitBE(0x04,streamHeader) == 0x00000800 &&
read_32bitBE(0x24,streamHeader) == read_32bitBE(0xb0,streamHeader) && /* sample rate repeat */
read_32bitBE(0x24,streamHeader) == read_32bitBE(read_32bitBE(0xf0,streamHeader)+0x08,streamHeader) && /* sample rate vs 1st DSP header */
read_32bitBE(0x28,streamHeader) == 0x10 &&
read_32bitBE(0xc0,streamHeader)*0x04 + read_32bitBE(0xc4,streamHeader) == header_size /* variable + variable */
) {
strwav->num_samples = read_32bitBE(0x20,streamHeader);
strwav->sample_rate = read_32bitBE(0x24,streamHeader);
strwav->flags = read_32bitBE(0x2c,streamHeader);
strwav->loop_start = read_32bitBE(0xd8,streamHeader);
strwav->loop_end = read_32bitBE(0xdc,streamHeader);
strwav->channels = read_32bitBE(0x70,streamHeader) * read_32bitBE(0x88,streamHeader); /* tracks of Nch */
strwav->loop_flag = strwav->flags & 0x01;
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000;
strwav->dsps_table = 0xf0;
strwav->codec = DSP;
;VGM_LOG("STR+WAV: header Pac-Man World 3 (GC)\n");
return 1;
}
#endif
/* Bad Boys II (PS2)[2004] */
/* Pac-Man World 3 (PS2)[2005] */
if ((read_32bitBE(0x04,streamHeader) == 0x00000800 ||
read_32bitBE(0x04,streamHeader) == 0x01000800) && /* rare (PW3 mu_spectral1_explore_2) */
read_32bitLE(0x24,streamHeader) == read_32bitLE(0x70,streamHeader) && /* sample rate repeat */
read_32bitLE(0x28,streamHeader) == 0x10 &&
read_32bitLE(0x78,streamHeader)*0x04 + read_32bitLE(0x7c,streamHeader) == header_size /* ~0xe0 + variable */
) {
strwav->num_samples = read_32bitLE(0x20,streamHeader);
strwav->sample_rate = read_32bitLE(0x24,streamHeader);
strwav->flags = read_32bitLE(0x2c,streamHeader);
strwav->loop_start = read_32bitLE(0x38,streamHeader);
strwav->interleave = read_32bitLE(0x40,streamHeader) == 1 ? 0x8000 : 0x4000;
strwav->loop_end = read_32bitLE(0x54,streamHeader);
strwav->channels = read_32bitLE(0x40,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
strwav->loop_flag = strwav->flags & 0x01;
strwav->interleave = strwav->channels > 2 ? 0x4000 : 0x8000;
strwav->codec = PSX;
;VGM_LOG("STR+WAV: header Bad Boys II (PS2)\n");
return 1;
}
/* Zapper: One Wicked Cricket! (PS2)[2005] */
if ( read_32bitBE(0x04,streamHeader) == 0x00000900 &&
read_32bitLE(0x24,streamHeader) == read_32bitLE(0x70,streamHeader) && /* sample rate repeat */
read_32bitLE(0x28,streamHeader) == 0x10 &&
read_32bitLE(0x7c,streamHeader) == header_size /* ~0xD0 */
) {
strwav->num_samples = read_32bitLE(0x20,streamHeader);
strwav->sample_rate = read_32bitLE(0x24,streamHeader);
strwav->flags = read_32bitLE(0x2c,streamHeader);
strwav->loop_start = read_32bitLE(0x38,streamHeader);
strwav->loop_end = read_32bitLE(0x54,streamHeader);
strwav->channels = read_32bitLE(0x40,streamHeader) * (strwav->flags & 0x02 ? 2 : 1); /* tracks of 2/1ch */
strwav->loop_flag = strwav->flags & 0x01;
strwav->interleave = strwav->channels > 2 ? 0x4000 : 0x8000;
strwav->codec = PSX;
;VGM_LOG("STR+WAV: header Zapper (PS2)\n");
return 1;
}
/* Zapper: One Wicked Cricket! (GC)[2005] */
if ( read_32bitBE(0x04,streamHeader) == 0x00000900 &&
read_32bitBE(0x24,streamHeader) == read_32bitBE(0xB0,streamHeader) && /* sample rate repeat */
read_32bitBE(0x28,streamHeader) == 0x10 &&
read_32bitLE(0xc0,streamHeader) == header_size /* variable LE size */
) {
strwav->num_samples = read_32bitBE(0x20,streamHeader);
strwav->sample_rate = read_32bitBE(0x24,streamHeader);
strwav->flags = read_32bitBE(0x2c,streamHeader);
strwav->loop_start = read_32bitBE(0xd8,streamHeader);
strwav->loop_end = read_32bitBE(0xdc,streamHeader);
strwav->channels = read_32bitBE(0x70,streamHeader) * read_32bitBE(0x88,streamHeader); /* tracks of Nch */
strwav->loop_flag = strwav->flags & 0x01;
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000;
strwav->dsps_table = 0xe0;
strwav->codec = DSP;
;VGM_LOG("STR+WAV: header Zapper (GC)\n");
return 1;
}
/* Pac-Man World 3 (GC)[2005] */
/* SpongeBob SquarePants: Creature from the Krusty Krab (GC)[2006] */
if ( read_32bitBE(0x04,streamHeader) == 0x00000800 &&
read_32bitBE(0x24,streamHeader) == read_32bitBE(0xb0,streamHeader) && /* sample rate repeat */
read_32bitBE(0x24,streamHeader) == read_32bitBE(read_32bitBE(0xf0,streamHeader)+0x08,streamHeader) && /* sample rate vs 1st DSP header */
read_32bitBE(0x28,streamHeader) == 0x10 &&
read_32bitBE(0xc0,streamHeader)*0x04 + read_32bitBE(0xc4,streamHeader) == read_32bitBE(0xe0,streamHeader) && /* main size */
(read_32bitBE(0xe0,streamHeader) + read_32bitBE(0xe4,streamHeader)*0x40 == header_size || /* main size + extradata 1 (config? PMW3 cs2.wav) */
read_32bitBE(0xe0,streamHeader) + read_32bitBE(0xe4,streamHeader)*0x08 == header_size) /* main size + extradata 2 (ids? SBSP 0_0_mu_hr.wav) */
) {
strwav->num_samples = read_32bitBE(0x20,streamHeader);
strwav->sample_rate = read_32bitBE(0x24,streamHeader);
strwav->flags = read_32bitBE(0x2c,streamHeader);
strwav->loop_start = read_32bitBE(0xd8,streamHeader);
strwav->loop_end = read_32bitBE(0xdc,streamHeader);
strwav->channels = read_32bitBE(0x70,streamHeader) * read_32bitBE(0x88,streamHeader); /* tracks of Nch */
strwav->loop_flag = strwav->flags & 0x01;
strwav->interleave = strwav->channels > 2 ? 0x8000 : 0x10000;
strwav->dsps_table = 0xf0;
strwav->codec = DSP;
;VGM_LOG("STR+WAV: header SpongeBob SquarePants (GC)\n");
return 1;
}
/* SpongeBob SquarePants: Creature from the Krusty Krab (PS2)[2006] */
if ( read_32bitBE(0x04,streamHeader) == 0x00000800 &&
read_32bitLE(0x08,streamHeader) == 0x00000000 &&
read_32bitLE(0x0c,streamHeader) != header_size && /* some ID */
(header_size == 0x74 + read_32bitLE(0x64,streamHeader)*0x04 ||
header_size == 0x78 + read_32bitLE(0x64,streamHeader)*0x04)
) {
strwav->loop_start = read_32bitLE(0x24,streamHeader); //not ok?
strwav->num_samples = read_32bitLE(0x30,streamHeader);
strwav->loop_end = read_32bitLE(0x34,streamHeader);
strwav->sample_rate = read_32bitLE(0x38,streamHeader);
strwav->flags = read_32bitLE(0x3c,streamHeader);
strwav->channels = read_32bitLE(0x64,streamHeader); /* tracks of 1ch */
strwav->loop_flag = strwav->flags & 0x01;
strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000;
strwav->codec = PSX;
;VGM_LOG("STR+WAV: header SpongeBob SquarePants (PS2)\n");
return 1;
}
/* Tak and the Guardians of Gross (PS2)[2008] */
if ( read_32bitBE(0x04,streamHeader) == 0x00000800 &&
read_32bitLE(0x08,streamHeader) != 0x00000000 &&
read_32bitLE(0x0c,streamHeader) == header_size && /* ~0x80+0x04*ch */
read_32bitLE(0x0c,streamHeader) == 0x80 + read_32bitLE(0x70,streamHeader)*0x04
) {
strwav->loop_start = read_32bitLE(0x24,streamHeader); //not ok?
strwav->num_samples = read_32bitLE(0x30,streamHeader);
strwav->loop_end = read_32bitLE(0x34,streamHeader);
strwav->sample_rate = read_32bitLE(0x38,streamHeader);
strwav->flags = read_32bitLE(0x3c,streamHeader);
strwav->channels = read_32bitLE(0x70,streamHeader); /* tracks of 1ch */
strwav->loop_flag = strwav->flags & 0x01;
strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000;
strwav->codec = PSX;
;VGM_LOG("STR+WAV: header Tak (PS2)\n");
return 1;
}
/* Tak and the Guardians of Gross (Wii)[2008] */
/* The House of the Dead: Overkill (Wii)[2009] (not Blitz but still the same format) */
if ((read_32bitBE(0x04,streamHeader) == 0x00000800 ||
read_32bitBE(0x04,streamHeader) == 0x00000700) && /* rare? */
read_32bitLE(0x08,streamHeader) != 0x00000000 &&
read_32bitBE(0x0c,streamHeader) == header_size && /* variable per DSP header */
read_32bitBE(0x38,streamHeader) == read_32bitBE(read_32bitBE(0x7c,streamHeader)+0x38,streamHeader) /* sample rate vs 1st DSP header */
) {
strwav->loop_start = 0; //read_32bitLE(0x24,streamHeader); //not ok?
strwav->num_samples = read_32bitBE(0x30,streamHeader);
strwav->loop_end = read_32bitBE(0x34,streamHeader);
strwav->sample_rate = read_32bitBE(0x38,streamHeader);
strwav->flags = read_32bitBE(0x3c,streamHeader);
strwav->channels = read_32bitBE(0x70,streamHeader); /* tracks of 1ch */
strwav->loop_flag = strwav->flags & 0x01;
strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000;
strwav->coefs_table = 0x7c;
strwav->codec = DSP;
;VGM_LOG("STR+WAV: header Tak (Wii)\n");
return 1;
}
/* unknown */
goto fail;
fail:
return 0;
}

View File

@ -397,7 +397,7 @@ static STREAMFILE * open_txth(STREAMFILE * streamFile) {
/* try "(path/)(name.ext).txth" */
get_streamfile_name(streamFile,filename,PATH_LIMIT);
strcat(filename, ".txth");
streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
streamText = open_streamfile(streamFile,filename);
if (streamText) return streamText;
/* try "(path/)(.ext).txth" */
@ -406,13 +406,13 @@ static STREAMFILE * open_txth(STREAMFILE * streamFile) {
strcat(filename,".");
strcat(filename, fileext);
strcat(filename, ".txth");
streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
streamText = open_streamfile(streamFile,filename);
if (streamText) return streamText;
/* try "(path/).txth" */
get_streamfile_path(streamFile,filename,PATH_LIMIT);
strcat(filename, ".txth");
streamText = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
streamText = open_streamfile(streamFile,filename);
if (streamText) return streamText;
/* not found */

View File

@ -1,90 +0,0 @@
#include "meta.h"
#include "../util.h"
VGMSTREAM * init_vgmstream_wii_str(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * infileSTH = NULL;
char filename[PATH_LIMIT];
char * filenameSTH = NULL;
int i, j, channel_count, loop_flag;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("str",filename_extension(filename))) goto fail;
/* check for .MIH file */
filenameSTH=(char *)malloc(strlen(filename)+1);
if (!filenameSTH) goto fail;
strcpy(filenameSTH,filename);
strcpy(filenameSTH+strlen(filenameSTH)-3,"sth");
infileSTH = streamFile->open(streamFile,filenameSTH,STREAMFILE_DEFAULT_BUFFER_SIZE);
/* STH File is necessary, so we can't confuse those file */
/* with others .STR file as it is a very common extension */
if (!infileSTH) goto fail;
if(read_32bitLE(0x2C,infileSTH)!=0)
goto fail;
channel_count = read_32bitBE(0x70,infileSTH);
if(channel_count==1)
loop_flag = (read_32bitBE(0xD4,infileSTH)==0x00740000);
else
loop_flag = (read_32bitBE(0x124,infileSTH)==0x00740000);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitBE(0x38,infileSTH);
vgmstream->interleave_block_size=0x8000;
vgmstream->num_samples=read_32bitBE(0x34,infileSTH);
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->meta_type = meta_WII_STR;
if(loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
}
/* open the file for reading by each channel */
{
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,vgmstream->interleave_block_size);
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset+=(off_t)(vgmstream->interleave_block_size*i);
for(j=0; j<16; j++) {
vgmstream->ch[i].adpcm_coef[j]=read_16bitBE(0xAC+(j*2)+(i*0x50),infileSTH);
}
}
}
close_streamfile(infileSTH); infileSTH=NULL;
return vgmstream;
/* clean up anything we may have opened */
fail:
if (infileSTH) close_streamfile(infileSTH);
if (filenameSTH) {free(filenameSTH); filenameSTH=NULL;}
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

55
src/meta/wv6.c Normal file
View File

@ -0,0 +1,55 @@
#include "meta.h"
#include "../coding/coding.h"
/* WV6 - Gorilla Systems PC games [Spy Kids: Mega Mission Zone (PC), Lilo & Stitch: Hawaiian Adventure (PC)] */
VGMSTREAM * init_vgmstream_wv6(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count;
/* checks */
if (!check_extensions(streamFile, "wv6"))
goto fail;
if (read_32bitLE(0x00,streamFile) != get_streamfile_size(streamFile))
goto fail;
if (read_32bitBE(0x2c,streamFile) != 0x57563620 || /* "WV6 " */
read_32bitBE(0x30,streamFile) != 0x494D415F) /* "IMA_" ("WV6 IMA_ADPCM COMPRESSED 16 BIT AUDIO") */
goto fail;
/* 0x54/58/5c/60/6c: unknown (reject to catch possible stereo files, but don't seem to exist) */
if (read_32bitLE(0x54,streamFile) != 0x01 ||
read_32bitLE(0x58,streamFile) != 0x01 ||
read_32bitLE(0x5c,streamFile) != 0x10 ||
read_32bitLE(0x68,streamFile) != 0x01 ||
read_32bitLE(0x6c,streamFile) != 0x88)
goto fail;
/* 0x64: PCM size (samples*channels*2) */
channel_count = 1;
loop_flag = 0;
start_offset = 0x8c;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(0x60, streamFile);
vgmstream->num_samples = ima_bytes_to_samples(read_32bitLE(0x88,streamFile), channel_count);
vgmstream->meta_type = meta_WV6;
vgmstream->coding_type = coding_WV6_IMA;
vgmstream->layout_type = layout_none;
read_string(vgmstream->stream_name,0x1c+1, 0x04,streamFile);
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -143,19 +143,14 @@ static STREAMFILE *open_stdio(STDIOSTREAMFILE *streamFile,const char * const fil
}
static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char * const filename, size_t buffersize) {
uint8_t * buffer;
STDIOSTREAMFILE * streamfile;
uint8_t * buffer = NULL;
STDIOSTREAMFILE * streamfile = NULL;
buffer = calloc(buffersize,1);
if (!buffer) {
return NULL;
}
if (!buffer) goto fail;
streamfile = calloc(1,sizeof(STDIOSTREAMFILE));
if (!streamfile) {
free(buffer);
return NULL;
}
if (!streamfile) goto fail;
streamfile->sf.read = (void*)read_stdio;
streamfile->sf.get_size = (void*)get_size_stdio;
@ -175,7 +170,19 @@ static STREAMFILE * open_stdio_streamfile_buffer_by_file(FILE *infile,const char
fseeko(streamfile->infile,0,SEEK_END);
streamfile->filesize = ftello(streamfile->infile);
/* some compilers/flags may use ftell, which only handles up to ~2.14GB
* (possible for banks like FSB, though unlikely). */
if (streamfile->filesize == 0xFFFFFFFF) { /* -1 on error */
VGM_LOG("STREAMFILE: ftell error\n");
goto fail; /* can be ignored but may result in strange/unexpected behaviors */
}
return &streamfile->sf;
fail:
free(buffer);
free(streamfile);
return NULL;
}
static STREAMFILE * open_stdio_streamfile_buffer(const char * const filename, size_t buffersize) {
@ -765,6 +772,10 @@ fail:
/* **************************************************** */
STREAMFILE * open_streamfile(STREAMFILE *streamFile, const char * pathname) {
return streamFile->open(streamFile,pathname,STREAMFILE_DEFAULT_BUFFER_SIZE);
}
STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext) {
char filename_ext[PATH_LIMIT];
@ -1028,6 +1039,7 @@ int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, in
return 0;
}
/* copies name as-is (may include full path included) */
void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size) {
streamFile->get_name(streamFile,buffer,size);
}
@ -1053,6 +1065,18 @@ void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size)
strcpy(buffer, foldername);
}
}
/* copies the filename without path or extension */
void get_streamfile_basename(STREAMFILE *streamFile, char * buffer, size_t size) {
char *ext;
get_streamfile_filename(streamFile,buffer,size);
ext = strrchr(buffer,'.');
if (ext) {
ext[0] = '\0'; /* remove .ext from buffer */
}
}
/* copies path removing name (NULL when if filename has no path) */
void get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size) {
const char *path;

View File

@ -98,6 +98,10 @@ STREAMFILE *open_fakename_streamfile(STREAMFILE *streamfile, const char * fakena
* The first streamfile is used to get names, stream index and so on. */
STREAMFILE *open_multifile_streamfile(STREAMFILE **streamfiles, size_t streamfiles_size);
/* Opens a STREAMFILE from a (path)+filename.
* Just a wrapper, to avoid having to access the STREAMFILE's callbacks directly. */
STREAMFILE * open_streamfile(STREAMFILE *streamFile, const char * pathname);
/* Opens a STREAMFILE from a base pathname + new extension
* Can be used to get companion headers. */
STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext);
@ -197,6 +201,7 @@ int find_chunk(STREAMFILE *streamFile, uint32_t chunk_id, off_t start_offset, in
void get_streamfile_name(STREAMFILE *streamFile, char * buffer, size_t size);
void get_streamfile_filename(STREAMFILE *streamFile, char * buffer, size_t size);
void get_streamfile_basename(STREAMFILE *streamFile, char * buffer, size_t size);
void get_streamfile_path(STREAMFILE *streamFile, char * buffer, size_t size);
void get_streamfile_ext(STREAMFILE *streamFile, char * filename, size_t size);
#endif

View File

@ -52,7 +52,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_raw,
init_vgmstream_ps2_vag,
init_vgmstream_psx_gms,
init_vgmstream_ps2_str,
init_vgmstream_ps2_ild,
init_vgmstream_ps2_pnb,
init_vgmstream_xbox_wavm,
@ -212,7 +211,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_ps2_tk5,
init_vgmstream_ps2_vsf_tta,
init_vgmstream_ads,
init_vgmstream_wii_str,
init_vgmstream_ps2_mcg,
init_vgmstream_zsd,
init_vgmstream_ps2_vgs,
@ -266,9 +264,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_ngc_dsp_mpds,
init_vgmstream_dsp_str_ig,
init_vgmstream_ea_swvr,
init_vgmstream_ngc_dsp_sth_str1,
init_vgmstream_ngc_dsp_sth_str2,
init_vgmstream_ngc_dsp_sth_str3,
init_vgmstream_ps2_b1s,
init_vgmstream_ps2_wad,
init_vgmstream_dsp_xiii,
@ -421,10 +416,14 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_ps2_ads_container,
init_vgmstream_asf,
init_vgmstream_xmd,
init_vgmstream_cks,
init_vgmstream_ckb,
init_vgmstream_wv6,
init_vgmstream_str_wav,
init_vgmstream_txth, /* should go at the end (lower priority) */
#ifdef VGM_USE_FFMPEG
init_vgmstream_ffmpeg, /* should go at the end */
init_vgmstream_ffmpeg, /* should go at the end (lowest priority) */
#endif
};
@ -1060,6 +1059,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_IMA_int:
case coding_DVI_IMA_int:
case coding_3DS_IMA:
case coding_WV6_IMA:
return 2;
case coding_XBOX_IMA:
case coding_XBOX_IMA_mch:
@ -1100,7 +1100,9 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
return 128;
case coding_MSADPCM:
return (vgmstream->interleave_block_size-(7-1)*vgmstream->channels)*2/vgmstream->channels;
return (vgmstream->interleave_block_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2;
case coding_MSADPCM_ck:
return (vgmstream->interleave_block_size - 0x07)*2 + 2;
case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */
return vgmstream->ws_output_size;
case coding_AICA:
@ -1221,6 +1223,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
case coding_DVI_IMA:
case coding_DVI_IMA_int:
case coding_3DS_IMA:
case coding_WV6_IMA:
return 0x01;
case coding_MS_IMA:
case coding_RAD_IMA:
@ -1234,7 +1237,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
return 0x14;
case coding_SNDS_IMA:
case coding_OTNS_IMA:
return 0;
return 0; //todo: 0x01?
case coding_UBI_IMA: /* variable (PCM then IMA) */
return 0;
case coding_XBOX_IMA:
@ -1270,6 +1273,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
return 0x4c*vgmstream->channels;
case coding_MSADPCM:
case coding_MSADPCM_ck:
return vgmstream->interleave_block_size;
case coding_WS:
return vgmstream->current_block_size;
@ -1714,6 +1718,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
samples_to_do);
}
break;
case coding_WV6_IMA:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_wv6_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
vgmstream->channels,vgmstream->samples_into_block,
samples_to_do);
}
break;
case coding_APPLE_IMA4:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_apple_ima4(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
@ -1847,17 +1858,21 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
break;
case coding_MSADPCM:
if (vgmstream->channels == 2) {
decode_msadpcm_stereo(vgmstream,
buffer+samples_written*vgmstream->channels,
decode_msadpcm_stereo(vgmstream,buffer+samples_written*vgmstream->channels,
vgmstream->samples_into_block,
samples_to_do);
}
else if (vgmstream->channels == 1)
{
decode_msadpcm_mono(vgmstream,
buffer+samples_written*vgmstream->channels,
vgmstream->samples_into_block,
samples_to_do);
else if (vgmstream->channels == 1) {
decode_msadpcm_mono(vgmstream,buffer+samples_written*vgmstream->channels,
vgmstream->channels,vgmstream->samples_into_block,
samples_to_do,0);
}
break;
case coding_MSADPCM_ck:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_msadpcm_ck(vgmstream,buffer+samples_written*vgmstream->channels+chan,
vgmstream->channels,vgmstream->samples_into_block,
samples_to_do, chan);
}
break;
case coding_AICA:
@ -2225,6 +2240,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
if (vgmstream->layout_type == layout_none && vgmstream->interleave_block_size > 0) {
switch (vgmstream->coding_type) {
case coding_MSADPCM:
case coding_MSADPCM_ck:
case coding_MS_IMA:
case coding_MC3:
case coding_WWISE_IMA:

View File

@ -124,6 +124,10 @@ typedef enum {
coding_DVI_IMA, /* DVI IMA ADPCM (stereo or mono, high nibble first) */
coding_DVI_IMA_int, /* DVI IMA ADPCM (mono/interleave, high nibble first) */
coding_3DS_IMA, /* 3DS IMA ADPCM */
coding_SNDS_IMA, /* Heavy Iron Studios .snds IMA ADPCM */
coding_OTNS_IMA, /* Omikron The Nomad Soul IMA ADPCM */
coding_WV6_IMA, /* Gorilla Systems WV6 4-bit IMA ADPCM */
coding_MS_IMA, /* Microsoft IMA ADPCM */
coding_XBOX_IMA, /* XBOX IMA ADPCM */
coding_XBOX_IMA_mch, /* XBOX IMA ADPCM (multichannel) */
@ -133,15 +137,14 @@ typedef enum {
coding_RAD_IMA, /* Radical IMA ADPCM */
coding_RAD_IMA_mono, /* Radical IMA ADPCM (mono/interleave) */
coding_APPLE_IMA4, /* Apple Quicktime IMA4 */
coding_SNDS_IMA, /* Heavy Iron Studios .snds IMA ADPCM */
coding_OTNS_IMA, /* Omikron The Nomad Soul IMA ADPCM */
coding_FSB_IMA, /* FMOD's FSB multichannel IMA ADPCM */
coding_WWISE_IMA, /* Audiokinetic Wwise IMA ADPCM */
coding_REF_IMA, /* Reflections IMA ADPCM */
coding_AWC_IMA, /* Rockstar AWC IMA ADPCM */
coding_UBI_IMA, /* Ubisoft IMA ADPCM */
coding_MSADPCM, /* Microsoft ADPCM */
coding_MSADPCM, /* Microsoft ADPCM (stereo/mono) */
coding_MSADPCM_ck, /* Microsoft ADPCM (Cricket Audio variation) */
coding_WS, /* Westwood Studios VBR ADPCM */
coding_AICA, /* Yamaha AICA ADPCM (stereo) */
coding_AICA_int, /* Yamaha AICA ADPCM (mono/interleave) */
@ -338,7 +341,7 @@ typedef enum {
meta_PS2_VAGm, /* VAGp Mono File */
meta_PS2_pGAV, /* VAGp with Little Endian Header */
meta_PSX_GMS, /* GMS File (used in PS1 & PS2) [no header_id] */
meta_PS2_STR, /* Pacman STR+STH files */
meta_STR_WAV, /* Blitz Games STR+WAV files */
meta_PS2_ILD, /* ILD File */
meta_PS2_PNB, /* PsychoNauts Bgm File */
meta_PS2_VAGs, /* VAG Stereo from Kingdom Hearts */
@ -502,7 +505,6 @@ typedef enum {
meta_PS2_GBTS, /* Pop'n'Music 9 Audio File */
meta_NGC_DSP_IADP, /* Gamecube Interleave DSP */
meta_PS2_TK5, /* Tekken 5 Stream Files */
meta_WII_STR, /* House of The Dead Overkill STR+STH */
meta_PS2_MCG, /* Gunvari MCG Files (was name .GCM on disk) */
meta_ZSD, /* Dragon Booster ZSD */
meta_RedSpark, /* "RedSpark" RSD (MadWorld) */
@ -542,7 +544,6 @@ typedef enum {
meta_NGC_DSP_MPDS, /* Big Air Freestyle, Terminator 3 */
meta_DSP_STR_IG, /* Micro Machines, Superman Superman: Shadow of Apokolis */
meta_EA_SWVR, /* Future Cop L.A.P.D., Freekstyle */
meta_NGC_DSP_STH_STR, /* SpongeBob Squarepants (NGC), Taz Wanted (NGC), Cubix (NGC), Tak (WII)*/
meta_PS2_B1S, /* 7 Wonders of the ancient world */
meta_PS2_WAD, /* The golden Compass */
meta_DSP_XIII, /* XIII, possibly more (Ubisoft header???) */
@ -685,6 +686,9 @@ typedef enum {
meta_OGG_MUS, /* Ogg Vorbis with encryption [Redux - Dark Matters (PC)] */
meta_ASF, /* Argonaut ASF [Croc 2 (PC)] */
meta_XMD, /* Konami XMD [Silent Hill 4 (Xbox), Castlevania: Curse of Darkness (Xbox)] */
meta_CKS, /* Cricket Audio stream [Part Time UFO (Android), Mega Man 1-6 (Android)] */
meta_CKB, /* Cricket Audio bank [Fire Emblem Heroes (Android), Mega Man 1-6 (Android)] */
meta_WV6, /* Gorilla Systems PC games */
#ifdef VGM_USE_FFMPEG
meta_FFmpeg,