Merge pull request #67 from bnnm/bik-lmp4-fsb-msf

Bik, lmp4, fsb, msf
This commit is contained in:
Christopher Snowhill 2017-02-04 16:58:51 -08:00 committed by GitHub
commit 35824ae6bc
18 changed files with 500 additions and 258 deletions

View File

@ -310,7 +310,8 @@ META_OBJS=meta/adx_header.o \
meta/xma.o \
meta/ps2.o \
meta/x360.o \
meta/dsp_adx.o
meta/dsp_adx.o \
meta/bik.o
EXT_LIBS = ../ext_libs/clHCA.o

View File

@ -26,6 +26,7 @@ void decode_rad_ima_mono(VGMSTREAMCHANNEL * stream, sample * outbuf, int channel
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);
/* ngc_dsp_decoder */
void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);

View File

@ -34,6 +34,32 @@ static const int IMA_IndexTable[16] =
};
/**
* expands one nibble to one PCM sample, MS-IMA style
*/
static void ms_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 = step >> 3;
if (sample_nibble & 1) delta += step >> 2;
if (sample_nibble & 2) delta += step >> 1;
if (sample_nibble & 4) delta += step;
if (sample_nibble & 8)
sample_decoded -= delta;
else
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;
}
void decode_nds_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int i, sample_count;
@ -304,7 +330,7 @@ void decode_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * o
//internal interleave (0x20+4 size), mixed channels (4 byte per ch, mixed stereo)
int block_samples = (vgmstream->channels==1) ?
32 :
32*(vgmstream->channels&2);
32*(vgmstream->channels&2);//todo this can be zero in 4/5/8ch = SEGFAULT using %
first_sample = first_sample % block_samples;
//normal header (per channel)
@ -384,7 +410,7 @@ void decode_int_xbox_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample
//semi-internal interleave (0x24 size), mixed channels (4 byte per ch)?
int block_samples = (vgmstream->channels==1) ?
32 :
32*(vgmstream->channels&2);
32*(vgmstream->channels&2);//todo this can be zero in 4/5/8ch = SEGFAULT using %
first_sample = first_sample % block_samples;
//normal header
@ -657,7 +683,7 @@ void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample *
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
//external interleave
//internal/byte interleave
//no header
@ -695,3 +721,39 @@ void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample *
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {
int i, sample_count;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
//internal interleave
int block_samples = (36 - 4) * 2; /* block size - header, 2 samples per byte */
first_sample = first_sample % block_samples;
//interleaved header (all hist per channel + all step_index per channel)
if (first_sample == 0) {
off_t hist_offset = stream->offset + 2*channel;
off_t step_offset = stream->offset + 2*channel + 2*vgmstream->channels;
hist1 = read_16bitLE(hist_offset,stream->streamfile);
step_index = read_8bit(step_offset,stream->streamfile);
if (step_index < 0) step_index=0;
if (step_index > 88) step_index=88;
}
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
off_t byte_offset = stream->offset + 4*vgmstream->channels + 2*channel + i/4*2*vgmstream->channels + (i%4)/2;//2-byte per channel
int nibble_shift = (i&1?4:0); //low nibble first
ms_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
outbuf[sample_count] = (short)(hist1);
}
//internal interleave: increment offset on complete frame
if (i == block_samples) stream->offset += 36*vgmstream->channels;
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}

View File

@ -28,7 +28,7 @@ static const char* extension_list[] = {
"aifcl",
//"aiff", //common
"aix",
"akb", //AAC
"akb",
"amts",
"as4",
"asd",
@ -53,6 +53,12 @@ static const char* extension_list[] = {
"bg00",
"bgw",
"bh2pcm",
"bik",
"bika",
"bik2",
"bik2a",
"bk2",
"bk2a",
"bmdx",
"bms",
"bnk",
@ -135,11 +141,12 @@ static const char* extension_list[] = {
"kraw",
"leg",
"logg",
"lmp4", //fake extension, for looping
"logg", //fake extension, for looping
"lpcm",
"lps",
"lsf",
"lwav",
"lwav", //fake extension, for looping
"matx",
"mca",
@ -149,6 +156,7 @@ static const char* extension_list[] = {
"mic",
"mihb",
"mnstr",
//"mp4", //common
"mpdsp",
"mpds",
"msa",
@ -170,6 +178,7 @@ static const char* extension_list[] = {
"nus3bank",
"nwa",
//"ogg", //common
"oma", //FFmpeg, not parsed (ATRAC3/ATRAC3PLUS/MP3/LPCM/WMA)
"omu",
"otm",
@ -285,6 +294,7 @@ static const char* extension_list[] = {
"wad",
"wam",
"was",
//"wav", //common
"wavm",
"wb",
"wii",
@ -406,6 +416,7 @@ static const coding_info coding_info_list[] = {
{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 multichannel 4-bit IMA ADPCM"},
{coding_WS, "Westwood Studios ADPCM"},
{coding_ACM, "InterPlay ACM"},
{coding_NWA0, "NWA DPCM Level 0"},

View File

@ -272,6 +272,10 @@
RelativePath=".\meta\bgw.c"
>
</File>
<File
RelativePath=".\meta\bik.c"
>
</File>
<File
RelativePath=".\meta\bnsf.c"
>

View File

@ -158,6 +158,7 @@
<ClCompile Include="meta\ast.c" />
<ClCompile Include="meta\baf.c" />
<ClCompile Include="meta\bgw.c" />
<ClCompile Include="meta\bik.c" />
<ClCompile Include="meta\bnsf.c" />
<ClCompile Include="meta\brstm.c" />
<ClCompile Include="meta\btsnd.c" />

View File

@ -1030,5 +1030,8 @@
<ClCompile Include="meta\dsp_adx.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\bik.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -251,5 +251,6 @@ libmeta_la_SOURCES += xma.c
libmeta_la_SOURCES += ps2.c
libmeta_la_SOURCES += x360.c
libmeta_la_SOURCES += dsp_adx.c
libmeta_la_SOURCES += bik.c
EXTRA_DIST = meta.h

125
src/meta/bik.c Normal file
View File

@ -0,0 +1,125 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../util.h"
#ifdef VGM_USE_FFMPEG
static uint32_t bik_get_num_samples(STREAMFILE *streamFile, int bits_per_sample);
/* BIK 1/2 - RAD Game Tools movies (audio/video format) */
VGMSTREAM * init_vgmstream_bik(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
ffmpeg_codec_data *data = NULL;
/* check extension, case insensitive (bika = manually demuxed audio) */
if (!check_extensions(streamFile,"bik,bika,bik2,bik2a,bk2,bk2a")) goto fail;
/* check header "BIK" (bik1) or "KB2" (bik2) (followed by version-char) */
if ((read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x42494B00 &&
(read_32bitBE(0x00,streamFile) & 0xffffff00) != 0x4B423200 ) goto fail;
/* FFmpeg can parse BIK audio, but can't get the number of samples, which vgmstream needs.
* The only way to get them is to read all frame headers */
data = init_ffmpeg_offset(streamFile, 0x0, get_streamfile_size(streamFile));
if (!data) goto fail;
vgmstream = allocate_vgmstream(data->channels, 0); /* alloc FFmpeg first to get channel count */
if (!vgmstream) goto fail;
vgmstream->codec_data = data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_FFmpeg;
vgmstream->sample_rate = data->sampleRate;
/* manually get num_samples since data->totalSamples is always 0 */
vgmstream->num_samples = bik_get_num_samples(streamFile, data->bitsPerSample);
if (vgmstream->num_samples == 0)
goto fail;
return vgmstream;
fail:
free_ffmpeg(data);
if (vgmstream) {
vgmstream->codec_data = NULL;
close_vgmstream(vgmstream);
}
return NULL;
}
/**
* Gets the number of samples in a BIK file by reading all frames' headers,
* as they are not in the main header. The header for BIK1 and 2 is the same.
* (a ~3 min movie needs ~6000-7000 frames = fseeks, should be fast enough)
*
* Needs bits per sample to calculate PCM samples, since most bink audio seems to use 32, actually.
*/
static uint32_t bik_get_num_samples(STREAMFILE *streamFile, int bits_per_sample) {
uint32_t *offsets = NULL;
uint32_t num_samples_b = 0;
off_t cur_offset;
size_t filesize;
int i, j, num_frames, num_tracks;
int target_stream = 0;
filesize = get_streamfile_size(streamFile);
num_frames = read_32bitLE(0x08,streamFile);
if (num_frames<=0) goto fail;
/* multichannel audio is usually N tracks of stereo/mono, no way to know channel layout */
num_tracks = read_32bitLE(0x28,streamFile);
if (num_tracks<=0 || num_tracks > 255) goto fail;
/* find the frame index table, which is after 3 audio headers of size 4 for each track */
cur_offset = 0x2c + num_tracks*4 * 3;
/* read offsets in a buffer, to avoid fseeking to the table back and forth
* the number of frames can be highly variable so we'll alloc */
offsets = malloc(sizeof(uint32_t) * num_frames);
for (i=0; i < num_frames; i++) {
offsets[i] = read_32bitLE(cur_offset,streamFile) & 0xFFFFFFFE; /* mask first bit (= keyframe) */
cur_offset += 0x4;
if (offsets[i] > filesize) goto fail;
}
/* after the last index is the file size, validate just in case */
if (read_32bitLE(cur_offset,streamFile)!=filesize) goto fail;
/* multistream support just for fun (FFmpeg should select the same target stream)
* (num_samples for other streams seem erratic though) */
if (target_stream > num_tracks) goto fail;
if (target_stream == 0) target_stream = 1;
VGM_ASSERT(num_tracks > 1, "BIK: multiple streams found (%i entries)\n", num_tracks);
/* read each frame header and sum all samples
* a frame has N audio packets with header (one per track) + video packet */
for (i=0; i < num_frames; i++) {
cur_offset = offsets[i];
/* read audio packet headers */
for (j=0; j < num_tracks; j++) {
uint32_t ap_size, samples_b;
ap_size = read_32bitLE(cur_offset+0x00,streamFile); /* not counting this int */
samples_b = read_32bitLE(cur_offset+0x04,streamFile); /* decoded samples in bytes */
if (ap_size==0) break; /* no audio in this frame */
if (j == target_stream-1) { /* target samples found, read next frame */
num_samples_b += samples_b;
break;
} else { /* check next audio packet */
cur_offset += 4 + ap_size; /* todo sometimes ap_size doesn't include itself (+4), others it does? */
}
}
}
free(offsets);
return num_samples_b / (bits_per_sample / 8);
fail:
free(offsets);
return 0;
}
#endif

View File

@ -480,6 +480,9 @@ fail:
void free_ffmpeg(ffmpeg_codec_data *data) {
if (data == NULL)
return;
if (data->lastReadPacket) {
av_packet_unref(data->lastReadPacket);
free(data->lastReadPacket);

View File

@ -315,27 +315,16 @@ VGMSTREAM * init_vgmstream_fsb_offset(STREAMFILE *streamFile, off_t offset) {
#endif
}
else if (fsbh.mode & FSOUND_IMAADPCM) { /* (codec 0x69, Voxware Byte Aligned) */
if (fsbh.mode & FSOUND_IMAADPCMSTEREO) { /* noninterleaved, true stereo IMA */
/* FSB4: Shatter, Blade Kitten (PC), Hard Corps: Uprising (PS3) */
vgmstream->coding_type = coding_MS_IMA;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = 0x24*vgmstream->channels;
//VGM_LOG("FSB FSOUND_IMAADPCMSTEREO found\n");
} else {
/* FSB3: Bioshock (PC); FSB4: Blade Kitten (PC) */
vgmstream->coding_type = coding_MS_IMA;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = 0x24*vgmstream->channels;
//VGM_LOG("FSB FSOUND_IMAADPCM found\n");
#if 0
if (fsbh.numchannels > 2) { /* Blade Kitten 5.1 (interleaved header?) */
vgmstream->coding_type = coding_XBOX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x12 * vgmstream->channels;
}
#endif
//VGM_ASSERT(fsbh.mode & FSOUND_IMAADPCMSTEREO, "FSB FSOUND_IMAADPCMSTEREO found\n");
/* FSOUND_IMAADPCMSTEREO is "noninterleaved, true stereo IMA", but doesn't seem to be any different
* (found in FSB4: Shatter, Blade Kitten (PC), Hard Corps: Uprising (PS3)) */
}
/* FSB3: Bioshock (PC); FSB4: Blade Kitten (PC) */
vgmstream->coding_type = coding_XBOX;
vgmstream->layout_type = layout_none;
/* "interleaved header" IMA, which seems only used with >2ch (ex. Blade Kitten 5.1) */
if (vgmstream->channels > 2)
vgmstream->coding_type = coding_FSB_IMA;
}
else if (fsbh.mode & FSOUND_VAG) {
/* FSB1: Jurassic Park Operation Genesis

View File

@ -5,8 +5,6 @@
/* FSB5 header */
VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t StartOffset;
int LoopFlag = 0;
@ -19,10 +17,10 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
int SampleHeaderStart, SampleHeaderLength, NameTableLength, SampleDataLength, CodingID, SampleMode;
int ExtraFlag, ExtraFlagStart, ExtraFlagType, ExtraFlagSize, ExtraFlagEnd;
int freq_mode, ch_mode;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("fsb",filename_extension(filename))) goto fail;
if (!check_extensions(streamFile,"fsb")) goto fail;
if (read_32bitBE(0x00,streamFile) != 0x46534235) goto fail; /* "FSB5" */
if (read_32bitLE(0x04,streamFile) != 0x01) goto fail; /* Version ID */
@ -39,23 +37,41 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
StartOffset = SampleHeaderLength + NameTableLength + 0x3C;
SampleMode = read_32bitLE(SampleHeaderStart+0x00,streamFile);
if (SampleMode&0x02)
{
SampleRate = 48000;
} else {
SampleRate = 44100;
/* get sample rate */
freq_mode = (SampleMode >> 1) & 0x0f; /* bits 5..1 */
switch (freq_mode) {
case 0: SampleRate = 4000; break; //???
case 1: SampleRate = 8000; break;
case 2: SampleRate = 11000; break;
case 3: SampleRate = 11025; break;
case 4: SampleRate = 16000; break;
case 5: SampleRate = 22050; break;
case 6: SampleRate = 24000; break;
case 7: SampleRate = 32000; break;
case 8: SampleRate = 44100; break;
case 9: SampleRate = 48000; break;
case 10: SampleRate = 96000; break; //???
default:
SampleRate = 44100;
//goto fail; /* probably better? */
break;
}
if (SampleMode&0x20)
{
ChannelCount = 2;
} else {
ChannelCount = 1;
/* get channels (from tests seems correct, but multichannel isn't very common, ex. no 4ch mode?) */
ch_mode = (SampleMode >> 5) & 0x03; /* bits 7..6 (maybe 8 too?) */
switch (ch_mode) {
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 */
/* other values (ex. 10ch) seem specified in the extra flags */
default:
goto fail;
}
/* get extra flags */
ExtraFlagStart = SampleHeaderStart+0x08;
if (SampleMode&0x01)
if (SampleMode&0x01) /* bit 0 */
{
do
{
@ -66,6 +82,11 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
switch(ExtraFlagType)
{
case 0x01: /* Channel Info */
{
ChannelCount = read_8bit(ExtraFlagStart+0x04,streamFile);
}
break;
case 0x02: /* Sample Rate Info */
{
SampleRate = read_32bitLE(ExtraFlagStart+0x04,streamFile);
@ -99,8 +120,8 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = ChannelCount;
vgmstream->sample_rate = SampleRate;
vgmstream->channels = ChannelCount;
vgmstream->sample_rate = SampleRate;
switch (CodingID)
@ -166,26 +187,17 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
vgmstream->coding_type = coding_NGC_DSP;
/* DSP Coeffs */
{
int c,i;
for (c=0;c<ChannelCount;c++) {
for (i=0;i<16;i++)
{
vgmstream->ch[c].adpcm_coef[i] = read_16bitBE(DSPInfoStart + c*0x2E + i*2,streamFile);
}
}
}
dsp_read_coefs_be(vgmstream,streamFile,DSPInfoStart,0x2E);
}
break;
case 0x07: /* FMOD_SOUND_FORMAT_IMAADPCM */
{
NumSamples = read_32bitLE(SampleHeaderStart+0x04,streamFile)/4;
vgmstream->layout_type = layout_none;
vgmstream->coding_type = coding_XBOX;
if (vgmstream->channels > 2) /* multichannel FSB IMA (interleaved header) */
vgmstream->coding_type = coding_FSB_IMA;
}
break;
@ -211,7 +223,7 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
{
NumSamples = read_32bitLE(SampleHeaderStart+0x04,streamFile)/2/ChannelCount;
#ifdef VGM_USE_MPEG
#ifdef VGM_USE_MPEG
{
mpeg_codec_data *mpeg_data = NULL;
struct mpg123_frameinfo mi;
@ -269,27 +281,8 @@ VGMSTREAM * init_vgmstream_fsb5(STREAMFILE *streamFile) {
vgmstream->loop_end_sample = LoopEnd;
}
/* 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<ChannelCount;i++) {
vgmstream->ch[i].streamfile = file;
if (vgmstream->coding_type == coding_XBOX) {
/* xbox interleaving is a little odd */
vgmstream->ch[i].channel_start_offset=StartOffset;
} else {
vgmstream->ch[i].channel_start_offset=
StartOffset+vgmstream->interleave_block_size*i;
}
vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset;
}
}
if (!vgmstream_open_stream(vgmstream,streamFile,StartOffset))
goto fail;
return vgmstream;

View File

@ -662,6 +662,8 @@ VGMSTREAM * init_vgmstream_ps2_svag_snk(STREAMFILE* streamFile);
#ifdef VGM_USE_FFMPEG
VGMSTREAM * init_vgmstream_xma(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_bik(STREAMFILE* streamFile);
#endif
VGMSTREAM * init_vgmstream_ps2_vds_vdm(STREAMFILE* streamFile);

View File

@ -165,69 +165,92 @@ fail:
#ifdef VGM_USE_FFMPEG
static int find_atom_be(STREAMFILE *streamFile, uint32_t atom_id, off_t start_offset, off_t *out_atom_offset, size_t *out_atom_size);
VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset = 0;
int loop_flag = 0;
int32_t num_samples = 0, loop_start_sample = 0, loop_end_sample = 0;
size_t filesize;
off_t atom_offset;
size_t atom_size;
int is_ffdl = 0;
ffmpeg_codec_data *ffmpeg_data = NULL;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if ( strcasecmp("mp4",filename_extension(filename))
&& strcasecmp("m4a",filename_extension(filename))
&& strcasecmp("m4v",filename_extension(filename))
&& strcasecmp("bin",filename_extension(filename)) ) /* Final Fantasy Dimensions iOS */
/* .bin: Final Fantasy Dimensions (iOS), Final Fantasy V (iOS) */
if (!check_extensions(streamFile,"mp4,m4a,m4v,lmp4,bin"))
goto fail;
filesize = streamFile->get_size(streamFile);
/* check header for Final Fantasy Dimensions */
if (read_32bitBE(0x00,streamFile) == 0x4646444C) { /* "FFDL" (any kind of FFD file) */
if (read_32bitBE(0x00,streamFile) == 0x4646444C) { /* "FFDL" (any kind of file) */
is_ffdl = 1;
if (read_32bitBE(0x04,streamFile) == 0x6D747873) { /* "mtxs" (bgm file) */
/* this value is erratic so we'll use FFmpeg's num_samples
* (can be bigger = silence-padded, or smaller = cut; doesn't matter for looping though)*/
num_samples = read_32bitLE(0x08,streamFile);
/* loop samples are within num_samples, and don't have encoder delay (loop_start=0 starts from encoder_delay) */
loop_start_sample = read_32bitLE(0x0c,streamFile);
loop_end_sample = read_32bitLE(0x10,streamFile);
loop_flag = !(loop_start_sample==0 && loop_end_sample==num_samples);
start_offset = 0x14;
/* some FFDL have muxed streams ("FFDL" + "mtxs" data1 + mp4 data1 + "mtxs" data2 + mp4 data2 + etc)
* check if there is anything after the first mp4 data */
if (!find_atom_be(streamFile, 0x6D646174, start_offset, &atom_offset, &atom_size)) goto fail; /* "mdat" */
if (atom_offset-8 + atom_size < filesize && read_32bitBE(atom_offset-8 + atom_size,streamFile) == 0x6D747873) { /*"mtxs"*/
VGM_LOG("FFDL: multiple streams found\n");
filesize = atom_offset-8 + atom_size; /* clamp size, though FFmpeg will ignore the extra data anyway */
}
} else {
start_offset = 0x4; /* some SEs */
start_offset = 0x4; /* some SEs contain "ftyp" after "FFDL" */
}
/* todo some FFDL have multi streams ("FFLD" + mtxsdata1 + mp4data1 + mtxsdata2 + mp4data2 + etc) */
}
/* check header */
if ( read_32bitBE(start_offset+0x04,streamFile) != 0x66747970) /* size 0x00 + "ftyp" 0x04 */
if ( read_32bitBE(start_offset+0x04,streamFile) != 0x66747970) /* atom size @0x00 + "ftyp" @0x04 */
goto fail;
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, streamFile->get_size(streamFile));
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset, filesize);
if ( !ffmpeg_data ) goto fail;
/* Tales of Hearts iOS has loop info in the first "free" atom */
if (!is_ffdl && find_atom_be(streamFile, 0x66726565, start_offset, &atom_offset, &atom_size)) { /* "free" */
if (read_32bitBE(atom_offset,streamFile) == 0x4F700002
&& (atom_size == 0x38 || atom_size == 0x40)) { /* make sure it's ToHr "free" */
/* 0x00: id? 0x04/8: s_rate; 0x10: num_samples (without padding, same as FFmpeg's) */
/* 0x14/18/1c: 0x238/250/278? 0x20: ? 0x24: start_pad */
loop_flag = read_32bitBE(atom_offset+0x28,streamFile);
if (loop_flag) { /* atom ends if no loop flag */
loop_start_sample = read_32bitBE(atom_offset+0x2c,streamFile);
loop_end_sample = read_32bitBE(atom_offset+0x30,streamFile);
}
}
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(ffmpeg_data->channels,loop_flag);
if (!vgmstream) goto fail;
vgmstream->num_samples = ffmpeg_data->totalSamples; /* todo FFD num_samples is different from this */
vgmstream->sample_rate = ffmpeg_data->sampleRate;
vgmstream->channels = ffmpeg_data->channels;
if (loop_flag) {
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
}
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_FFmpeg;
vgmstream->codec_data = ffmpeg_data;
vgmstream->num_samples = ffmpeg_data->totalSamples;
vgmstream->sample_rate = ffmpeg_data->sampleRate;
vgmstream->channels = ffmpeg_data->channels;
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
return vgmstream;
fail:
/* clean up anything we may have opened */
if (ffmpeg_data) {
free_ffmpeg(ffmpeg_data);
if (vgmstream) vgmstream->codec_data = NULL;
@ -236,4 +259,35 @@ fail:
return NULL;
}
/**
* Almost the same as streamfile.c's find_chunk but for "atom" chunks, which have chunk_size first because Apple.
*
* returns 0 on failure
*/
static int find_atom_be(STREAMFILE *streamFile, uint32_t atom_id, off_t start_offset, off_t *out_atom_offset, size_t *out_atom_size) {
size_t filesize;
off_t current_atom = start_offset;
int full_atom_size = 1;
int size_big_endian = 1;
filesize = get_streamfile_size(streamFile);
/* read chunks */
while (current_atom < filesize) {
off_t chunk_size = size_big_endian ?
read_32bitBE(current_atom+0,streamFile) :
read_32bitLE(current_atom+0,streamFile);
uint32_t chunk_type = read_32bitBE(current_atom+4,streamFile);
if (chunk_type == atom_id) {
if (out_atom_size) *out_atom_size = chunk_size;
if (out_atom_offset) *out_atom_offset = current_atom+8;
return 1;
}
current_atom += full_atom_size ? chunk_size : 4+4+chunk_size;
}
return 0;
}
#endif

View File

@ -2,164 +2,149 @@
#include "../coding/coding.h"
#include "../util.h"
/* MSF header */
/* MSF - Sony's PS3 SDK format (MultiStream File) */
VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset, header_offset = 0;
int32_t data_size, loop_start, loop_end;
int loop_flag = 0;
int channel_count;
int codec_id;
#ifdef VGM_USE_FFMPEG
ffmpeg_codec_data *ffmpeg_data = NULL;
#endif
uint32_t data_size, loop_start = 0, loop_end = 0;
uint32_t id, codec_id, flags;
int loop_flag = 0, channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("msf",filename_extension(filename))) goto fail;
if (!check_extensions(streamFile,"msf,at3")) goto fail; /* .at3: Silent Hill HD Collection */
/* "WMSF" variation with a mini header over the MSFC header, same extension */
if (read_32bitBE(0x00,streamFile) == 0x574D5346) {
header_offset = 0x10;
}
start_offset = header_offset+0x40;
start_offset = header_offset+0x40; /* MSF header is always 0x40 */
/* usually 0x4D534643 "MSFC" */
if (read_8bit(header_offset+0x0,streamFile) != 0x4D) goto fail; /* M */
if (read_8bit(header_offset+0x1,streamFile) != 0x53) goto fail; /* S */
if (read_8bit(header_offset+0x2,streamFile) != 0x46) goto fail; /* F */
/* check header "MSF" + version-char
* usually "MSF\0\1", "MSF\0\2", "MSF5"(\3\5), "MSFC"(\4\3) (latest/common version) */
id = read_32bitBE(header_offset+0x00,streamFile);
if ((id & 0xffffff00) != 0x4D534600) goto fail;
codec_id = read_32bitBE(header_offset+0x04,streamFile);
channel_count = read_32bitBE(header_offset+0x08,streamFile);
data_size = read_32bitBE(header_offset+0x0C,streamFile); /* without header */
if (data_size == 0xFFFFFFFF) /* unneeded? */
data_size = get_streamfile_size(streamFile) - start_offset;
data_size = read_32bitBE(header_offset+0x0C,streamFile); /* without header*/
if (data_size==0xFFFFFFFF) {
size_t fileLength = get_streamfile_size(streamFile);
data_size = fileLength - start_offset;
}
/* byte flags, not in MSFv1 or v2
* 0x01/02/04/08: loop marker 0/1/2/3 (requires flag 0x10)
* 0x10: "resample" loop option (may be active with no 0x01 flag set)
* 0x20: VBR MP3
* 0x40: joint stereo MP3 (apparently interleaved stereo for other formats)
* 0x80+: (none/reserved) */
flags = read_32bitBE(header_offset+0x14,streamFile);
/* sometimes loop_start/end is set but not flag 0x01, but from tests it only loops with 0x01 */
loop_flag = flags != 0xffffffff && (flags & 0x10) && (flags & 0x01);
/* block_align/loop_type? = read_32bitBE(header_offset+0x14,streamFile);*/ /* 00/40 when no loop, 11/50/51/71 */
loop_start = read_32bitBE(header_offset+0x18,streamFile);
loop_end = read_32bitBE(header_offset+0x1C,streamFile); /* loop duration */
loop_flag = loop_start != 0xFFFFFFFF;
/* loop markers (marker N @ 0x18 + N*(4+4), but in practice only marker 0 is used) */
if (loop_flag) {
if (loop_end==0xFFFFFFFF) {/* not seen */
loop_start = read_32bitBE(header_offset+0x18,streamFile);
loop_end = read_32bitBE(header_offset+0x1C,streamFile); /* loop duration */
loop_end = loop_start + loop_end; /* usually equals data_size but not always */
if (loop_end > data_size)/* not seen */
loop_end = data_size;
} else {
loop_end = loop_start + loop_end; /* usually equals data_size but not always */
if ( loop_end > data_size)/* not seen */
loop_end = data_size;
}
}
channel_count = read_32bitBE(header_offset+0x8,streamFile);
codec_id = read_32bitBE(header_offset+0x4,streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->channels = channel_count;
/* Sample rate hack for strange files that don't have a specified frequency */
if (read_32bitBE(header_offset+0x10,streamFile)==0x00000000)
/* Sample rate hack for strange MSFv1 files that don't have a specified frequency */
vgmstream->sample_rate = read_32bitBE(header_offset+0x10,streamFile);
if (vgmstream->sample_rate == 0x00000000) /* PS ADPCM only? */
vgmstream->sample_rate = 48000;
else
vgmstream->sample_rate = read_32bitBE(header_offset+0x10,streamFile);
vgmstream->meta_type = meta_PS3_MSF;
switch (codec_id) {
case 0x0: /* PCM (Big Endian) */
{
vgmstream->coding_type = coding_PCM16BE;
vgmstream->num_samples = data_size/2/channel_count;
if (loop_flag){
vgmstream->loop_start_sample = loop_start/2/channel_count;
vgmstream->loop_end_sample = loop_end/2/channel_count;
}
if (channel_count == 1)
{
vgmstream->layout_type = layout_none;
}
else if (channel_count > 1)
{
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 2;
}
}
break;
case 0x3: /* PSx ADPCM */
{
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = data_size*28/16/channel_count;
case 0x0: /* PCM (Big Endian) */
case 0x1: { /* PCM (Little Endian) */
vgmstream->coding_type = codec_id==0 ? coding_PCM16BE : coding_PCM16LE;
vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave;
vgmstream->interleave_block_size = 2;
if (loop_flag)
{
vgmstream->loop_start_sample = loop_start*28/16/channel_count;
vgmstream->loop_end_sample = loop_end*28/16/channel_count;
}
if (channel_count == 1)
{
vgmstream->layout_type = layout_none;
}
else if (channel_count > 1)
{
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10;
}
vgmstream->num_samples = data_size/2/channel_count;
if (loop_flag){
vgmstream->loop_start_sample = loop_start/2/channel_count;
vgmstream->loop_end_sample = loop_end/2/channel_count;
}
break;
}
case 0x2: { /* PCM 32 (Float) */
goto fail; //probably unused/spec only
}
case 0x3: { /* PS ADPCM */
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = channel_count == 1 ? layout_none : layout_interleave;
vgmstream->interleave_block_size = 0x10;
vgmstream->num_samples = data_size*28/16/channel_count;
if (loop_flag) {
vgmstream->loop_start_sample = loop_start*28/16/channel_count;
vgmstream->loop_end_sample = loop_end*28/16/channel_count;
}
break;
}
#ifdef VGM_USE_FFMPEG
case 0x4: /* ATRAC3 (frame size 96) */
case 0x5: /* ATRAC3 (frame size 152) */
case 0x6: /* ATRAC3 (frame size 192) */
/* delegate to FFMpeg, it can parse MSF files */
ffmpeg_data = init_ffmpeg_offset(streamFile, header_offset, streamFile->get_size(streamFile) );
if ( !ffmpeg_data ) goto fail;
case 0x4: /* ATRAC3 low (66 kbps, frame size 96, Joint Stereo) */
case 0x5: /* ATRAC3 mid (105 kbps, frame size 152) */
case 0x6: { /* ATRAC3 high (132 kbps, frame size 192) */
ffmpeg_codec_data *ffmpeg_data = NULL;
uint8_t buf[100];
int32_t bytes, samples_size = 1024, block_size, encoder_delay, joint_stereo, max_samples;
block_size = (codec_id==4 ? 0x60 : (codec_id==5 ? 0x98 : 0xC0)) * vgmstream->channels;
encoder_delay = 0x0; //todo MSF encoder delay (around 440-450*2)
max_samples = (data_size / block_size) * samples_size;
joint_stereo = codec_id==4; /* interleaved joint stereo (ch must be even) */
if (vgmstream->sample_rate==0xFFFFFFFF) /* some MSFv1 (Digi World SP) */
vgmstream->sample_rate = 44100;//voice tracks seems to use 44khz, not sure about other tracks
/* make a fake riff so FFmpeg can parse the ATRAC3 */
bytes = ffmpeg_make_riff_atrac3(buf, 100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_size, joint_stereo, encoder_delay);
if (bytes <= 0) goto fail;
ffmpeg_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
if (!ffmpeg_data) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_FFmpeg;
vgmstream->codec_data = ffmpeg_data;
vgmstream->num_samples = ffmpeg_data->totalSamples;
if (loop_flag && ffmpeg_data->blockAlign > 0) {
vgmstream->loop_start_sample = (loop_start / ffmpeg_data->blockAlign) * ffmpeg_data->frameSize;
vgmstream->loop_end_sample = (loop_end / ffmpeg_data->blockAlign) * ffmpeg_data->frameSize;
vgmstream->num_samples = max_samples;
if (loop_flag) {
vgmstream->loop_start_sample = (loop_start / block_size) * samples_size;
vgmstream->loop_end_sample = (loop_end / block_size) * samples_size;
}
break;
}
#endif
#ifdef VGM_USE_FFMPEG
case 0x7: /* MPEG */
case 0x7: { /* MPEG (LAME MP3 of any quality) */
/* delegate to FFMpeg, it can parse MSF files */
ffmpeg_data = init_ffmpeg_offset(streamFile, header_offset, streamFile->get_size(streamFile) );
ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, header_offset, streamFile->get_size(streamFile) );
if ( !ffmpeg_data ) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_FFmpeg;
vgmstream->codec_data = ffmpeg_data;
/* TODO check CBR better (bitrate % X != 0?) */
if (ffmpeg_data->bitrate == 0)
goto fail;
/* vgmstream->num_samples = ffmpeg_data->totalSamples; */ /* duration may not be set/inaccurate */
vgmstream->num_samples = (int64_t)data_size * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate;
if (loop_flag) {
//todo properly apply encoder delay, which seems to vary between 1152 (1f), 528, 576 or 528+576
int frame_size = ffmpeg_data->frameSize;
vgmstream->loop_start_sample = (int64_t)loop_start * ffmpeg_data->sampleRate * 8 / ffmpeg_data->bitrate;
vgmstream->loop_start_sample -= vgmstream->loop_start_sample==frame_size ? frame_size
@ -170,67 +155,51 @@ VGMSTREAM * init_vgmstream_ps3_msf(STREAMFILE *streamFile) {
}
break;
}
#endif
#if defined(VGM_USE_MPEG) && !defined(VGM_USE_FFMPEG)
case 0x7: /* MPEG */
{
int frame_size = 576; /* todo incorrect looping calcs, MP3 can have other sizes */
case 0x7: { /* MPEG (LAME MP3 of any quality) */
int frame_size = 576; /* todo incorrect looping calcs */
mpeg_codec_data *mpeg_data = NULL;
struct mpg123_frameinfo mi;
coding_t ct;
mpeg_codec_data *mpeg_data = NULL;
struct mpg123_frameinfo mi;
coding_t ct;
mpeg_data = init_mpeg_codec_data(streamFile, start_offset, vgmstream->sample_rate, vgmstream->channels, &ct, NULL, NULL);
if (!mpeg_data) goto fail;
vgmstream->codec_data = mpeg_data;
mpeg_data = init_mpeg_codec_data(streamFile, start_offset, vgmstream->sample_rate, vgmstream->channels, &ct, NULL, NULL);
if (!mpeg_data) goto fail;
vgmstream->codec_data = mpeg_data;
if (MPG123_OK != mpg123_info(mpeg_data->m, &mi)) goto fail;
if (MPG123_OK != mpg123_info(mpeg_data->m, &mi)) goto fail;
vgmstream->coding_type = ct;
vgmstream->layout_type = layout_mpeg;
if (mi.vbr != MPG123_CBR) goto fail;
vgmstream->num_samples = mpeg_bytes_to_samples(data_size, &mi);
vgmstream->num_samples -= vgmstream->num_samples % frame_size;
if (loop_flag) {
vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, &mi);
vgmstream->loop_start_sample -= vgmstream->loop_start_sample % frame_size;
vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, &mi);
vgmstream->loop_end_sample -= vgmstream->loop_end_sample % frame_size;
}
vgmstream->interleave_block_size = 0;
vgmstream->coding_type = ct;
vgmstream->layout_type = layout_mpeg;
if (mi.vbr != MPG123_CBR) goto fail;
vgmstream->num_samples = mpeg_bytes_to_samples(data_size, &mi);
vgmstream->num_samples -= vgmstream->num_samples % frame_size;
if (loop_flag) {
vgmstream->loop_start_sample = mpeg_bytes_to_samples(loop_start, &mi);
vgmstream->loop_start_sample -= vgmstream->loop_start_sample % frame_size;
vgmstream->loop_end_sample = mpeg_bytes_to_samples(loop_end, &mi);
vgmstream->loop_end_sample -= vgmstream->loop_end_sample % frame_size;
}
vgmstream->interleave_block_size = 0;
break;
}
#endif
default:
default: /* 8+: not defined */
goto fail;
}
/* 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;
}
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
/* clean up anything we may have opened */
fail:
#ifdef VGM_USE_FFMPEG
if (ffmpeg_data) {
free_ffmpeg(ffmpeg_data);
if (vgmstream) vgmstream->codec_data = NULL;
}
#endif
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -70,19 +70,31 @@ void concatn(int length, char * dst, const char * src);
/* Simple stdout logging for debugging and regression testing purposes.
* Needs C99 variadic macros. */
#ifdef VGM_DEBUG_OUTPUT
/* equivalent to printf when condition is true */
#define VGM_ASSERT(condition, ...) \
do { if (condition) printf(__VA_ARGS__); } while (0)
/* equivalent to printf */
#define VGM_LOG(...) \
do { printf(__VA_ARGS__); } while (0)
/* prints file/line/func */
#define VGM_LOGF() \
do { printf("%s:%i '%s'\n", __FILE__, __LINE__, __func__); } while (0)
/* prints a buffer/array */
#define VGM_LOGB(buf, buf_size, bytes_per_line) \
do { \
int i; \
for (i=0; i < buf_size; i++) { \
printf("%02x",buf[i]); \
if (bytes_per_line && (i+1) % bytes_per_line == 0) printf("\n"); \
} \
printf("\n"); \
} while (0)
#else
#define VGM_ASSERT(condition,fmt, ...) /* nothing */
#define VGM_ASSERT(condition, ...) /* nothing */
#define VGM_LOG(...) /* nothing */
#define VGM_LOGF(...) /* nothing */
#define VGM_LOGF() /* nothing */
#define VGM_LOGB(buf, buf_size, bytes_per_line) /* nothing */
#endif

View File

@ -342,6 +342,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
#ifdef VGM_USE_FFMPEG
init_vgmstream_xma,
init_vgmstream_mp4_aac_ffmpeg,
init_vgmstream_bik,
init_vgmstream_ffmpeg, /* should go at the end */
#endif
@ -1042,6 +1043,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
return (vgmstream->interleave_block_size - 1) * 2; /* decodes 1 byte into 2 bytes */
case coding_XBOX:
case coding_INT_XBOX:
case coding_FSB_IMA:
return 64;
case coding_EA_XA:
return 28;
@ -1174,6 +1176,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
return 14*vgmstream->channels;
case coding_XBOX:
case coding_INT_XBOX:
case coding_FSB_IMA:
return 36;
case coding_MAXIS_ADPCM:
return 15*vgmstream->channels;
@ -1558,6 +1561,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
samples_to_do,chan);
}
break;
case coding_FSB_IMA:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_fsb_ima(vgmstream, &vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
vgmstream->channels,vgmstream->samples_into_block,
samples_to_do,chan);
}
break;
case coding_WS:
for (chan=0;chan<vgmstream->channels;chan++) {

View File

@ -120,6 +120,7 @@ typedef enum {
coding_DAT4_IMA, /* Eurocom 'DAT4' IMA ADPCM */
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_WS, /* Westwood Studios VBR ADPCM */
coding_MSADPCM, /* Microsoft ADPCM */