Merge pull request #71 from bnnm/xwb

XWB support
This commit is contained in:
Christopher Snowhill 2017-02-28 19:12:44 -08:00 committed by GitHub
commit f2b0a96afc
13 changed files with 966 additions and 488 deletions

View File

@ -25,7 +25,8 @@ CODING_OBJS=coding/adx_decoder.o \
coding/at3_decoder.o \ coding/at3_decoder.o \
coding/g719_decoder.o \ coding/g719_decoder.o \
coding/hca_decoder.o \ coding/hca_decoder.o \
coding/ffmpeg_decoder.o coding/ffmpeg_decoder.o \
coding/ffmpeg_decoder_utils.o
LAYOUT_OBJS=layout/ast_blocked.o \ LAYOUT_OBJS=layout/ast_blocked.o \
layout/blocked.o \ layout/blocked.o \

View File

@ -154,17 +154,44 @@ void decode_mp4_aac(mp4_aac_codec_data * data, sample * outbuf, int32_t samples_
void decode_at3plus(VGMSTREAM *vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel); void decode_at3plus(VGMSTREAM *vgmstream, sample * outbuf, int channelspacing, int32_t samples_to_do, int channel);
#endif #endif
/* ffmpeg_decoder */
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
/* ffmpeg_decoder */
void decode_ffmpeg(VGMSTREAM *stream, sample * outbuf, int32_t samples_to_do, int channels); void decode_ffmpeg(VGMSTREAM *stream, sample * outbuf, int32_t samples_to_do, int channels);
void reset_ffmpeg(VGMSTREAM *vgmstream); void reset_ffmpeg(VGMSTREAM *vgmstream);
void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample); void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample);
void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples); void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples);
/* ffmpeg_decoder_utils */
int ffmpeg_fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec);
int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay); int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay);
int ffmpeg_make_riff_xma1(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int stream_mode);
int ffmpeg_make_riff_xma2(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size); int ffmpeg_make_riff_xma2(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size);
int ffmpeg_make_riff_xma2_from_fmt(uint8_t * buf, size_t buf_size, off_t fmt_offset, size_t fmt_size, size_t data_size, STREAMFILE *streamFile, int big_endian); int ffmpeg_make_riff_xma2_from_fmt(uint8_t * buf, size_t buf_size, off_t fmt_offset, size_t fmt_size, size_t data_size, STREAMFILE *streamFile, int big_endian);
int ffmpeg_make_riff_xwma(uint8_t * buf, size_t buf_size, int codec, size_t sample_count, size_t data_size, int channels, int sample_rate, int avg_bps, int block_align);
/* XMA sample parser info (struct to avoid passing so much stuff, separate for reusing) */
typedef struct {
int xma_version;
int channels;
int stream_mode;
off_t data_offset;
size_t data_size;
int loop_flag;
/* frame offsets */
uint32_t loop_start_b;
uint32_t loop_end_b;
uint32_t loop_start_subframe;
uint32_t loop_end_subframe;
/* output */
int32_t num_samples;
int32_t skip_samples;
int32_t loop_start_sample;
int32_t loop_end_sample;
} xma_sample_data;
void xma_get_samples(xma_sample_data * xma, STREAMFILE *streamFile);
#endif #endif

View File

@ -324,207 +324,4 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples) {
data->skipSamples = skip_samples; data->skipSamples = skip_samples;
} }
/* ******************************************** */
/* FAKE RIFF HELPERS */
/* ******************************************** */
static int ffmpeg_fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec);
/**
* Copies a ATRAC3 riff to buf
*
* returns number of bytes in buf or -1 when buf is not big enough
*/
int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay) {
uint16_t codec_ATRAC3 = 0x0270;
size_t riff_size = 4+4+ 4 + 0x28 + 0x10 + 4+4;
if (buf_size < riff_size)
return -1;
memcpy(buf+0x00, "RIFF", 4);
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
memcpy(buf+0x08, "WAVE", 4);
memcpy(buf+0x0c, "fmt ", 4);
put_32bitLE(buf+0x10, 0x20);/*fmt size*/
put_16bitLE(buf+0x14, codec_ATRAC3);
put_16bitLE(buf+0x16, channels);
put_32bitLE(buf+0x18, sample_rate);
put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong) */
put_32bitLE(buf+0x20, (int16_t)(block_align)); /* block align */
put_16bitLE(buf+0x24, 0x0e); /* extra data size */
put_16bitLE(buf+0x26, 1); /* unknown, always 1 */
put_16bitLE(buf+0x28, 0x0800 * channels); /* unknown (some size? 0x1000=2ch, 0x0800=1ch) */
put_16bitLE(buf+0x2a, 0); /* unknown, always 0 */
put_16bitLE(buf+0x2c, joint_stereo ? 0x0001 : 0x0000);
put_16bitLE(buf+0x2e, joint_stereo ? 0x0001 : 0x0000); /* repeated? */
put_16bitLE(buf+0x30, 1); /* unknown, always 1 (frame_factor?) */
put_16bitLE(buf+0x32, 0); /* unknown, always 0 */
memcpy(buf+0x34, "fact", 4);
put_32bitLE(buf+0x38, 0x8); /* fact size */
put_32bitLE(buf+0x3c, sample_count);
put_32bitLE(buf+0x40, encoder_delay);
memcpy(buf+0x44, "data", 4);
put_32bitLE(buf+0x48, data_size); /* data size */
return riff_size;
}
/**
* Copies a XMA2 riff to buf
*
* returns number of bytes in buf or -1 when buf is not big enough
*/
int ffmpeg_make_riff_xma2(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size) {
uint16_t codec_XMA2 = 0x0166;
size_t riff_size = 4+4+ 4 + 0x3c + 4+4;
size_t bytecount;
uint32_t streams = 0;
uint32_t speakers = 0; /* see audiodefs.h */
if (buf_size < riff_size)
return -1;
bytecount = sample_count * channels * sizeof(sample);
/* todo untested */
switch (channels) {
case 1:
streams = 1;
speakers = 0x00000004; /* FC */
break;
case 2:
streams = 1;
speakers = 0x00000001 | 0x00000002; /* FL FR */
break;
case 3:
streams = 3;
speakers = 0x00000001 | 0x00000002 | 0x00000004; /* FL FC FR */
break;
case 4:
streams = 2;
speakers = 0x00000001 | 0x00000002 | 0x00000010 | 0x00000020; /* FL FR BL BR */
break;
case 5:
streams = 3;
speakers = 0x00000001 | 0x00000002 | 0x00000010 | 0x00000020 | 0x00000004; /* FL C FR BL BR*/
break;
case 6:
streams = 3;
speakers = 0x00000001 | 0x00000002 | 0x00000010 | 0x00000020 | 0x00000200 | 0x00000400; /* FL FR BL BR SL SR */
break;
default:
streams = 1;
speakers = 0x80000000;
break;
}
/*memset(buf,0, sizeof(uint8_t) * fmt_size);*/
memcpy(buf+0x00, "RIFF", 4);
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
memcpy(buf+0x08, "WAVE", 4);
memcpy(buf+0x0c, "fmt ", 4);
put_32bitLE(buf+0x10, 0x34);/*fmt size*/
put_16bitLE(buf+0x14, codec_XMA2);
put_16bitLE(buf+0x16, channels);
put_32bitLE(buf+0x18, sample_rate);
put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong) */
put_16bitLE(buf+0x20, (int16_t)(channels*sizeof(sample))); /* block align */
put_16bitLE(buf+0x22, sizeof(sample)*8); /* bits per sample */
put_16bitLE(buf+0x24, 0x22); /* extra data size */
put_16bitLE(buf+0x26, streams); /* number of streams */
put_32bitLE(buf+0x28, speakers); /* speaker position */
put_32bitLE(buf+0x2c, bytecount); /* PCM samples */
put_32bitLE(buf+0x30, block_size); /* XMA block size */
/* (looping values not set, expected to be handled externally) */
put_32bitLE(buf+0x34, 0); /* play begin */
put_32bitLE(buf+0x38, 0); /* play length */
put_32bitLE(buf+0x3c, 0); /* loop begin */
put_32bitLE(buf+0x40, 0); /* loop length */
put_8bit(buf+0x44, 0); /* loop count */
put_8bit(buf+0x45, 4); /* encoder version */
put_16bitLE(buf+0x46, block_count); /* blocks count = entried in seek table */
memcpy(buf+0x48, "data", 4);
put_32bitLE(buf+0x4c, data_size); /* data size */
return riff_size;
}
/**
* Copies a XMA2 riff to buf from a fmt chunk offset
*
* returns number of bytes in buf or -1 when buf is not big enough
*/
int ffmpeg_make_riff_xma2_from_fmt(uint8_t * buf, size_t buf_size, off_t fmt_offset, size_t fmt_size, size_t data_size, STREAMFILE *streamFile, int big_endian) {
size_t riff_size = 4+4+ 4 + 4+4+fmt_size + 4+4;
uint8_t chunk[100];
if (buf_size < riff_size || fmt_size > 100)
goto fail;
if (read_streamfile(chunk,fmt_offset,fmt_size, streamFile) != fmt_size)
goto fail;
if (big_endian)
ffmpeg_fmt_chunk_swap_endian(chunk, 0x166);
memcpy(buf+0x00, "RIFF", 4);
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
memcpy(buf+0x08, "WAVE", 4);
memcpy(buf+0x0c, "fmt ", 4);
put_32bitLE(buf+0x10, fmt_size);/*fmt size*/
memcpy(buf+0x14, chunk, fmt_size);
memcpy(buf+0x14+fmt_size, "data", 4);
put_32bitLE(buf+0x14+fmt_size+4, data_size); /* data size */
return riff_size;
fail:
return -1;
}
/**
* Swaps endianness
*
* returns 0 on error
*/
static int ffmpeg_fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec) {
if (codec != 0x166)
goto fail;
put_16bitLE(chunk + 0x00, get_16bitBE(chunk + 0x00));/*wFormatTag*/
put_16bitLE(chunk + 0x02, get_16bitBE(chunk + 0x02));/*nChannels*/
put_32bitLE(chunk + 0x04, get_32bitBE(chunk + 0x04));/*nSamplesPerSec*/
put_32bitLE(chunk + 0x08, get_32bitBE(chunk + 0x08));/*nAvgBytesPerSec*/
put_16bitLE(chunk + 0x0c, get_16bitBE(chunk + 0x0c));/*nBlockAlign*/
put_16bitLE(chunk + 0x0e, get_16bitBE(chunk + 0x0e));/*wBitsPerSample*/
put_16bitLE(chunk + 0x10, get_16bitBE(chunk + 0x10));/*cbSize*/
put_16bitLE(chunk + 0x12, get_16bitBE(chunk + 0x12));/*NumStreams*/
put_32bitLE(chunk + 0x14, get_32bitBE(chunk + 0x14));/*ChannelMask*/
put_32bitLE(chunk + 0x18, get_32bitBE(chunk + 0x18));/*SamplesEncoded*/
put_32bitLE(chunk + 0x1c, get_32bitBE(chunk + 0x1c));/*BytesPerBlock*/
put_32bitLE(chunk + 0x20, get_32bitBE(chunk + 0x20));/*PlayBegin*/
put_32bitLE(chunk + 0x24, get_32bitBE(chunk + 0x24));/*PlayLength*/
put_32bitLE(chunk + 0x28, get_32bitBE(chunk + 0x28));/*LoopBegin*/
put_32bitLE(chunk + 0x2c, get_32bitBE(chunk + 0x2c));/*LoopLength*/
/* put_8bit(chunk + 0x30, get_8bit(chunk + 0x30));*//*LoopCount*/
/* put_8bit(chunk + 0x31, get_8bit(chunk + 0x31));*//*EncoderVersion*/
put_16bitLE(chunk + 0x32, get_16bitBE(chunk + 0x32));/*BlockCount*/
return 1;
fail:
return 0;
}
#endif #endif

View File

@ -0,0 +1,497 @@
#include "coding.h"
#include "../vgmstream.h"
#ifdef VGM_USE_FFMPEG
#define XMA_CHECK_SKIPS 0
#define XMA_BYTES_PER_PACKET 2048
#define XMA_SAMPLES_PER_FRAME 512
#define XMA_SAMPLES_PER_SUBFRAME 128
/* ******************************************** */
/* INTERNAL UTILS */
/* ******************************************** */
/**
* read num_bits (up to 25) from a bit offset.
* 25 since we read a 32 bit int, and need to adjust up to 7 bits from the byte-rounded fseek (32-7=25)
*/
static uint32_t read_bitsBE_b(off_t bit_offset, int num_bits, STREAMFILE *streamFile) {
uint32_t num, mask;
if (num_bits > 25) return -1; //???
num = read_32bitBE(bit_offset / 8, streamFile); /* fseek rounded to 8 */
num = num << (bit_offset % 8); /* offset adjust (up to 7) */
num = num >> (32 - num_bits);
mask = 0xffffffff >> (32 - num_bits);
return num & mask;
}
/* ******************************************** */
/* FAKE RIFF HELPERS */
/* ******************************************** */
/* All helpers copy a RIFF header to buf and returns the number of bytes in buf or -1 when buf is not big enough */
int ffmpeg_make_riff_atrac3(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_align, int joint_stereo, int encoder_delay) {
uint16_t codec_ATRAC3 = 0x0270;
size_t riff_size = 4+4+ 4 + 0x28 + 0x10 + 4+4;
if (buf_size < riff_size)
return -1;
memcpy(buf+0x00, "RIFF", 4);
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
memcpy(buf+0x08, "WAVE", 4);
memcpy(buf+0x0c, "fmt ", 4);
put_32bitLE(buf+0x10, 0x20);/*fmt size*/
put_16bitLE(buf+0x14, codec_ATRAC3);
put_16bitLE(buf+0x16, channels);
put_32bitLE(buf+0x18, sample_rate);
put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong) */
put_32bitLE(buf+0x20, (int16_t)(block_align)); /* block align */
put_16bitLE(buf+0x24, 0x0e); /* extra data size */
put_16bitLE(buf+0x26, 1); /* unknown, always 1 */
put_16bitLE(buf+0x28, 0x0800 * channels); /* unknown (some size? 0x1000=2ch, 0x0800=1ch) */
put_16bitLE(buf+0x2a, 0); /* unknown, always 0 */
put_16bitLE(buf+0x2c, joint_stereo ? 0x0001 : 0x0000);
put_16bitLE(buf+0x2e, joint_stereo ? 0x0001 : 0x0000); /* repeated? */
put_16bitLE(buf+0x30, 1); /* unknown, always 1 (frame_factor?) */
put_16bitLE(buf+0x32, 0); /* unknown, always 0 */
memcpy(buf+0x34, "fact", 4);
put_32bitLE(buf+0x38, 0x8); /* fact size */
put_32bitLE(buf+0x3c, sample_count);
put_32bitLE(buf+0x40, encoder_delay);
memcpy(buf+0x44, "data", 4);
put_32bitLE(buf+0x48, data_size); /* data size */
return riff_size;
}
int ffmpeg_make_riff_xma1(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int stream_mode) {
uint16_t codec_XMA1 = 0x0165;
size_t riff_size;
int streams, i;
/* stream disposition:
* 0: default (ex. 5ch = 2ch + 2ch + 1ch = 3 streams)
* 1: lineal (ex. 5ch = 1ch + 1ch + 1ch + 1ch + 1ch = 5 streams), unusual but exists
* others: not seen (ex. maybe 5ch = 2ch + 1ch + 1ch + 1ch = 4 streams) */
switch(stream_mode) {
case 0 : streams = (channels + 1) / 2; break;
case 1 : streams = channels; break;
default: return 0;
}
riff_size = 4+4+ 4 + 0x14 + 0x14*streams + 4+4;
if (buf_size < riff_size)
return -1;
memcpy(buf+0x00, "RIFF", 4);
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
memcpy(buf+0x08, "WAVE", 4);
memcpy(buf+0x0c, "fmt ", 4);
put_32bitLE(buf+0x10, 0xc + 0x14*streams);/*fmt size*/
put_16bitLE(buf+0x14, codec_XMA1);
put_16bitLE(buf+0x16, 16); /* bits per sample */
put_16bitLE(buf+0x18, 0x10D6); /* encoder options */
put_16bitLE(buf+0x1a, 0); /* largest stream skip (wrong, unneeded) */
put_16bitLE(buf+0x1c, streams); /* number of streams */
put_8bit (buf+0x1e, 0); /* loop count */
put_8bit (buf+0x1f, 2); /* version */
for (i = 0; i < streams; i++) {
int stream_channels;
uint32_t speakers;
off_t off = 0x20 + 0x14*i;/* stream riff offset */
if (stream_mode == 1) {
/* lineal */
stream_channels = 1;
switch(i) { /* per stream, values observed */
case 0: speakers = 0x0001; break;/* L */
case 1: speakers = 0x0002; break;/* R */
case 2: speakers = 0x0004; break;/* C */
case 3: speakers = 0x0008; break;/* LFE */
case 4: speakers = 0x0040; break;/* LB */
case 5: speakers = 0x0080; break;/* RB */
case 6: speakers = 0x0000; break;/* ? */
case 7: speakers = 0x0000; break;/* ? */
default: speakers = 0;
}
}
else {
/* with odd channels the last stream is mono */
stream_channels = channels / streams + (channels%2 != 0 && i+1 != streams ? 1 : 0);
switch(i) { /* per stream, values from xmaencode */
case 0: speakers = stream_channels == 1 ? 0x0001 : 0x0201; break;/* L R */
case 1: speakers = stream_channels == 1 ? 0x0004 : 0x0804; break;/* C LFE */
case 2: speakers = stream_channels == 1 ? 0x0040 : 0x8040; break;/* LB RB */
case 3: speakers = stream_channels == 1 ? 0x0000 : 0x0000; break;/* somehow empty (maybe should use 0x2010 LS RS) */
default: speakers = 0;
}
}
put_32bitLE(buf+off+0x00, sample_rate*stream_channels / sizeof(sample)); /* average bytes per second (wrong, unneeded) */
put_32bitLE(buf+off+0x04, sample_rate);
put_32bitLE(buf+off+0x08, 0); /* loop start */
put_32bitLE(buf+off+0x0c, 0); /* loop end */
put_8bit (buf+off+0x10, 0); /* loop subframe */
put_8bit (buf+off+0x11, channels);
put_16bitLE(buf+off+0x12, speakers);
}
memcpy(buf+riff_size-4-4, "data", 4);
put_32bitLE(buf+riff_size-4, data_size); /* data size */
return riff_size;
}
int ffmpeg_make_riff_xma2(uint8_t * buf, size_t buf_size, size_t sample_count, size_t data_size, int channels, int sample_rate, int block_count, int block_size) {
uint16_t codec_XMA2 = 0x0166;
size_t riff_size = 4+4+ 4 + 0x3c + 4+4;
size_t bytecount;
int streams;
uint32_t speakers;
/* info from xma2defs.h, xact3wb.h and audiodefs.h */
streams = (channels + 1) / 2;
switch (channels) {
case 1: speakers = 0x04; break; /* 1.0: FC */
case 2: speakers = 0x01 | 0x02; break; /* 2.0: FL FR */
case 3: speakers = 0x01 | 0x02 | 0x08; break; /* 2.1: FL FR LF */
case 4: speakers = 0x01 | 0x02 | 0x10 | 0x20; break; /* 4.0: FL FR BL BR */
case 5: speakers = 0x01 | 0x02 | 0x08 | 0x10 | 0x20; break; /* 4.1: FL FR LF BL BR */
case 6: speakers = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20; break; /* 5.1: FL FR FC LF BL BR */
case 7: speakers = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x0100; break; /* 6.1: FL FR FC LF BL BR BC */
case 8: speakers = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40 | 0x80; break; /* 7.1: FL FR FC LF BL BR FLC FRC */
default: speakers = 0; break;
}
if (buf_size < riff_size)
return -1;
bytecount = sample_count * channels * sizeof(sample);
memcpy(buf+0x00, "RIFF", 4);
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
memcpy(buf+0x08, "WAVE", 4);
memcpy(buf+0x0c, "fmt ", 4);
put_32bitLE(buf+0x10, 0x34);/*fmt size*/
put_16bitLE(buf+0x14, codec_XMA2);
put_16bitLE(buf+0x16, channels);
put_32bitLE(buf+0x18, sample_rate);
put_32bitLE(buf+0x1c, sample_rate*channels / sizeof(sample)); /* average bytes per second (wrong unneeded) */
put_16bitLE(buf+0x20, (int16_t)(channels*sizeof(sample))); /* block align */
put_16bitLE(buf+0x22, 16); /* bits per sample */
put_16bitLE(buf+0x24, 0x22); /* extra data size */
put_16bitLE(buf+0x26, streams); /* number of streams */
put_32bitLE(buf+0x28, speakers); /* speaker position */
put_32bitLE(buf+0x2c, bytecount); /* PCM samples */
put_32bitLE(buf+0x30, block_size); /* XMA block size */
/* (looping values not set, expected to be handled externally) */
put_32bitLE(buf+0x34, 0); /* play begin */
put_32bitLE(buf+0x38, 0); /* play length */
put_32bitLE(buf+0x3c, 0); /* loop begin */
put_32bitLE(buf+0x40, 0); /* loop length */
put_8bit(buf+0x44, 0); /* loop count */
put_8bit(buf+0x45, 4); /* encoder version */
put_16bitLE(buf+0x46, block_count); /* blocks count = entries in seek table */
memcpy(buf+0x48, "data", 4);
put_32bitLE(buf+0x4c, data_size); /* data size */
return riff_size;
}
int ffmpeg_make_riff_xma2_from_fmt(uint8_t * buf, size_t buf_size, off_t fmt_offset, size_t fmt_size, size_t data_size, STREAMFILE *streamFile, int big_endian) {
size_t riff_size = 4+4+ 4 + 4+4+fmt_size + 4+4;
uint8_t chunk[100];
if (buf_size < riff_size || fmt_size > 100)
goto fail;
if (read_streamfile(chunk,fmt_offset,fmt_size, streamFile) != fmt_size)
goto fail;
if (big_endian)
ffmpeg_fmt_chunk_swap_endian(chunk, 0x166);
memcpy(buf+0x00, "RIFF", 4);
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
memcpy(buf+0x08, "WAVE", 4);
memcpy(buf+0x0c, "fmt ", 4);
put_32bitLE(buf+0x10, fmt_size);/*fmt size*/
memcpy(buf+0x14, chunk, fmt_size);
memcpy(buf+0x14+fmt_size, "data", 4);
put_32bitLE(buf+0x14+fmt_size+4, data_size); /* data size */
return riff_size;
fail:
return -1;
}
int ffmpeg_make_riff_xwma(uint8_t * buf, size_t buf_size, int codec, size_t sample_count, size_t data_size, int channels, int sample_rate, int avg_bps, int block_align) {
size_t riff_size = 4+4+ 4 + 0x1a + 4+4;
if (buf_size < riff_size)
return -1;
memcpy(buf+0x00, "RIFF", 4);
put_32bitLE(buf+0x04, (int32_t)(riff_size-4-4 + data_size)); /* riff size */
memcpy(buf+0x08, "XWMA", 4);
memcpy(buf+0x0c, "fmt ", 4);
put_32bitLE(buf+0x10, 0x12);/*fmt size*/
put_16bitLE(buf+0x14, codec);
put_16bitLE(buf+0x16, channels);
put_32bitLE(buf+0x18, sample_rate);
put_32bitLE(buf+0x1c, avg_bps); /* average bits per second, somehow vital for XWMA */
put_16bitLE(buf+0x20, block_align); /* block align */
put_16bitLE(buf+0x22, 16); /* bits per sample */
put_16bitLE(buf+0x24, 0); /* unk */
/* here goes the "dpds" table, but it's not needed by FFmpeg */
memcpy(buf+0x26, "data", 4);
put_32bitLE(buf+0x2a, data_size); /* data size */
return riff_size;
}
int ffmpeg_fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec) {
if (codec != 0x166)/* XMA2 */
goto fail;
put_16bitLE(chunk + 0x00, get_16bitBE(chunk + 0x00));/*wFormatTag*/
put_16bitLE(chunk + 0x02, get_16bitBE(chunk + 0x02));/*nChannels*/
put_32bitLE(chunk + 0x04, get_32bitBE(chunk + 0x04));/*nSamplesPerSec*/
put_32bitLE(chunk + 0x08, get_32bitBE(chunk + 0x08));/*nAvgBytesPerSec*/
put_16bitLE(chunk + 0x0c, get_16bitBE(chunk + 0x0c));/*nBlockAlign*/
put_16bitLE(chunk + 0x0e, get_16bitBE(chunk + 0x0e));/*wBitsPerSample*/
put_16bitLE(chunk + 0x10, get_16bitBE(chunk + 0x10));/*cbSize*/
put_16bitLE(chunk + 0x12, get_16bitBE(chunk + 0x12));/*NumStreams*/
put_32bitLE(chunk + 0x14, get_32bitBE(chunk + 0x14));/*ChannelMask*/
put_32bitLE(chunk + 0x18, get_32bitBE(chunk + 0x18));/*SamplesEncoded*/
put_32bitLE(chunk + 0x1c, get_32bitBE(chunk + 0x1c));/*BytesPerBlock*/
put_32bitLE(chunk + 0x20, get_32bitBE(chunk + 0x20));/*PlayBegin*/
put_32bitLE(chunk + 0x24, get_32bitBE(chunk + 0x24));/*PlayLength*/
put_32bitLE(chunk + 0x28, get_32bitBE(chunk + 0x28));/*LoopBegin*/
put_32bitLE(chunk + 0x2c, get_32bitBE(chunk + 0x2c));/*LoopLength*/
/* put_8bit(chunk + 0x30, get_8bit(chunk + 0x30));*//*LoopCount*/
/* put_8bit(chunk + 0x31, get_8bit(chunk + 0x31));*//*EncoderVersion*/
put_16bitLE(chunk + 0x32, get_16bitBE(chunk + 0x32));/*BlockCount*/
return 1;
fail:
return 0;
}
/* ******************************************** */
/* XMA PARSING */
/* ******************************************** */
/**
* Find total and loop samples by reading XMA frame headers.
*
* A XMA stream is made of packets, each containing N small frames of X samples.
* Frames are further divided into subframes for looping purposes.
* XMA1 and XMA2 only differ in the packet headers.
*/
void xma_get_samples(xma_sample_data * xma, STREAMFILE *streamFile) {
int frames = 0, samples = 0, loop_start_frame = 0, loop_end_frame = 0, skip_packets;
#if XMA_CHECK_SKIPS
int start_skip = 0, end_skip = 0, first_start_skip = 0, last_end_skip = 0;
#endif
uint32_t first_frame_b, packet_skip_count = 0, frame_size_b, packet_size_b;
uint64_t offset_b, packet_offset_b, frame_offset_b;
size_t size;
uint32_t packet_size = XMA_BYTES_PER_PACKET;
off_t offset = xma->data_offset;
uint32_t stream_offset_b = xma->data_offset * 8;
size = offset + xma->data_size;
packet_size_b = packet_size * 8;
/* if we knew the streams mode then we could read just the first one and adjust samples later
* not a big deal but maybe important for skip stuff */
//streams = (xma->stream_mode==0 ? (xma->channels + 1) / 2 : xma->channels)
skip_packets = 0;
/* read packets */
while (offset < size) {
offset_b = offset * 8; /* global offset in bits */
offset += packet_size; /* global offset in bytes */
/* skip packets not owned by the first stream, since we only need samples from it */
if (skip_packets && packet_skip_count) {
packet_skip_count--;
continue;
}
/* XMA1 or XMA2 packet header */
if (xma->xma_version == 1) {
//packet_sequence = read_bitsBE_b(offset_b+0, 4, streamFile); /* numbered from 0 to N */
//unknown = read_bitsBE_b(offset_b+4, 2, streamFile); /* packet_metadata? (always 2) */
first_frame_b = read_bitsBE_b(offset_b+6, 15, streamFile); /* offset in bits inside the packet */
packet_skip_count = read_bitsBE_b(offset_b+21, 11, streamFile); /* packets to skip for next packet of this stream */
} else {
//frame_count = read_bitsBE_b(offset_b+0, 6, streamFile); /* frames that begin in this packet */
first_frame_b = read_bitsBE_b(offset_b+6, 15, streamFile); /* offset in bits inside this packet */
//packet_metadata = read_bitsBE_b(offset_b+21, 3, streamFile); /* packet_metadata (always 1) */
packet_skip_count = read_bitsBE_b(offset_b+24, 8, streamFile); /* packets to skip for next packet of this stream */
}
/* full packet skip */
if (packet_skip_count == 0x7FF) {
packet_skip_count = 0;
continue;
}
if (packet_skip_count > 255) { /* seen in some (converted?) XMA1 */
packet_skip_count = 0;
}
VGM_ASSERT(packet_skip_count > 10, "XMA: found big packet skip %i\n", packet_skip_count);//a bit unusual...
//VGM_LOG("packet: off=%x, ff=%i, ps=%i\n", offset, first_frame_b, packet_skip_b);
packet_offset_b = 4*8 + first_frame_b; /* packet offset in bits */
/* read packet frames */
while (packet_offset_b < packet_size_b) {
frame_offset_b = offset_b + packet_offset_b; /* in bits for aligment stuff */
//todo not sure if frames or frames+1 (considering skip_samples)
if (xma->loop_flag && (offset_b + packet_offset_b) - stream_offset_b == xma->loop_start_b)
loop_start_frame = frames;
if (xma->loop_flag && (offset_b + packet_offset_b) - stream_offset_b == xma->loop_end_b)
loop_end_frame = frames;
/* XMA1/2 frame header */
frame_size_b = read_bitsBE_b(frame_offset_b, 15, streamFile);
frame_offset_b += 15;
if (frame_size_b == 0) /* observed in some files with empty frames/packets */
break;
packet_offset_b += frame_size_b; /* including header */
#if 0
{
uint32_t frame_config
frame_config = read_bitsBE_b(frame_offset_b, 15, streamFile);
//VGM_LOG(" frame %04i: off_b=%I64x (~0x%I64x), fs_b=%i (~0x%x), fs=%x\n",frames, frame_offset_b, frame_offset_b/8, frame_size_b,frame_size_b/8, frame_config);
//if (frame_config != 0x7f00) /* "contains all subframes"? */
// continue; // todo read packet end bit instead
}
#endif
frame_offset_b += 15;
if (frame_size_b == 0x7FFF) { /* end packet frame marker */
break;
}
#if 0
// more header stuff (info from FFmpeg)
{
int flag;
/* ignore "postproc transform" */
if (xma->channels > 1) {
flag = read_bitsBE_b(frame_offset_b, 1, streamFile);
frame_offset_b += 1;
if (flag) {
flag = read_bitsBE_b(frame_offset_b, 1, streamFile);
frame_offset_b += 1;
if (flag) {
frame_offset_b += 1 + 4 * xma->channels*xma->channels; /* 4-something per double channel? */
}
}
}
/* get start/end skips to get the proper number of samples */ //todo check if first bit =1 means full 512 skip
flag = read_bitsBE_b(frame_offset_b, 1, streamFile);
frame_offset_b += 1;
if (flag) {
int new_skip;
/* get start skip */
flag = read_bitsBE_b(frame_offset_b, 1, streamFile);
frame_offset_b += 1;
if (flag) {
new_skip = read_bitsBE_b(frame_offset_b, 10, streamFile);
frame_offset_b += 10;
VGM_ASSERT(start_skip, "XMA: more than one start_skip (%i)\n", new_skip);
if (new_skip > XMA_SAMPLES_PER_FRAME) { /* from xmaencode */
VGM_LOG("XMA: bad start_skip (%i)\n", new_skip);
new_skip = XMA_SAMPLES_PER_FRAME;
}
if (frames==0) first_start_skip = new_skip; /* sometimes in the middle */
start_skip += new_skip;
}
/* get end skip */
flag = read_bitsBE_b(frame_offset_b, 1, streamFile);
frame_offset_b += 1;
if (flag) {
new_skip = read_bitsBE_b(frame_offset_b, 10, streamFile);
frame_offset_b += 10;
VGM_ASSERT(end_skip, "XMA: more than one end_skip (%i)\n", new_skip);
if (new_skip > XMA_SAMPLES_PER_FRAME) { /* from xmaencode */
VGM_LOG("XMA: bad end_skip (%i)\n", new_skip);
new_skip = XMA_SAMPLES_PER_FRAME;
}
last_end_skip = new_skip; /* not seen */
end_skip += new_skip;
}
VGM_LOG(" skip: st=%i, ed=%i\n", start_skip, end_skip);
}
}
#endif
samples += XMA_SAMPLES_PER_FRAME;
frames++;
}
}
#if XMA_CHECK_SKIPS
//todo this seems to usually work, but not always
/* apply skips (not sure why 64, empty samples generated by the decoder not in the file?) */
samples = samples + 64 - start_skip;
samples = samples + 64 - end_skip;
xma->skip_samples = 64 + 512; //todo not always correct
#endif
xma->num_samples = samples;
if (xma->loop_flag && loop_end_frame > loop_start_frame) {
xma->loop_start_sample = loop_start_frame * XMA_SAMPLES_PER_FRAME + xma->loop_start_subframe * XMA_SAMPLES_PER_SUBFRAME;
xma->loop_end_sample = loop_end_frame * XMA_SAMPLES_PER_FRAME + xma->loop_end_subframe * XMA_SAMPLES_PER_SUBFRAME;
#if XMA_CHECK_SKIPS
/* maybe this is needed */
//xma->loop_start_sample -= xma->skip_samples;
//xma->loop_end_sample -= xma->skip_samples;
#endif
}
}
#endif

View File

@ -602,7 +602,7 @@ static const meta_info meta_info_list[] = {
{meta_FSB4, "FMOD Sample Bank (FSB4) Header"}, {meta_FSB4, "FMOD Sample Bank (FSB4) Header"},
{meta_FSB5, "FMOD Sample Bank (FSB5) Header"}, {meta_FSB5, "FMOD Sample Bank (FSB5) Header"},
{meta_RWX, "RWX Header"}, {meta_RWX, "RWX Header"},
{meta_XWB, "XWB WBND Header"}, {meta_XWB, "Microsoft XWB Header"},
{meta_XA30, "XA30 Header"}, {meta_XA30, "XA30 Header"},
{meta_MUSC, "MUSC Header"}, {meta_MUSC, "MUSC Header"},
{meta_MUSX_V004, "MUSX / Version 004 Header"}, {meta_MUSX_V004, "MUSX / Version 004 Header"},

View File

@ -1258,10 +1258,14 @@
RelativePath=".\coding\eaxa_decoder.c" RelativePath=".\coding\eaxa_decoder.c"
> >
</File> </File>
<File <File
RelativePath=".\coding\ffmpeg_decoder.c" RelativePath=".\coding\ffmpeg_decoder.c"
> >
</File> </File>
<File
RelativePath=".\coding\ffmpeg_decoder_utils.c"
>
</File>
<File <File
RelativePath=".\coding\g719_decoder.c" RelativePath=".\coding\g719_decoder.c"
> >

View File

@ -124,6 +124,7 @@
<ItemGroup> <ItemGroup>
<ClCompile Include="coding\at3_decoder.c" /> <ClCompile Include="coding\at3_decoder.c" />
<ClCompile Include="coding\ffmpeg_decoder.c" /> <ClCompile Include="coding\ffmpeg_decoder.c" />
<ClCompile Include="coding\ffmpeg_decoder_utils.c" />
<ClCompile Include="coding\lsf_decoder.c" /> <ClCompile Include="coding\lsf_decoder.c" />
<ClCompile Include="coding\mp4_aac_decoder.c" /> <ClCompile Include="coding\mp4_aac_decoder.c" />
<ClCompile Include="coding\mtaf_decoder.c" /> <ClCompile Include="coding\mtaf_decoder.c" />

View File

@ -1021,6 +1021,9 @@
<ClCompile Include="coding\ffmpeg_decoder.c"> <ClCompile Include="coding\ffmpeg_decoder.c">
<Filter>coding\Source Files</Filter> <Filter>coding\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="coding\ffmpeg_decoder_utils.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\xma.c"> <ClCompile Include="meta\xma.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>

View File

@ -137,7 +137,7 @@ static int ffmpeg_read(void *opaque, uint8_t *buf, int buf_size)
/* when "fake" size is smaller than "real" size we need to make sure bytes_read (ret) is clamped; /* when "fake" size is smaller than "real" size we need to make sure bytes_read (ret) is clamped;
* it confuses FFmpeg in rare cases (STREAMFILE may have valid data after size) */ * it confuses FFmpeg in rare cases (STREAMFILE may have valid data after size) */
if (offset + buf_size > data->size + data->header_size) { if (offset + buf_size > data->size + data->header_size) {
buf_size = data->size + data->header_size - offset; buf_size = data->size - offset; /* header "read" is manually inserted later */
} }
ret = read_streamfile(buf, offset + data->start, buf_size, data->streamfile); ret = read_streamfile(buf, offset + data->start, buf_size, data->streamfile);

View File

@ -194,8 +194,6 @@ VGMSTREAM * init_vgmstream_rwx(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xwb(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_xwb(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xwb2(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_xa30(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_xa30(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_musc(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_musc(STREAMFILE * streamFile);

View File

@ -1,12 +1,9 @@
#include "meta.h" #include "meta.h"
#include "../util.h" #include "../util.h"
#include "../coding/coding.h"
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
#define ADJUST_SAMPLE_RATE 0
#define XMA_BYTES_PER_PACKET 2048
#define XMA_SAMPLES_PER_FRAME 512
#define XMA_SAMPLES_PER_SUBFRAME 128
#define FAKE_RIFF_BUFFER_SIZE 100 #define FAKE_RIFF_BUFFER_SIZE 100
@ -24,26 +21,23 @@ typedef struct {
uint8_t xma2_version; uint8_t xma2_version;
int needs_header; int needs_header;
int force_little_endian; /* FFmpeg can't parse big endian "fmt" chunks */ int force_little_endian; /* FFmpeg can't parse big endian "fmt" chunks */
int skip_samples;
/* info */ /* info */
int channels;
int loop_flag; int loop_flag;
int32_t num_samples; int32_t num_samples;
int32_t loop_start_sample; int32_t loop_start_sample;
int32_t loop_end_sample; int32_t loop_end_sample;
int32_t xma1_loop_start_offset_b; int32_t loop_start_b;
int32_t xma1_loop_end_offset_b; int32_t loop_end_b;
int32_t xma1_subframe_data; int32_t loop_subframe;
} xma_header_data; } xma_header_data;
static int parse_header(xma_header_data * xma, STREAMFILE *streamFile); static int parse_header(xma_header_data * xma, STREAMFILE *streamFile);
static void parse_xma1_sample_data(xma_header_data * xma, STREAMFILE *streamFile); static void fix_samples(xma_header_data * xma, STREAMFILE *streamFile);
static int create_riff_header(uint8_t * buf, size_t buf_size, xma_header_data * xma, STREAMFILE *streamFile); static int create_riff_header(uint8_t * buf, size_t buf_size, xma_header_data * xma, STREAMFILE *streamFile);
static int fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec);
#if ADJUST_SAMPLE_RATE
static int get_xma_sample_rate(int32_t general_rate);
#endif
/** /**
* XMA 1/2 (Microsoft) * XMA 1/2 (Microsoft)
@ -51,7 +45,6 @@ static int get_xma_sample_rate(int32_t general_rate);
* Usually in RIFF headers and minor variations. * Usually in RIFF headers and minor variations.
*/ */
VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) {
char filename[PATH_LIMIT];
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
ffmpeg_codec_data *data = NULL; ffmpeg_codec_data *data = NULL;
@ -61,12 +54,8 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) {
/* check extension, case insensitive */ /* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename)); /* .xma2: Skullgirls, .nps: Beautiful Katamari, .past: SoulCalibur II HD */
if (strcasecmp("xma",filename_extension(filename)) if ( !check_extensions(streamFile, "xma,xma2,nps,past") )
&& strcasecmp("xma2",filename_extension(filename)) /* Skullgirls */
&& strcasecmp("nps",filename_extension(filename)) /* Beautiful Katamari */
&& strcasecmp("past",filename_extension(filename)) /* SoulCalibur II HD */
)
goto fail; goto fail;
/* check header */ /* check header */
@ -91,24 +80,29 @@ VGMSTREAM * init_vgmstream_xma(STREAMFILE *streamFile) {
/* build VGMSTREAM */ /* build VGMSTREAM */
vgmstream = allocate_vgmstream(data->channels, xma.loop_flag); vgmstream = allocate_vgmstream(data->channels, xma.loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
/*vgmstream->channels = data->channels;*/
/*vgmstream->loop_flag = loop_flag;*/
vgmstream->codec_data = data; vgmstream->codec_data = data;
vgmstream->coding_type = coding_FFmpeg; vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_FFmpeg; vgmstream->meta_type = meta_FFmpeg;
vgmstream->sample_rate = data->sampleRate; vgmstream->sample_rate = data->sampleRate;
#if ADJUST_SAMPLE_RATE
vgmstream->sample_rate = get_xma_sample_rate(vgmstream->sample_rate);
#endif
vgmstream->num_samples = xma.num_samples; /* data->totalSamples: XMA1 = not set; XMA2 = not reliable */
/* fix samples for some formats (data->totalSamples: XMA1 = not set; XMA2 = not reliable) */
xma.channels = data->channels;
fix_samples(&xma, streamFile);
vgmstream->num_samples = xma.num_samples;
if (vgmstream->loop_flag) { if (vgmstream->loop_flag) {
vgmstream->loop_start_sample = xma.loop_start_sample; vgmstream->loop_start_sample = xma.loop_start_sample;
vgmstream->loop_end_sample = xma.loop_end_sample; vgmstream->loop_end_sample = xma.loop_end_sample;
} }
#if 0
//not active due to a FFmpeg bug that misses some of the last packet samples and decodes
// garbage if asked for more samples (always happens but more apparent with skip_samples active)
/* fix encoder delay */
if (data->skipSamples==0)
ffmpeg_set_skip_samples(data, xma.skip_samples);
#endif
return vgmstream; return vgmstream;
@ -178,7 +172,8 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) {
size_t riff_size = 0, fmt_size = 0, xma2_size = 0; size_t riff_size = 0, fmt_size = 0, xma2_size = 0;
riff_size = read_32bit(4,streamFile); riff_size = read_32bit(4,streamFile);
if (riff_size+8 > xma->file_size) goto fail; if (riff_size != xma->file_size && /* some Beautiful Katamari, unsure if bad rip */
riff_size+8 > xma->file_size) goto fail;
while (current_chunk < xma->file_size && current_chunk < riff_size+8) { while (current_chunk < xma->file_size && current_chunk < riff_size+8) {
uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); uint32_t chunk_type = read_32bitBE(current_chunk,streamFile);
@ -229,7 +224,7 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) {
} }
} }
else if (id == id_NXMA) { /* Namco (Tekken 6, Galaga Legions DX) */ else if (id == id_NXMA) { /* Namco (Tekken 6, Galaga Legions DX) */
/* custom header with a "XMA2" or "fmt " data chunk inside, most other values are unknown */ /* custom header with a "XMA2" or "fmt " data chunk inside, most other values are unknown */
uint32_t chunk_type = read_32bit(0xC,streamFile); uint32_t chunk_type = read_32bit(0xC,streamFile);
xma->data_offset = 0x100; xma->data_offset = 0x100;
xma->data_size = read_32bit(0x14,streamFile); xma->data_size = read_32bit(0x14,streamFile);
@ -248,7 +243,7 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) {
if (xma->data_size + xma->data_offset > xma->file_size) goto fail; if (xma->data_size + xma->data_offset > xma->file_size) goto fail;
} }
else if (id == id_PASX) { /* SoulCalibur II HD */ else if (id == id_PASX) { /* SoulCalibur II HD */
/* custom header with a "fmt " data chunk inside */ /* custom header with a "fmt " data chunk inside */
xma->chunk_size = read_32bit(0x08,streamFile); xma->chunk_size = read_32bit(0x08,streamFile);
xma->data_size = read_32bit(0x0c,streamFile); xma->data_size = read_32bit(0x0c,streamFile);
xma->chunk_offset = read_32bit(0x10,streamFile); xma->chunk_offset = read_32bit(0x10,streamFile);
@ -262,18 +257,21 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) {
goto fail; goto fail;
} }
/* find sample data */ /* find sample data */
if (xma->xma2_version) { /* old XMA2 (internally always BE) */ if (xma->xma2_version) { /* old XMA2 (internally always BE) */
xma->loop_start_sample = read_32bitBE(xma->chunk_offset+0x4,streamFile); xma->loop_start_sample = read_32bitBE(xma->chunk_offset+0x4,streamFile);
xma->loop_end_sample = read_32bitBE(xma->chunk_offset+0x8,streamFile); xma->loop_end_sample = read_32bitBE(xma->chunk_offset+0x8,streamFile);
xma->loop_flag = (uint8_t)read_8bit(xma->chunk_offset+0x3,streamFile) > 0 /* rarely not set */ xma->loop_flag = (uint8_t)read_8bit(xma->chunk_offset+0x3,streamFile) > 0 /* rarely not set, encoder default */
|| xma->loop_end_sample; || xma->loop_end_sample;
if (xma->xma2_version == 3) { if (xma->xma2_version == 3) {
xma->num_samples = read_32bitBE(xma->chunk_offset+0x14,streamFile); xma->num_samples = read_32bitBE(xma->chunk_offset+0x14,streamFile);
/*xma->pcm_samples = read_32bitBE(xma->chunk_offset+0x18,streamFile)*/
} else { } else {
xma->num_samples = read_32bitBE(xma->chunk_offset+0x1C,streamFile); xma->num_samples = read_32bitBE(xma->chunk_offset+0x1C,streamFile);
/*xma->pcm_samples = read_32bitBE(xma->chunk_offset+0x20,streamFile)*/
} }
/* num_samples is the max samples in the file (apparently not including encoder delay) */
/* pcm_samples are original WAV's; not current since samples and sample rate may be adjusted for looping purposes */
} }
else if (xma->fmt_codec == 0x166) { /* pure XMA2 */ else if (xma->fmt_codec == 0x166) { /* pure XMA2 */
xma->num_samples = read_32bit(xma->chunk_offset+0x18,streamFile); xma->num_samples = read_32bit(xma->chunk_offset+0x18,streamFile);
@ -281,17 +279,16 @@ static int parse_header(xma_header_data * xma, STREAMFILE *streamFile) {
xma->loop_end_sample = xma->loop_start_sample + read_32bit(xma->chunk_offset+0x2C,streamFile); xma->loop_end_sample = xma->loop_start_sample + read_32bit(xma->chunk_offset+0x2C,streamFile);
xma->loop_flag = (uint8_t)read_8bit(xma->chunk_offset+0x30,streamFile) > 0 /* never set in practice */ xma->loop_flag = (uint8_t)read_8bit(xma->chunk_offset+0x30,streamFile) > 0 /* never set in practice */
|| xma->loop_end_sample; || xma->loop_end_sample;
/* not needed but may affect looping? (sometimes these don't match loop/total samples) */ /* play_begin+end = probably pcm_samples (for original sample rate), don't seem to affect anything */
/* int32_t play_begin_sample = read_32bit(xma->chunk_offset+0x20,streamFile); */ /* int32_t play_begin_sample = read_32bit(xma->chunk_offset+0x20,streamFile); */
/* int32_t play_end_sample = play_begin_sample + read_32bit(xma->chunk_offset+0x24,streamFile); */ /* int32_t play_end_sample = play_begin_sample + read_32bit(xma->chunk_offset+0x24,streamFile); */
} }
else if (xma->fmt_codec == 0x165) { /* pure XMA1 */ else if (xma->fmt_codec == 0x165) { /* pure XMA1 */
xma->loop_flag = (uint8_t)read_8bit(xma->chunk_offset+0xA,streamFile) > 0; xma->loop_flag = (uint8_t)read_8bit(xma->chunk_offset+0xA,streamFile) > 0;
xma->xma1_loop_start_offset_b = read_32bit(xma->chunk_offset+0x14,streamFile); xma->loop_start_b = read_32bit(xma->chunk_offset+0x14,streamFile);
xma->xma1_loop_end_offset_b = read_32bit(xma->chunk_offset+0x18,streamFile); xma->loop_end_b = read_32bit(xma->chunk_offset+0x18,streamFile);
xma->xma1_subframe_data = (uint8_t)read_8bit(xma->chunk_offset+0x1C,streamFile); xma->loop_subframe = (uint8_t)read_8bit(xma->chunk_offset+0x1C,streamFile);
/* find samples count + loop samples since they are not in the header */ /* num_samples are parsed later */
parse_xma1_sample_data(xma, streamFile);
} }
else { /* unknown chunk */ else { /* unknown chunk */
goto fail; goto fail;
@ -304,69 +301,42 @@ fail:
} }
/** static void fix_samples(xma_header_data * xma, STREAMFILE *streamFile) {
* XMA1: manually find total and loop samples xma_sample_data xma_sd;
*
* A XMA1 stream is made of packets, each containing N frames of X samples, and frame is divided into subframes for looping purposes.
* FFmpeg can't get total samples without decoding, so we'll count frames+samples by reading packet headers.
*/
static void parse_xma1_sample_data(xma_header_data * xma, STREAMFILE *streamFile) {
int frames = 0, loop_frame_start = 0, loop_frame_end = 0, loop_subframe_end, loop_subframe_skip;
uint32_t header, first_frame_b, packet_skip_b, frame_size_b, packet_size_b;
uint64_t packet_offset_b, frame_offset_b;
uint32_t size;
uint32_t packet_size = XMA_BYTES_PER_PACKET; /* for now only XMA1 is fixed, but xmaencode.exe doesn't seem to use
uint32_t offset = xma->data_offset; * XMA2 sample values in the headers, and the exact number of samples may not be exact.
uint32_t offset_b = 0; * Also loop values don't seem to need skip_samples. */
uint32_t stream_offset_b = xma->data_offset * 8;
size = offset + xma->data_size; if (xma->fmt_codec != 0x165) {
packet_size_b = packet_size*8; return;
while (offset < size) { /* stream global offset*/
/* XMA1 packet header (32b) = packet_sequence:4, unk:2: frame_offset_in_bits:15, packet_stream_skip_count:11 */
header = read_32bitBE(offset, streamFile);
first_frame_b = (header >> 11) & 0x7FFF;
packet_skip_b = (header) & 0x7FF;
offset_b = offset * 8;
packet_offset_b = 4*8 + first_frame_b;
while (packet_offset_b < packet_size_b && packet_skip_b!=0x7FF) { /* internal packet offset + full packet skip (needed?) */
frame_offset_b = offset_b + packet_offset_b; /* global offset to packet, in bits for aligment stuff */
/* XMA1 frame header (32b) = frame_length_in_bits:15, frame_data:17+ */
header = read_32bitBE(frame_offset_b/8, streamFile);
frame_size_b = (header >> (17 - frame_offset_b % 8)) & 0x7FFF;
if (frame_size_b == 0) /* observed in some files */
break;
packet_offset_b += frame_size_b;/* including header */
if (frame_size_b != 0x7FFF) /* end frame marker*/
frames++;
if (xma->loop_flag && frame_offset_b - stream_offset_b == xma->xma1_loop_start_offset_b)
loop_frame_start = frames;
if (xma->loop_flag && frame_offset_b - stream_offset_b == xma->xma1_loop_end_offset_b)
loop_frame_end = frames;
}
offset += packet_size;
} }
loop_subframe_end = xma->xma1_subframe_data >> 4; /* upper 4b: subframe where the loop ends, 0..3 */ memset(&xma_sd,0,sizeof(xma_sample_data));
loop_subframe_skip = xma->xma1_subframe_data & 0xF; /* lower 4b: subframe where the loop starts, 0..4 */
xma->num_samples = frames * XMA_SAMPLES_PER_FRAME; /* call xma parser (copy to its own struct, a bit clunky but oh well...) */
if (xma->loop_flag) { xma_sd.xma_version = xma->fmt_codec==0x165 ? 1 : 2;
xma->loop_start_sample = loop_frame_start * XMA_SAMPLES_PER_FRAME + loop_subframe_skip * XMA_SAMPLES_PER_SUBFRAME; xma_sd.channels = xma->channels;
xma->loop_end_sample = loop_frame_end * XMA_SAMPLES_PER_FRAME + loop_subframe_end * XMA_SAMPLES_PER_SUBFRAME; xma_sd.data_offset = xma->data_offset;
} xma_sd.data_size = xma->data_size;
xma_sd.loop_flag = xma->loop_flag;
xma_sd.loop_start_b = xma->loop_start_b;
xma_sd.loop_end_b = xma->loop_end_b;
xma_sd.loop_start_subframe = xma->loop_subframe & 0xF; /* lower 4b: subframe where the loop starts, 0..4 */
xma_sd.loop_end_subframe = xma->loop_subframe >> 4; /* upper 4b: subframe where the loop ends, 0..3 */
xma_get_samples(&xma_sd, streamFile);
/* and recieve results */
xma->num_samples = xma_sd.num_samples;
xma->skip_samples = xma_sd.skip_samples;
xma->loop_start_sample = xma_sd.loop_start_sample;
xma->loop_end_sample = xma_sd.loop_end_sample;
/* XMA2 loop/num_samples don't seem to skip_samples */
} }
/** /**
* Recreates a RIFF header that FFmpeg can read since it lacks support for some variations. * Recreates a RIFF header that FFmpeg can read since it lacks support for some variations.
* *
@ -406,7 +376,7 @@ static int create_riff_header(uint8_t * buf, size_t buf_size, xma_header_data *
internal_size = 4+4+xma->chunk_size; internal_size = 4+4+xma->chunk_size;
if (xma->force_little_endian ) { if (xma->force_little_endian ) {
if ( !fmt_chunk_swap_endian(chunk, xma->fmt_codec) ) if ( !ffmpeg_fmt_chunk_swap_endian(chunk, xma->fmt_codec) )
goto fail; goto fail;
} }
@ -434,44 +404,10 @@ fail:
} }
#if 0
/** /**
* Swaps endianness * Get real XMA sample rate (from Microsoft docs).
* * Info only, not for playback as the encoder adjusts sample rate for looping purposes (sample<>data align).
* returns 0 on error
*/
static int fmt_chunk_swap_endian(uint8_t * chunk, uint16_t codec) {
if (codec != 0x166)
goto fail;
put_16bitLE(chunk + 0x00, get_16bitBE(chunk + 0x00));/*wFormatTag*/
put_16bitLE(chunk + 0x02, get_16bitBE(chunk + 0x02));/*nChannels*/
put_32bitLE(chunk + 0x04, get_32bitBE(chunk + 0x04));/*nSamplesPerSec*/
put_32bitLE(chunk + 0x08, get_32bitBE(chunk + 0x08));/*nAvgBytesPerSec*/
put_16bitLE(chunk + 0x0c, get_16bitBE(chunk + 0x0c));/*nBlockAlign*/
put_16bitLE(chunk + 0x0e, get_16bitBE(chunk + 0x0e));/*wBitsPerSample*/
put_16bitLE(chunk + 0x10, get_16bitBE(chunk + 0x10));/*cbSize*/
put_16bitLE(chunk + 0x12, get_16bitBE(chunk + 0x12));/*NumStreams*/
put_32bitLE(chunk + 0x14, get_32bitBE(chunk + 0x14));/*ChannelMask*/
put_32bitLE(chunk + 0x18, get_32bitBE(chunk + 0x18));/*SamplesEncoded*/
put_32bitLE(chunk + 0x1c, get_32bitBE(chunk + 0x1c));/*BytesPerBlock*/
put_32bitLE(chunk + 0x20, get_32bitBE(chunk + 0x20));/*PlayBegin*/
put_32bitLE(chunk + 0x24, get_32bitBE(chunk + 0x24));/*PlayLength*/
put_32bitLE(chunk + 0x28, get_32bitBE(chunk + 0x28));/*LoopBegin*/
put_32bitLE(chunk + 0x2c, get_32bitBE(chunk + 0x2c));/*LoopLength*/
/* put_8bit(chunk + 0x30, get_8bit(chunk + 0x30));*//*LoopCount*/
/* put_8bit(chunk + 0x31, get_8bit(chunk + 0x31));*//*EncoderVersion*/
put_16bitLE(chunk + 0x32, get_16bitBE(chunk + 0x32));/*BlockCount*/
return 1;
fail:
return 0;
}
#if ADJUST_SAMPLE_RATE
/**
* Get real XMA sample rate (from Microsoft docs, apparently info only and not correct for playback).
*/ */
static int32_t get_xma_sample_rate(int32_t general_rate) { static int32_t get_xma_sample_rate(int32_t general_rate) {
int32_t xma_rate = 48000; /* default XMA */ int32_t xma_rate = 48000; /* default XMA */

View File

@ -1,169 +1,384 @@
#include "meta.h" #include "meta.h"
#include "../util.h" #include "../util.h"
#include "../coding/coding.h"
/* XWB - XACT Wave Bank (Main format on the XBOX console) */ /* most info from XWBtool, xactwb.h, xact2wb.h and xact3wb.h */
#define WAVEBANK_FLAGS_COMPACT 0x00020000 // Bank uses compact format
#define WAVEBANKENTRY_FLAGS_IGNORELOOP 0x00000008 // Used internally when the loop region can't be used
static const int32_t wma_avg_bps_index[] = { /*7*/
12000, 24000, 4000, 6000, 8000, 20000, 2500
};
static const int32_t wma_block_align_index[] = { /*17*/
929, 1487, 1280, 2230, 8917, 8192, 4459, 5945, 2304, 1536, 1485, 1008, 2731, 4096, 6827, 5462, 1280
};
typedef enum { PCM, XBOX_ADPCM, MS_ADPCM, XMA1, XMA2, WMA, XWMA } xact_codec;
typedef struct {
int little_endian;
int xact; /* rough XACT version (1/2/3) */
int version;
/* segments */
off_t base_offset;
size_t base_size;
off_t entry_offset;
size_t entry_size;
off_t data_offset;
size_t data_size;
off_t stream_offset;
size_t stream_size;
uint32_t base_flags;
size_t entry_elem_size;
size_t entry_alignment;
int streams;
uint32_t entry_flags;
uint32_t format;
int tag;
int channels;
int sample_rate;
int block_align;
int bits_per_sample;
xact_codec codec;
int loop_flag;
uint32_t num_samples;
uint32_t loop_start;
uint32_t loop_end;
uint32_t loop_start_sample;
uint32_t loop_end_sample;
} xwb_header;
/* XWB - XACT Wave Bank (Microsoft SDK format for XBOX/XBOX360/Windows) */
VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT]; off_t start_offset, off, suboff;
off_t start_offset; xwb_header xwb;
int loop_flag = 0; int target_stream = 0;
int channel_count; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
int coding;
int coding_flag;
int num_samples;
int header_start;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("xwb",filename_extension(filename))) goto fail;
/* check header */ /* basic checks */
if ((read_32bitBE(0x00,streamFile) != 0x57424E44) && /* WBND */ if (!check_extensions(streamFile,"xwb")) goto fail;
(read_32bitBE(0x00,streamFile) != 0x444E4257)) /* DNBW */
if ((read_32bitBE(0x00,streamFile) != 0x57424E44) && /* "WBND" (LE) */
(read_32bitBE(0x00,streamFile) != 0x444E4257)) /* "DNBW" (BE) */
goto fail; goto fail;
/* check if the file is been used as container */
if (read_32bitBE(0x2C,streamFile) != 0x01000000)
goto fail;
header_start = read_32bitLE(0x10,streamFile);
loop_flag = (read_32bitLE(header_start+0x10,streamFile) != 0x0);
channel_count = 2;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */ memset(&xwb,0,sizeof(xwb_header));
start_offset = read_32bitLE(0x20,streamFile);
vgmstream->channels = channel_count;
vgmstream->sample_rate = (((uint32_t)read_32bitLE(header_start+0x4, streamFile)) << 8 >> 12) / 2;
coding_flag = (uint16_t)(read_16bitLE(header_start+0x2,streamFile));
switch (coding_flag) {
case 0:
coding = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x2;
num_samples = read_32bitLE(header_start+0xC,streamFile)/2/channel_count;
if (loop_flag) {
vgmstream->loop_start_sample = read_32bitLE(header_start+10,streamFile)/2/channel_count;
vgmstream->loop_end_sample = read_32bitLE(header_start+0xC,streamFile)/2/channel_count;
}
break;
case 1:
coding = coding_XBOX;
vgmstream->layout_type = layout_none;
num_samples = read_32bitLE(header_start+0xC,streamFile)/36/channel_count*64;
if (loop_flag) {
vgmstream->loop_start_sample = read_32bitLE(header_start+0x10,streamFile)/36/channel_count*64;
vgmstream->loop_end_sample = read_32bitLE(header_start+0xC,streamFile)/36/channel_count*64;
}
break;
default:
goto fail;
}
vgmstream->coding_type = coding; xwb.little_endian = read_32bitBE(0x00,streamFile) == 0x57424E44;/* WBND */
vgmstream->num_samples = num_samples; if (xwb.little_endian) {
vgmstream->meta_type = meta_XWB; read_32bit = read_32bitLE;
} else {
read_32bit = read_32bitBE;
}
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
/* read main header (WAVEBANKHEADER) */
if (vgmstream->coding_type == coding_XBOX) { xwb.version = read_32bit(0x04, streamFile);
/* xbox interleaving is a little odd */ if (xwb.version <= 3) {
vgmstream->ch[i].channel_start_offset=start_offset; xwb.xact = 1; /* XACT1: XBOX [The King of Fighters 2003] */
} else { } else if (xwb.version < 42) {
vgmstream->ch[i].channel_start_offset= xwb.xact = 2; /* XACT2: early XBOX360 [Kameo, Table Tennis, Blue Dragon], Windows */
start_offset+vgmstream->interleave_block_size*i; } else {
} xwb.xact = 3; /* XACT3: late XBOX360, Windows */
vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset; }
/* read segment offsets (SEGIDX) */
off = xwb.xact <= 2 ? 0x08 : 0x0c; /* XACT3: 0x04=tool version, 0x08=header version */
xwb.base_offset = read_32bit(off+0x00, streamFile);//BANKDATA
xwb.base_size = read_32bit(off+0x04, streamFile);
xwb.entry_offset= read_32bit(off+0x08, streamFile);//ENTRYMETADATA
xwb.entry_size = read_32bit(off+0x0c, streamFile);
/* go to last segment (XACT2/3 have 5 segments, XACT1 4) */
//0x10: XACT1/2: ENTRYNAMES, XACT3: SEEKTABLES
//0x14: XACT1: none (ENTRYWAVEDATA), XACT2: EXTRA, XACT3: ENTRYNAMES
suboff = xwb.xact >= 2 ? 0x08+0x08 : 0x08;
xwb.data_offset = read_32bit(off+0x10+suboff, streamFile);//ENTRYWAVEDATA
xwb.data_size = read_32bit(off+0x10+suboff, streamFile);
/* read base entry (WAVEBANKDATA) */
off = xwb.base_offset;
xwb.base_flags = (uint32_t)read_32bit(off+0x00, streamFile);
xwb.streams = read_32bit(off+0x04, streamFile);
/* 0x08 bank_name */
suboff = 0x08 + (xwb.xact == 1 ? 0x10 : 0x40);
xwb.entry_elem_size = read_32bit(off+suboff+0x00, streamFile);
/* suboff+0x04: meta name entry size */
xwb.entry_alignment = read_32bit(off+suboff+0x08, streamFile); /* usually 1 dvd sector */
xwb.format = read_32bit(off+suboff+0x0c, streamFile); /* compact mode only */
/* suboff+0x10: build time 64b (XACT2/3) */
VGM_ASSERT(xwb.streams > 1, "XWB: multiple streams found (%i entries)\n", xwb.streams);
if (target_stream == 0) target_stream = 1; /* auto: default to 1 */
if (xwb.streams < 1 || target_stream > xwb.streams) goto fail;
/* read stream entry (WAVEBANKENTRY) */
off = xwb.entry_offset + (target_stream-1) * xwb.entry_elem_size;
if (xwb.base_flags & WAVEBANK_FLAGS_COMPACT) { /* compact entry */
/* offset_in_sectors:21 and sector_alignment_in_bytes:11 */
uint32_t entry = (uint32_t)read_32bit(off+0x00, streamFile);
xwb.stream_offset = xwb.data_offset + (entry >> 11) * xwb.entry_alignment + (entry & 0x7FF);
/* find size (up to next entry or data end) */
if (xwb.streams > 1) {
entry = (uint32_t)read_32bit(off+xwb.entry_size, streamFile);
xwb.stream_size = xwb.stream_offset -
(xwb.data_offset + (entry >> 11) * xwb.entry_alignment + (entry & 0x7FF));
} else {
xwb.stream_size = xwb.data_size;
}
}
else {
uint32_t entry_info = (uint32_t)read_32bit(off+0x00, streamFile);
if (xwb.xact == 1) {
xwb.entry_flags = entry_info;
} else {
xwb.entry_flags = (entry_info) & 0xF; /*4*/
xwb.num_samples = (entry_info >> 4) & 0x0FFFFFFF; /*28*/
}
xwb.format = (uint32_t)read_32bit(off+0x04, streamFile);
xwb.stream_offset = xwb.data_offset + (uint32_t)read_32bit(off+0x08, streamFile);
xwb.stream_size = (uint32_t)read_32bit(off+0x0c, streamFile);
if (xwb.xact == 1) { //LoopRegion (bytes within data)
xwb.loop_start = (uint32_t)read_32bit(off+0x10, streamFile);
xwb.loop_end = (uint32_t)read_32bit(off+0x14, streamFile);//length
} else if (xwb.xact == 2) {//LoopRegion (bytes within data) or XMALoopRegion (bits within data)
xwb.loop_start = (uint32_t)read_32bit(off+0x10, streamFile);
xwb.loop_end = (uint32_t)read_32bit(off+0x14, streamFile);//length (LoopRegion) or offset (XMALoopRegion)
} else {//LoopRegion (samples)
xwb.loop_start_sample = (uint32_t)read_32bit(off+0x10, streamFile);
xwb.loop_end_sample = xwb.loop_start_sample + (uint32_t)read_32bit(off+0x14, streamFile);
}
xwb.loop_flag = (xwb.loop_end > 0 || xwb.loop_end_sample > xwb.loop_start)
&& !(xwb.entry_flags & WAVEBANKENTRY_FLAGS_IGNORELOOP);
}
/* parse format */
if (xwb.xact == 1) {
xwb.bits_per_sample = (xwb.format >> 31) & 0x1; /*1*/
xwb.sample_rate = (xwb.format >> 5) & 0x3FFFFFF; /*26*/
xwb.channels = (xwb.format >> 2) & 0x7; /*3*/
xwb.tag = (xwb.format) & 0x3; /*2*/
}
else if (xwb.version <= 34) { /* early XACT2, not sure if includes v35/36 */
xwb.bits_per_sample = (xwb.format >> 31) & 0x1; /*1*/
xwb.block_align = (xwb.format >> 24) & 0xFF; /*8*/
xwb.sample_rate = (xwb.format >> 4) & 0x7FFFF; /*19*/
xwb.channels = (xwb.format >> 1) & 0x7; /*3*/
xwb.tag = (xwb.format) & 0x1; /*1*/
}
else {
xwb.bits_per_sample = (xwb.format >> 31) & 0x1; /*1*/
xwb.block_align = (xwb.format >> 23) & 0xFF; /*8*/
xwb.sample_rate = (xwb.format >> 5) & 0x3FFFF; /*18*/
xwb.channels = (xwb.format >> 2) & 0x7; /*3*/
xwb.tag = (xwb.format) & 0x3; /*2*/
}
/* standardize tag to codec */
if (xwb.xact == 1) {
switch(xwb.tag){
case 0: xwb.codec = PCM; break;
case 1: xwb.codec = XBOX_ADPCM; break;
case 2: xwb.codec = WMA; break;
default: goto fail;
}
} else if (xwb.xact == 2) {
switch(xwb.tag) {
case 0: xwb.codec = PCM; break;
/* Table Tennis (v34): XMA1, Prey (v38): XMA2, v35/36/37: ? */
case 1: xwb.codec = xwb.version <= 34 ? XMA1 : XMA2; break;
case 2: xwb.codec = MS_ADPCM; break;
default: goto fail;
}
} else {
switch(xwb.tag) {
case 0: xwb.codec = PCM; break;
case 1: xwb.codec = XMA2; break;
case 2: xwb.codec = MS_ADPCM; break;
case 3: xwb.codec = XWMA; break;
default: goto fail;
} }
} }
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}
VGMSTREAM * init_vgmstream_xwb2(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag = 0;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("xwb",filename_extension(filename))) goto fail;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("xwb",filename_extension(filename))) goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x444E4257) /* WBND */
goto fail;
loop_flag = (read_32bitBE(0xA4,streamFile)!=0);
channel_count = 2;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x800;
vgmstream->channels = channel_count;
vgmstream->sample_rate = 44100;
vgmstream->coding_type = coding_PCM16BE;
vgmstream->num_samples = read_32bitBE(0xA0,streamFile)/2/channel_count;
if (loop_flag) {
vgmstream->loop_start_sample = read_32bitBE(0xA4,streamFile);
vgmstream->loop_end_sample = read_32bitBE(0xA0,streamFile)/2/channel_count;
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
vgmstream->meta_type = meta_XWB;
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
/* fix samples */
if ((xwb.xact == 1 || xwb.xact == 2) && xwb.codec == PCM) {
xwb.num_samples = xwb.stream_size / 2 / xwb.channels;
if (xwb.loop_flag) {
xwb.loop_start_sample = (xwb.loop_start) / 2 / xwb.channels;
xwb.loop_end_sample = (xwb.loop_start + xwb.loop_end) / 2 / xwb.channels;
} }
} }
else if (xwb.xact == 1 && xwb.codec == XBOX_ADPCM) {
xwb.num_samples = xwb.stream_size / 36 / xwb.channels*64;
if (xwb.loop_flag) {
xwb.loop_start_sample = xwb.loop_start / 36 / xwb.channels*64;
xwb.loop_end_sample = (xwb.loop_start + xwb.loop_end) / 36 / xwb.channels*64;
}
}
else if (xwb.xact == 2 && xwb.codec == MS_ADPCM && xwb.loop_flag) {
int block_size, samples_per_frame;
block_size = (xwb.block_align + 22) * xwb.channels; /*22=CONVERSION_OFFSET (?)*/
samples_per_frame = (block_size-(7-1) * xwb.channels) * 2 / xwb.channels;
xwb.loop_start_sample = (xwb.loop_start) / block_size / samples_per_frame;
xwb.loop_end_sample = (xwb.loop_start + xwb.loop_end) / block_size / samples_per_frame;
}
else if (xwb.xact == 2 && (xwb.codec == XMA1 || xwb.codec == XMA2) && xwb.loop_flag) {
/* need to manually find sample offsets, thanks to Microsoft dumb headers */
xma_sample_data xma_sd;
memset(&xma_sd,0,sizeof(xma_sample_data));
xma_sd.xma_version = xwb.codec == XMA1 ? 1 : 2;
xma_sd.channels = xwb.channels;
xma_sd.data_offset = xwb.stream_offset;
xma_sd.data_size = xwb.stream_size;
xma_sd.loop_flag = xwb.loop_flag;
xma_sd.loop_start_b = xwb.loop_start; /* bit offset in the stream */
xma_sd.loop_end_b = (xwb.loop_end >> 4); /*28b */
/* XACT adds +1 to the subframe, but this means 0 can't be used? */
xma_sd.loop_end_subframe = ((xwb.loop_end >> 2) & 0x3) + 1; /* 2b */
xma_sd.loop_start_subframe = ((xwb.loop_end >> 0) & 0x3) + 1; /* 2b */
xma_get_samples(&xma_sd, streamFile);
xwb.loop_start_sample = xma_sd.loop_start_sample;
xwb.loop_end_sample = xma_sd.loop_end_sample;
// todo fix properly (XWB loop_start/end seem to count padding samples while XMA1 RIFF doesn't)
xwb.loop_start_sample -= 512;
xwb.loop_end_sample -= 512;
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(xwb.channels,xwb.loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = xwb.sample_rate;
vgmstream->num_samples = xwb.num_samples;
vgmstream->loop_start_sample = xwb.loop_start_sample;
vgmstream->loop_end_sample = xwb.loop_end_sample;
vgmstream->meta_type = meta_XWB;
switch(xwb.codec) {
case PCM:
vgmstream->coding_type = xwb.bits_per_sample == 0 ? coding_PCM8 :
(xwb.little_endian ? coding_PCM16LE : coding_PCM16BE);
vgmstream->layout_type = xwb.channels > 1 ? layout_interleave : layout_none;
vgmstream->interleave_block_size = xwb.bits_per_sample == 0 ? 0x01 : 0x02;
break;
case XBOX_ADPCM:
vgmstream->coding_type = coding_XBOX;
vgmstream->layout_type = layout_none;
break;
case MS_ADPCM:
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = (xwb.block_align + 22) * xwb.channels; /*22=CONVERSION_OFFSET (?)*/
break;
#ifdef VGM_USE_FFMPEG
case XMA1: {
ffmpeg_codec_data *ffmpeg_data = NULL;
uint8_t buf[100];
int bytes;
bytes = ffmpeg_make_riff_xma1(buf, 100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, 0);
if (bytes <= 0) goto fail;
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
if ( !ffmpeg_data ) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
break;
}
case XMA2: {
ffmpeg_codec_data *ffmpeg_data = NULL;
uint8_t buf[100];
int bytes, block_size, block_count;
block_size = 0x10000; /* XACT default */
block_count = xwb.stream_size / block_size + (xwb.stream_size % block_size ? 1 : 0);
bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
if (bytes <= 0) goto fail;
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
if ( !ffmpeg_data ) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
break;
}
case WMA: { /* WMAudio1 (WMA v1) */
ffmpeg_codec_data *ffmpeg_data = NULL;
ffmpeg_data = init_ffmpeg_offset(streamFile, xwb.stream_offset,xwb.stream_size);
if ( !ffmpeg_data ) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
break;
}
case XWMA: { /* WMAudio2 (WMA v2), WMAudio3 (WMA Pro) */
ffmpeg_codec_data *ffmpeg_data = NULL;
uint8_t buf[100];
int bytes, bps_index, block_align, block_index, avg_bps, wma_codec;
bps_index = (xwb.block_align >> 5); /* upper 3b bytes-per-second index */ //docs say 2b+6b but are wrong
block_index = (xwb.block_align) & 0x1F; /*lower 5b block alignment index */
if (bps_index >= 7) goto fail;
if (block_index >= 17) goto fail;
avg_bps = wma_avg_bps_index[bps_index];
block_align = wma_block_align_index[block_index];
wma_codec = xwb.bits_per_sample ? 0x162 : 0x161; /* 0=WMAudio2, 1=WMAudio3 */
bytes = ffmpeg_make_riff_xwma(buf, 100, wma_codec, vgmstream->num_samples, xwb.stream_size, vgmstream->channels, vgmstream->sample_rate, avg_bps, block_align);
if (bytes <= 0) goto fail;
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, xwb.stream_offset,xwb.stream_size);
if ( !ffmpeg_data ) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
break;
}
#endif
default:
goto fail;
}
start_offset = xwb.data_offset;
if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
goto fail;
return vgmstream; return vgmstream;
/* clean up anything we may have opened */
fail: fail:
if (vgmstream) close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }

View File

@ -98,7 +98,6 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
init_vgmstream_fsb5, init_vgmstream_fsb5,
init_vgmstream_rwx, init_vgmstream_rwx,
init_vgmstream_xwb, init_vgmstream_xwb,
init_vgmstream_xwb2,
init_vgmstream_xa30, init_vgmstream_xa30,
init_vgmstream_musc, init_vgmstream_musc,
init_vgmstream_musx_v004, init_vgmstream_musx_v004,