Merge pull request #212 from bnnm/ubi-misc

Ubi, misc
This commit is contained in:
Christopher Snowhill 2018-04-07 19:48:21 -07:00 committed by GitHub
commit 7a458c0bc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1070 additions and 447 deletions

View File

@ -77,7 +77,7 @@ int main(int argc, char ** argv) {
int print_adxencd = 0;
int print_oggenc = 0;
int print_batchvar = 0;
int write_lwav = 0;
int write_lwav = 0, write_lwav_loop_start = 0, write_lwav_loop_end = 0;
int only_stereo = -1;
int stream_index = 0;
double loop_count = 2.0;
@ -220,6 +220,12 @@ int main(int argc, char ** argv) {
vgmstream_force_loop(vgmstream, 0, 0,0);
}
if (write_lwav) {
write_lwav_loop_start = vgmstream->loop_start_sample;
write_lwav_loop_end = vgmstream->loop_end_sample;
vgmstream_force_loop(vgmstream, 0, 0,0);
}
if (play_sdtout) {
if (outfilename) {
fprintf(stderr,"either -p or -o, make up your mind\n");
@ -318,17 +324,11 @@ int main(int argc, char ** argv) {
{
uint8_t wav_buf[0x100];
int channels = (only_stereo != -1) ? 2 : vgmstream->channels;
int smpl_chunk = (write_lwav && vgmstream->loop_flag);
size_t bytes_done;
bytes_done = make_wav_header(wav_buf,0x100,
len_samples, vgmstream->sample_rate, channels,
smpl_chunk, vgmstream->loop_start_sample, vgmstream->loop_end_sample);
/* once "smpl" with loops is written we don't want actual file looping */
if (smpl_chunk) {
vgmstream_force_loop(vgmstream, 0, 0,0);
}
write_lwav, write_lwav_loop_start, write_lwav_loop_end);
fwrite(wav_buf,sizeof(uint8_t),bytes_done,outfile);
}
@ -407,26 +407,22 @@ int main(int argc, char ** argv) {
vgmstream_force_loop(vgmstream, 0, 0,0);
}
if (write_lwav) {
write_lwav_loop_start = vgmstream->loop_start_sample;
write_lwav_loop_end = vgmstream->loop_end_sample;
vgmstream_force_loop(vgmstream, 0, 0,0);
}
/* slap on a .wav header */
{
uint8_t wav_buf[0x100];
int channels = (only_stereo != -1) ? 2 : vgmstream->channels;
int smpl_chunk = (write_lwav && vgmstream->loop_flag);
size_t bytes_done;
bytes_done = make_wav_header(wav_buf,0x100,
len_samples, vgmstream->sample_rate, channels,
smpl_chunk, vgmstream->loop_start_sample, vgmstream->loop_end_sample);
write_lwav, write_lwav_loop_start, write_lwav_loop_end);
/* once "smpl" with looping is written we don't want actual file looping */
if (smpl_chunk) {
vgmstream_force_loop(vgmstream, 0, 0,0);
}
if (write_lwav && vgmstream->loop_flag) { // Adding space for smpl chunk at end
int32_t bytecount = get_32bitLE((uint8_t*)buf + 4);
put_32bitLE((uint8_t*)buf + 4, bytecount + 0x44);
}
fwrite(wav_buf,sizeof(uint8_t),bytes_done,outfile);
}
@ -499,7 +495,7 @@ static size_t make_wav_header(uint8_t * buf, size_t buf_size, int32_t sample_cou
data_size = sample_count*channels*sizeof(sample);
header_size = 0x2c;
if (smpl_chunk)
if (smpl_chunk && loop_end)
header_size += 0x3c+ 0x08;
if (header_size > buf_size)
@ -519,7 +515,7 @@ static size_t make_wav_header(uint8_t * buf, size_t buf_size, int32_t sample_cou
put_16bitLE(buf+0x20, (int16_t)(channels*sizeof(sample))); /* block align */
put_16bitLE(buf+0x22, sizeof(sample)*8); /* significant bits per sample */
if (smpl_chunk) {
if (smpl_chunk && loop_end) {
make_smpl_chunk(buf+0x24, loop_start, loop_end);
memcpy(buf+0x24+0x3c+0x08, "data", 0x04); /* WAVE data chunk */
put_32bitLE(buf+0x28+0x3c+0x08, (int32_t)data_size); /* size of WAVE data chunk */

View File

@ -58,7 +58,11 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m
break;
case MPEG_LYN:
goto fail; /* not fully implemented */
if (data->config.interleave <= 0)
goto fail; /* needs external fixed size */
data->default_buffer_size = data->config.interleave;
//todo simplify/unify XVAG/P3D/SCD/LYN and just feed arbitrary chunks to the decoder
break;
case MPEG_STANDARD:
case MPEG_AHX:
@ -137,9 +141,9 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
case MPEG_P3D: /* fixed interleave, not frame-aligned (ie. blocks may end/start in part of a frame) */
case MPEG_SCD:
case MPEG_LYN:
current_interleave = data->config.interleave;
#if 1
/* check if current interleave block is short */
{
off_t block_offset = stream->offset - stream->channel_start_offset;
@ -148,7 +152,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
if (data->config.data_size && block_offset + next_block >= data->config.data_size)
current_interleave = (data->config.data_size % next_block) / data->streams_size; /* short_interleave*/
}
#endif
current_interleave_pre = current_interleave*num_stream;
current_interleave_post = current_interleave*(data->streams_size-1) - current_interleave_pre;
@ -162,7 +166,7 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
break;
}
if (!current_data_size || current_data_size > ms->buffer_size) {
VGM_LOG("MPEG: incorrect data_size 0x%x\n", current_data_size);
VGM_LOG("MPEG: incorrect data_size 0x%x vs buffer 0x%x\n", current_data_size, ms->buffer_size);
goto fail;
}

View File

@ -638,7 +638,7 @@ fail:
}
/* Skip EA-frames from other streams for multichannel (interleaved 1 EA-frame per stream).
/* Skip EA-frames from other streams for .sns/sps multichannel (interleaved 1 EA-frame per stream).
* Due to EALayer3 being in blocks and other complexities (we can't go past a block) all
* streams's offsets should start in the first stream's EA-frame.
*
@ -649,6 +649,8 @@ fail:
* - skip one EA-frame per following streams until offset is in first stream's EA-frame
* (ie. 1st stream skips 2, 2nd stream skips 1, 3rd stream skips 0)
* - repeat again for granule1
*
* EALayer3 v1 in SCHl uses external offsets and 1ch multichannel instead.
*/
static int ealayer3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start) {
int ok, i;
@ -657,6 +659,9 @@ static int ealayer3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, i
uint8_t ibuf[EALAYER3_EA_FRAME_BUFFER_SIZE];
int skips = at_start ? num_stream : data->streams_size - 1 - num_stream;
/* v1 does multichannel with set offsets */
if (data->type == MPEG_EAL31)
return 1;
for (i = 0; i < skips; i++) {
is.buf = ibuf;

View File

@ -131,6 +131,8 @@ mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, co
memcpy(&data->config, config, sizeof(mpeg_custom_config));
data->config.channels = channels;
data->default_buffer_size = MPEG_DATA_BUFFER_SIZE;
/* init per subtype */
switch(data->type) {
case MPEG_EAL31:
@ -145,6 +147,7 @@ mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, co
if (channels <= 0 || channels > 16) goto fail; /* arbitrary max */
if (channels < data->channels_per_frame) goto fail;
if (data->default_buffer_size > 0x8000) goto fail;
/* init streams */
@ -161,7 +164,7 @@ mpeg_codec_data *init_mpeg_custom(STREAMFILE *streamFile, off_t start_offset, co
if (!data->streams[i]->output_buffer) goto fail;
/* one per stream as sometimes mpg123 can't read the whole buffer in one pass */
data->streams[i]->buffer_size = MPEG_DATA_BUFFER_SIZE;
data->streams[i]->buffer_size = data->default_buffer_size;
data->streams[i]->buffer = calloc(sizeof(uint8_t), data->streams[i]->buffer_size);
if (!data->streams[i]->buffer) goto fail;
}

View File

@ -198,7 +198,9 @@ static const char* extension_list[] = {
//"mpc", //FFmpeg, not parsed (musepack) //common
"mpdsp",
"mpds",
"ms",
"msa",
"msb",
"msd",
"msf",
"mss",
@ -241,7 +243,7 @@ static const char* extension_list[] = {
"ps2stm", //fake extension for .stm (to be removed)
"psh", // fake extension for VSV(?) Dawn of Mana needs to be checked again
"psnd",
"psw",
"psw", //fake extension for .wam
"r",
"rac", //txth/reserved [Manhunt (Xbox)]
@ -292,6 +294,7 @@ static const char* extension_list[] = {
"sgd",
"sgx",
"sl3",
"slb", //txth/reserved [THE Nekomura no Hitobito (PS2)]
"sli",
"smp",
"smpl", //fake extension (to be removed)
@ -303,6 +306,7 @@ static const char* extension_list[] = {
"snr",
"sns",
"snu",
"son",
"spd",
"spm",
"sps",
@ -346,6 +350,7 @@ static const char* extension_list[] = {
"vawx",
"vb",
"vbk",
"vbx", //txth/reserved [THE Taxi 2 (PS2)]
"vds",
"vdm",
"vgs",
@ -682,7 +687,7 @@ static const meta_info meta_info_list[] = {
{meta_AUS, "Capcom AUS Header"},
{meta_RWS, "RenderWare RWS header"},
{meta_EA_1SNH, "Electronic Arts 1SNh/EACS header"},
{meta_SL3, "SL3 Header"},
{meta_SL3, "Atari Melbourne House SL3 header"},
{meta_FSB1, "FMOD Sample Bank (FSB1) Header"},
{meta_FSB2, "FMOD Sample Bank (FSB2) Header"},
{meta_FSB3, "FMOD Sample Bank (FSB3) Header"},
@ -719,7 +724,6 @@ static const meta_info meta_info_list[] = {
{meta_SCD_PCM, "Lunar: Eternal Blue .PCM header"},
{meta_PS2_PCM, "Konami KCEJ East .PCM header"},
{meta_PS2_RKV, "Legacy of Kain - Blood Omen 2 RKV PS2 header"},
{meta_PS2_PSW, "Rayman Raving Rabbids Riff Container File"},
{meta_PS2_VAS, "Pro Baseball Spirits 5 VAS Header"},
{meta_PS2_TEC, "assumed TECMO badflagged stream by .tec extension"},
{meta_XBOX_WVS, "Metal Arms WVS Header (XBOX)"},
@ -764,7 +768,7 @@ static const meta_info meta_info_list[] = {
{meta_FFXI_SPW, "SPW SeWave header"},
{meta_PS2_ASS, "ASS Header"},
{meta_IDSP, "IDSP Header"},
{meta_WAA_WAC_WAD_WAM, "WAA/WAC/WAD/WAM RIFF Header"},
{meta_UBI_JADE, "Ubisoft Jade RIFF header"},
{meta_PS2_SEG, "SEG (PS2) Header"},
{meta_XBOX_SEG, "SEG (XBOX) Header"},
{meta_NDS_STRM_FFTA2, "Final Fantasy Tactics A2 RIFF Header"},
@ -790,7 +794,6 @@ static const meta_info meta_info_list[] = {
{meta_NGC_DSP_IADP, "IADP Header"},
{meta_RSTM_shrunken, "Nintendo RSTM header, corrupted by Atlus"},
{meta_RIFF_WAVE_MWV, "RIFF WAVE header with .mwv flavoring"},
{meta_RIFF_WAVE_SNS, "RIFF WAVE header with .sns flavoring"},
{meta_FFCC_STR, "Final Fantasy: Crystal Chronicles STR header"},
{meta_SAT_BAKA, "BAKA header from Crypt Killer"},
{meta_NDS_SWAV, "SWAV Header"},
@ -984,6 +987,9 @@ static const meta_info meta_info_list[] = {
{meta_MP4, "MP4/AAC header"},
{meta_PCM_SRE, "Capcom .PCM+SRE header"},
{meta_DSP_MCADPCM, "Bethesda .mcadpcm header"},
{meta_UBI_LYN, "Ubisoft LyN RIFF header"},
{meta_MSB_MSH, "Sony MSB+MSH header"},
{meta_OGG_RPGMV, "Ogg Vorbis (RPGMV header)"},
#ifdef VGM_USE_FFMPEG
{meta_FFmpeg, "FFmpeg supported file format"},

View File

@ -126,14 +126,22 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
break;
#ifdef VGM_USE_MPEG
/* id, size, samples, offset?, unknown (null for MP2, some constant for all blocks for EALayer3) */
/* id, size, samples, offsets, unknown (null for MP2, some size/config for EALayer3; only if not >2ch) */
case coding_MPEG_custom:
case coding_MPEG_layer1:
case coding_MPEG_layer2:
case coding_MPEG_layer3:
case coding_MPEG_ealayer3:
for (i = 0; i < vgmstream->channels; i++) {
off_t channel_start = read_32bit(block_offset + 0x0C,streamFile);
off_t channel_start;
/* EALayer3 6ch uses 1ch*6 with offsets, no flag in header [Medal of Honor 2010 (PC) movies] */
if (vgmstream->channels > 2) {
channel_start = read_32bit(block_offset + 0x0C + 0x04*i,streamFile);
} else {
channel_start = read_32bit(block_offset + 0x0C,streamFile);
}
vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start;
}

View File

@ -231,6 +231,10 @@
<File
RelativePath=".\meta\sqex_scd_streamfile.h"
>
</File>
<File
RelativePath=".\meta\ubi_lyn_ogg_streamfile.h"
>
</File>
</Filter>
<Filter
@ -930,6 +934,14 @@
RelativePath=".\meta\ps2_msa.c"
>
</File>
<File
RelativePath=".\meta\mp4.c"
>
</File>
<File
RelativePath=".\meta\msb_msh.c"
>
</File>
<File
RelativePath=".\meta\mss.c"
>
@ -958,10 +970,6 @@
RelativePath=".\meta\ps2_psh.c"
>
</File>
<File
RelativePath=".\meta\ps2_psw.c"
>
</File>
<File
RelativePath=".\meta\ps2_rnd.c"
>
@ -1278,6 +1286,10 @@
RelativePath=".\meta\ubi_ckd.c"
>
</File>
<File
RelativePath=".\meta\ubi_lyn.c"
>
</File>
<File
RelativePath=".\meta\ubi_raki.c"
>
@ -1307,7 +1319,7 @@
>
</File>
<File
RelativePath=".\meta\waa_wac_wad_wam.c"
RelativePath=".\meta\ubi_jade.c"
>
</File>
<File

View File

@ -100,6 +100,7 @@
<ClInclude Include="meta\bar_streamfile.h" />
<ClInclude Include="meta\ea_eaac_eatrax_streamfile.h" />
<ClInclude Include="meta\sqex_scd_streamfile.h" />
<ClInclude Include="meta\ubi_lyn_ogg_streamfile.h" />
<ClInclude Include="meta\meta.h" />
<ClInclude Include="meta\hca_keys.h" />
<ClInclude Include="meta\fsb_keys.h" />
@ -143,6 +144,7 @@
<ClCompile Include="meta\mn_str.c" />
<ClCompile Include="meta\mogg.c" />
<ClCompile Include="meta\mp4.c" />
<ClCompile Include="meta\msb_msh.c" />
<ClCompile Include="meta\ngca.c" />
<ClCompile Include="meta\nsw_opus.c" />
<ClCompile Include="meta\nub_vag.c" />
@ -334,7 +336,6 @@
<ClCompile Include="meta\ps2_pcm.c" />
<ClCompile Include="meta\ps2_pnb.c" />
<ClCompile Include="meta\ps2_psh.c" />
<ClCompile Include="meta\ps2_psw.c" />
<ClCompile Include="meta\ps2_rnd.c" />
<ClCompile Include="meta\ps2_rstm.c" />
<ClCompile Include="meta\ps2_rxws.c" />
@ -400,13 +401,14 @@
<ClCompile Include="meta\thp.c" />
<ClCompile Include="meta\vgs.c" />
<ClCompile Include="meta\ubi_ckd.c" />
<ClCompile Include="meta\ubi_lyn.c" />
<ClCompile Include="meta\ubi_raki.c" />
<ClCompile Include="meta\ubi_sb.c" />
<ClCompile Include="meta\vs.c" />
<ClCompile Include="meta\vsf.c" />
<ClCompile Include="meta\vsf_tta.c" />
<ClCompile Include="meta\vxn.c" />
<ClCompile Include="meta\waa_wac_wad_wam.c" />
<ClCompile Include="meta\ubi_jade.c" />
<ClCompile Include="meta\waf.c" />
<ClCompile Include="meta\wave_segmented.c" />
<ClCompile Include="meta\wave.c" />

View File

@ -80,6 +80,9 @@
<ClInclude Include="meta\sqex_scd_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\ubi_lyn_ogg_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\meta.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
@ -583,9 +586,6 @@
<ClCompile Include="meta\ps2_psh.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_psw.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ps2_rnd.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -781,6 +781,9 @@
<ClCompile Include="meta\ubi_ckd.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ubi_lyn.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ubi_raki.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -799,7 +802,7 @@
<ClCompile Include="meta\vxn.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\waa_wac_wad_wam.c">
<ClCompile Include="meta\ubi_jade.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\waf.c">
@ -1231,6 +1234,9 @@
<ClCompile Include="meta\mp4.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\msb_msh.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\mp4_aac_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>

View File

@ -221,8 +221,6 @@ VGMSTREAM * init_vgmstream_ps2_pcm(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_rkv(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_psw(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_vas(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_tec(STREAMFILE * streamFile);
@ -319,7 +317,8 @@ VGMSTREAM * init_vgmstream_spw(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ps2_ass(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_waa_wac_wad_wam(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ubi_jade_container(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_seg(STREAMFILE * streamFile);
@ -730,4 +729,9 @@ VGMSTREAM * init_vgmstream_pcm_sre(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_msb_msh(STREAMFILE * streamFile);
#endif /*_META_H*/

86
src/meta/msb_msh.c Normal file
View File

@ -0,0 +1,86 @@
#include "meta.h"
#include "../coding/coding.h"
/* MSB+MSH - Sony sfx container companion of MIH+MIB [namCollection - Ace Combat 2 (PS2) sfx, EyeToy Play (PS2)] */
VGMSTREAM * init_vgmstream_msb_msh(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamHeader = NULL;
off_t start_offset, header_offset = 0;
size_t stream_size;
int loop_flag = 0, channel_count, sample_rate;
int total_subsongs, target_subsong = streamFile->stream_index;
/* checks */
if (!check_extensions(streamFile, "msb"))
goto fail;
streamHeader = open_streamfile_by_ext(streamFile, "msh");
if (!streamHeader) goto fail;
if (read_32bitLE(0x00,streamHeader) != get_streamfile_size(streamHeader))
goto fail;
/* 0x04: unknown */
/* parse entries */
{
int i;
int entries = read_32bitLE(0x08,streamHeader);
total_subsongs = 0;
if (target_subsong == 0) target_subsong = 1;
for (i = 0; i < entries; i++) {
if (read_32bitLE(0x0c + 0x10*i, streamHeader) == 0) /* size 0 = empty entry */
continue;
total_subsongs++;
if (total_subsongs == target_subsong && !header_offset) {
header_offset = 0x0c + 0x10*i;
}
}
if (!header_offset) goto fail;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
}
loop_flag = 0;
channel_count = 1;
stream_size = read_32bitLE(header_offset+0x00, streamHeader);
if (read_32bitLE(header_offset+0x04, streamHeader) != 0) /* stereo flag? */
goto fail;
start_offset = read_32bitLE(header_offset+0x08, streamHeader);
sample_rate = read_32bitLE(header_offset+0x0c, streamHeader); /* Ace Combat 2 seems to set wrong values but probably their bug */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = ps_bytes_to_samples(stream_size,channel_count);
vgmstream->num_streams = total_subsongs;
vgmstream->stream_size = stream_size;
vgmstream->meta_type = meta_MSB_MSH;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10;
close_streamfile(streamHeader);
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_streamfile(streamHeader);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -176,6 +176,29 @@ static void l2sd_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, v
}
}
static void rpgmvo_ogg_decryption_callback(void *ptr, size_t size, size_t nmemb, void *datasource) {
static const uint8_t header[16] = { /* OggS, packet type, granule, stream id(empty) */
0x4F,0x67,0x67,0x53,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};
size_t bytes_read = size*nmemb;
ogg_vorbis_streamfile * const ov_streamfile = datasource;
int i;
/* first 0x10 are xor'd with a key, but the header can be easily reconstructed
* (key is also in (game)/www/data/System.json "encryptionKey") */
for (i = 0; i < bytes_read; i++) {
if (ov_streamfile->offset+i < 0x10) {
((uint8_t*)ptr)[i] = header[(ov_streamfile->offset + i) % 16];
/* last two bytes are the stream id, get from next OggS */
if (ov_streamfile->offset+i == 0x0e)
((uint8_t*)ptr)[i] = read_8bit(0x58, ov_streamfile->streamfile);
if (ov_streamfile->offset+i == 0x0f)
((uint8_t*)ptr)[i] = read_8bit(0x59, ov_streamfile->streamfile);
}
}
}
/* Ogg Vorbis, by way of libvorbisfile; may contain loop comments */
VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
@ -189,6 +212,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
int is_sngw = 0;
int is_isd = 0;
int is_l2sd = 0;
int is_rpgmvo = 0;
/* check extension */
@ -206,6 +230,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
is_sngw = 1;
} else if (check_extensions(streamFile,"isd")) { /* .isd: Azure Striker Gunvolt (PC) */
is_isd = 1;
} else if (check_extensions(streamFile,"rpgmvo")) { /* .rpgmvo: RPG Maker MV games (PC) */
is_rpgmvo = 1;
} else {
goto fail;
}
@ -267,6 +293,18 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
// 0x0c(2): PCM block size, 0x0e(2): PCM bps, 0x10: null, 0x18: samples (in PCM bytes)
}
/* check RPGMKVO (RPG Maker MV), header + minor encryption */
if (is_rpgmvo) {
if (read_32bitBE(0x00,streamFile) != 0x5250474D && /* "RPGM" */
read_32bitBE(0x00,streamFile) != 0x56000000) { /* "V\0\0\0" */
goto fail;
}
ovmi.decryption_callback = rpgmvo_ogg_decryption_callback;
start_offset = 0x10;
}
if (is_um3) {
ovmi.meta_type = meta_OGG_UM3;
@ -280,6 +318,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
ovmi.meta_type = meta_OGG_ISD;
} else if (is_l2sd) {
ovmi.meta_type = meta_OGG_L2SD;
} else if (is_rpgmvo) {
ovmi.meta_type = meta_OGG_RPGMV;
} else {
ovmi.meta_type = meta_OGG_VORBIS;
}

View File

@ -1,88 +0,0 @@
#include "meta.h"
#include "../util.h"
/* PSW (from Rayman Raving Rabbids)
...coefs are missing for the dsp type... */
VGMSTREAM * init_vgmstream_ps2_psw(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("psw",filename_extension(filename))) goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x52494646 && /* "RIFF" */
read_32bitBE(0x08,streamFile) != 0x57415645 && /* "WAVE" */
read_32bitBE(0x26,streamFile) != 0x64617461) /* "data" */
goto fail;
loop_flag = 0;
channel_count = read_16bitLE(0x16,streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
switch ((uint16_t)read_16bitBE(0x14,streamFile)) {
case 0xFFFF:
start_offset = 0x2E;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_16bitLE(0x1C,streamFile);
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = read_32bitLE(0x2A,streamFile)*28/16/channel_count;
if (loop_flag) {
vgmstream->loop_start_sample = loop_flag;
vgmstream->loop_end_sample = read_32bitLE(0x2A,streamFile)*28/16/channel_count;
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x6400;
vgmstream->meta_type = meta_PS2_PSW;
break;
case 0xFEFF:
start_offset = 0x2E;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_16bitLE(0x1C,streamFile);
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->num_samples = read_32bitLE(0x2A,streamFile)*28/16/channel_count;
if (loop_flag) {
vgmstream->loop_start_sample = loop_flag;
vgmstream->loop_end_sample = read_32bitLE(0x2A,streamFile)*28/16/channel_count;
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x12C00;
vgmstream->meta_type = meta_PS2_PSW;
break;
default:
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;
}
}
return vgmstream;
/* clean up anything we may have opened */
fail:
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,65 +1,45 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* SL3 (from Test Drive Unlimited, Transformers) */
/* SL3 - Atari Melbourne House games [ Test Drive Unlimited (PS2), Transformers (PS2)] */
VGMSTREAM * init_vgmstream_sl3(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag = 0, channel_count;
int loop_flag = 0;
int channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("sl3",filename_extension(filename))) goto fail;
/* check header */
/* checks */
/* .ms: actual extension, sl3: header id */
if (!check_extensions(streamFile, "ms,sl3"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x534C3300) /* "SL3\0" */
goto fail;
loop_flag = 0;
channel_count = read_32bitLE(0x14,streamFile);
start_offset = 0x8000; /* also at 0x24? */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* fill in the vital statistics */
start_offset = 0x8000;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x18,streamFile);
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = (get_streamfile_size(streamFile)-0x8000)*28/16/channel_count;
vgmstream->num_samples = ps_bytes_to_samples(get_streamfile_size(streamFile)-start_offset,channel_count);
if (loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = read_32bitLE(0x1C,streamFile);
}
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = read_32bitLE(0x20,streamFile);
vgmstream->meta_type = meta_SL3;
/* 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:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -1,6 +1,5 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* TK5 (Tekken 5 Streams) */
VGMSTREAM * init_vgmstream_ps2_tk5(STREAMFILE *streamFile) {
@ -65,65 +64,46 @@ fail:
return NULL;
}
/* TK1 (Tekken 5 Streams from Tekken (NamCollection)) */
/* OVB - Tekken 5 Streams from Tekken (NamCollection) */
VGMSTREAM * init_vgmstream_ps2_tk1(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t start_offset;
int loop_flag = 0;
int channel_count;
int loop_flag = 0, channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("tk1",filename_extension(filename))) goto fail;
/* checks */
/* .ovb: actual extension, tk1: fake extension */
if (!check_extensions(streamFile, "ovb,tk1"))
goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x544B3553)
goto fail;
loop_flag = (read_32bitLE(0x0C,streamFile)!=0);
channel_count = 2;
start_offset = 0x800;
/* NamCollection uses 44100 while Tekken 5 48000, no apparent way to tell them apart */
/* 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->num_samples = ps_bytes_to_samples(read_32bitLE(0x08,streamFile)*channel_count, channel_count);
if (vgmstream->loop_flag) {
vgmstream->loop_start_sample = read_32bitLE(0x08,streamFile)/16*28;
vgmstream->loop_end_sample = vgmstream->loop_start_sample + ps_bytes_to_samples(read_32bitLE(0x0c,streamFile)*channel_count, channel_count);
}
vgmstream->coding_type = coding_PSX_badflags;
vgmstream->num_samples = read_32bitLE(0x08,streamFile)/16*28;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x10;
vgmstream->meta_type = meta_PS2_TK1;
if (vgmstream->loop_flag)
{
vgmstream->loop_start_sample = read_32bitLE(0x08,streamFile)/16*28;
vgmstream->loop_end_sample = vgmstream->loop_start_sample + (read_32bitLE(0x0C,streamFile)/16*28);
}
/* 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:
if (vgmstream) close_vgmstream(vgmstream);
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -90,7 +90,7 @@ typedef struct {
int interleave;
} riff_fmt_chunk;
static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk, riff_fmt_chunk * fmt, int sns, int mwv) {
static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk, riff_fmt_chunk * fmt, int mwv) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE;
int16_t (*read_16bit)(off_t,STREAMFILE*) = big_endian ? read_16bitBE : read_16bitLE;
@ -143,7 +143,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk
fmt->coding_type = coding_AICA;
break;
case 0x69: /* XBOX IMA ADPCM [Dynasty Warriors 5 (Xbox), Rayman Raving Rabbids 2 (PC) --maybe waa/wac/wam/wad?] */
case 0x69: /* XBOX IMA ADPCM [Dynasty Warriors 5 (Xbox)] */
if (fmt->bps != 4) goto fail;
fmt->coding_type = coding_XBOX_IMA;
break;
@ -167,12 +167,6 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk
fmt->interleave = 0x12;
break;
case 0x5050: /* Ubisoft LyN engine's DSP (unofficial) */
if (!sns) goto fail;
fmt->coding_type = coding_NGC_DSP;
fmt->interleave = 0x08;
break;
#ifdef VGM_USE_VORBIS
case 0x6771: /* Ogg Vorbis (mode 3+) */
fmt->coding_type = coding_OGG_VORBIS;
@ -221,7 +215,7 @@ static int read_fmt(int big_endian, STREAMFILE * streamFile, off_t current_chunk
#endif
}
break;
goto fail;
default:
goto fail;
@ -253,7 +247,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
int mwv = 0; /* Level-5 .mwv (Dragon Quest VIII, Rogue Galaxy) */
off_t mwv_pflt_offset = -1;
off_t mwv_ctrl_offset = -1;
int sns = 0; /* Ubisoft .sns LyN engine (Red Steel 2, Just Dance 3) */
int at3 = 0; /* Sony ATRAC3 / ATRAC3plus */
int at9 = 0; /* Sony ATRAC9 */
@ -269,9 +262,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
else if ( check_extensions(streamFile, "mwv") ) {
mwv = 1;
}
else if ( check_extensions(streamFile, "sns") ) {
sns = 1;
}
/* .rws: Climax games (Silent Hill Origins PSP, Oblivion PSP), .aud: EA Replay */
else if ( check_extensions(streamFile, "at3,rws,aud") ) {
at3 = 1;
@ -329,7 +319,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
streamFile,
current_chunk,
&fmt,
sns,
mwv))
goto fail;
@ -395,8 +384,8 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
case 0x66616374: /* "fact" */
if (chunk_size == 0x04) { /* standard, usually found with ADPCM */
fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile);
} else if (sns && chunk_size == 0x10) {
fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile);
} else if (chunk_size == 0x10 && read_32bitBE(current_chunk+0x08+0x04, streamFile) == 0x4C794E20) { /* "LyN " */
goto fail; /* parsed elsewhere */
} else if ((at3 || at9) && chunk_size == 0x08) {
fact_sample_count = read_32bitLE(current_chunk+0x08, streamFile);
fact_sample_skip = read_32bitLE(current_chunk+0x0c, streamFile);
@ -441,6 +430,14 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
&& (fmt.coding_type==coding_MSADPCM /*|| fmt.coding_type==coding_MS_IMA*/ || fmt.coding_type==coding_XBOX_IMA))
goto fail;
/* ignore Beyond Good & Evil HD PS3 evil reuse of PCM codec */
if (fmt.coding_type == coding_PCM16LE &&
read_32bitBE(start_offset+0x00, streamFile) == 0x4D534643 && /* "MSF\43" */
read_32bitBE(start_offset+0x34, streamFile) == 0xFFFFFFFF && /* always */
read_32bitBE(start_offset+0x38, streamFile) == 0xFFFFFFFF &&
read_32bitBE(start_offset+0x3c, streamFile) == 0xFFFFFFFF)
goto fail;
#ifdef VGM_USE_VORBIS
/* special case using init_vgmstream_ogg_vorbis */
@ -515,29 +512,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
vgmstream->num_samples = fact_sample_count; /* some (converted?) Xbox games have bigger fact_samples */
break;
case coding_NGC_DSP:
if (!sns) goto fail;
if (fact_sample_count <= 0) goto fail;
vgmstream->num_samples = fact_sample_count;
//vgmstream->num_samples = dsp_bytes_to_samples(data_size, fmt.channel_count);
/* coefs */
{
int i, ch;
static const int16_t coef[16] = { /* common codebook? */
0x04ab,0xfced,0x0789,0xfedf,0x09a2,0xfae5,0x0c90,0xfac1,
0x084d,0xfaa4,0x0982,0xfdf7,0x0af6,0xfafa,0x0be6,0xfbf5
};
for (ch = 0; ch < fmt.channel_count; ch++) {
for (i = 0; i < 16; i++) {
vgmstream->ch[ch].adpcm_coef[i] = coef[i];
}
}
}
break;
#ifdef VGM_USE_FFMPEG
case coding_FFmpeg: {
ffmpeg_codec_data *ffmpeg_data = init_ffmpeg_offset(streamFile, 0x00, streamFile->get_size(streamFile));
@ -649,9 +623,6 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
if (mwv) {
vgmstream->meta_type = meta_RIFF_WAVE_MWV;
}
if (sns) {
vgmstream->meta_type = meta_RIFF_WAVE_SNS;
}
if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
@ -714,7 +685,6 @@ VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) {
streamFile,
current_chunk,
&fmt,
0, /* sns == false */
0)) /* mwv == false */
goto fail;

389
src/meta/ubi_jade.c Normal file
View File

@ -0,0 +1,389 @@
#include "meta.h"
#include "../coding/coding.h"
static STREAMFILE* setup_jade_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext);
static int get_loop_points(STREAMFILE *streamFile, int *out_loop_start, int *out_loop_end);
/* Jade RIFF - from Ubisoft Jade engine games [Beyond Good & Evil (multi), Rayman Raving Rabbids 1/2 (multi)] */
VGMSTREAM * init_vgmstream_ubi_jade(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, first_offset = 0xc;
off_t fmt_offset, data_offset;
size_t fmt_size, data_size;
int loop_flag, channel_count, sample_rate, codec, block_size;
int loop_start = 0, loop_end = 0;
int is_jade_v2 = 0;
/* checks */
/* .waa: ambiances, .wam: music, .wac: sfx, .wad: dialogs (usually)
* .wav: Beyond Good & Evil HD (PS3), .psw: fake/badly extracted names [ex. Rayman Raving Rabbids (PS2)] */
if (!check_extensions(streamFile,"waa,wac,wad,wam,wav,lwav,psw"))
goto fail;
/* a slightly twisted RIFF with custom codecs */
if (read_32bitBE(0x00,streamFile) != 0x52494646 || /* "RIFF" */
read_32bitBE(0x08,streamFile) != 0x57415645) /* "WAVE" */
goto fail;
if (check_extensions(streamFile,"psw")) { /* .psw are incorrectly extracted missing 0x04 at the end */
if (read_32bitLE(0x04,streamFile)+0x04 != get_streamfile_size(streamFile))
goto fail;
}
else {
if (read_32bitLE(0x04,streamFile)+0x04+0x04 != get_streamfile_size(streamFile))
goto fail;
}
if (!find_chunk(streamFile, 0x666d7420,first_offset,0, &fmt_offset,&fmt_size, 0, 0)) /* "fmt " */
goto fail;
if (!find_chunk(streamFile, 0x64617461,first_offset,0, &data_offset,&data_size, 0, 0)) /* "data" */
goto fail;
/* parse format */
{
if (fmt_size < 0x10)
goto fail;
codec = (uint16_t)read_16bitLE(fmt_offset+0x00,streamFile);
channel_count = read_16bitLE(fmt_offset+0x02,streamFile);
sample_rate = read_32bitLE(fmt_offset+0x04,streamFile);
block_size = (uint16_t)read_16bitLE(fmt_offset+0x0c,streamFile);
/* 0x08: average bytes, 0x0e: bps, etc */
/* autodetect Jade "v2", uses a different interleave [Rayman Raving Rabbids (PS2/Wii)] */
switch(codec) {
case 0xFFFF: { /* PS2 */
int i;
/* half interleave check as there is no flag (ends with the PS-ADPCM stop frame) */
for (i = 0; i < channel_count; i++) {
off_t end_frame = data_offset + (data_size / channel_count) * (i+1) - 0x10;
if (read_32bitBE(end_frame+0x00,streamFile) != 0x07007777 ||
read_32bitBE(end_frame+0x04,streamFile) != 0x77777777 ||
read_32bitBE(end_frame+0x08,streamFile) != 0x77777777 ||
read_32bitBE(end_frame+0x0c,streamFile) != 0x77777777) {
is_jade_v2 = 1;
break;
}
}
break;
}
case 0xFFFE: /* GC/Wii */
is_jade_v2 = (read_16bitLE(fmt_offset+0x10,streamFile) == 0); /* extra data size (0x2e*channels) */
break;
}
/* hopefully catches PC Rabbids */
if (find_chunk(streamFile, 0x63756520,first_offset,0, NULL,NULL, 0, 0)) { /* "cue " */
is_jade_v2 = 1;
}
}
/* get loop points */
if (is_jade_v2) {
loop_flag = get_loop_points(streamFile, &loop_start, &loop_end); /* loops in "LIST" */
}
else {
/* BG&E files don't contain looping information, so the looping is done by extension.
* wam and waa contain ambient sounds and music, so often they contain looped music.
* Later, if the file is too short looping will be disabled. */
loop_flag = check_extensions(streamFile,"waa,wam");
}
start_offset = data_offset;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->meta_type = meta_UBI_JADE;
if (is_jade_v2) {
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
}
switch(codec) {
case 0x0069: /* Xbox */
if (block_size != 0x24*channel_count)
goto fail;
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, channel_count);
if (!is_jade_v2) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
}
break;
case 0xFFFF: /* PS2 */
if (block_size != 0x10)
goto fail;
vgmstream->coding_type = coding_PSX;
vgmstream->layout_type = layout_interleave;
if (is_jade_v2) {
vgmstream->interleave_block_size = 0x6400;
if (vgmstream->interleave_block_size)
vgmstream->interleave_last_block_size = (data_size % (vgmstream->interleave_block_size*vgmstream->channels)) / vgmstream->channels;
}
else {
vgmstream->interleave_block_size = data_size / channel_count;
}
vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count);
if (!is_jade_v2) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
}
break;
case 0xFFFE: /* GC/Wii */
if (block_size != 0x08)
goto fail;
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count);
if (!is_jade_v2) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
}
/* coefs / interleave */
if (is_jade_v2) {
vgmstream->interleave_block_size = 0x6400;
if (vgmstream->interleave_block_size)
vgmstream->interleave_last_block_size = ((data_size % (vgmstream->interleave_block_size*vgmstream->channels))/2+7)/8*8;
{
static const int16_t coef[16] = { /* default Ubisoft coefs, from ELF */
0x04ab,0xfced,0x0789,0xfedf,0x09a2,0xfae5,0x0c90,0xfac1,
0x084d,0xfaa4,0x0982,0xfdf7,0x0af6,0xfafa,0x0be6,0xfbf5
};
int i, ch;
for (ch = 0; ch < channel_count; ch++) {
for (i = 0; i < 16; i++) {
vgmstream->ch[ch].adpcm_coef[i] = coef[i];
}
}
}
}
else {
/* has extra 0x2e coefs before each channel, not counted in data_size */
vgmstream->interleave_block_size = (data_size + 0x2e*channel_count) / channel_count;
dsp_read_coefs_be(vgmstream, streamFile, start_offset+0x00, vgmstream->interleave_block_size);
dsp_read_hist_be (vgmstream, streamFile, start_offset+0x20, vgmstream->interleave_block_size);
start_offset += 0x2e;
}
break;
case 0x0002: /* PC */
if (block_size != 0x24*channel_count)
goto fail;
vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = 0x24*channel_count;
vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, vgmstream->interleave_block_size, channel_count);
if (!is_jade_v2) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
}
break;
case 0x0001: { /* PS3 */
VGMSTREAM *temp_vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
if (block_size != 0x02*channel_count)
goto fail;
/* a MSF (usually ATRAC3) masquerading as PCM */
if (read_32bitBE(start_offset, streamFile) != 0x4D534643) /* "MSF\43" */
goto fail;
temp_streamFile = setup_jade_streamfile(streamFile, start_offset, data_size, "msf");
if (!temp_streamFile) goto fail;
temp_vgmstream = init_vgmstream_ps3_msf(temp_streamFile);
close_streamfile(temp_streamFile);
if (!temp_vgmstream) goto fail;
temp_vgmstream->meta_type = vgmstream->meta_type;
close_vgmstream(vgmstream);
return temp_vgmstream;
}
default: /* X360 uses .XMA */
goto fail;
}
/* V1 loops by extension, try to detect incorrectly looped jingles (too short) */
if (!is_jade_v2) {
if(loop_flag
&& vgmstream->num_samples < 15*sample_rate) { /* in seconds */
vgmstream->loop_flag = 0;
}
}
if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* extract loops from "cue /LIST", returns if loops (info from Droolie) */
static int get_loop_points(STREAMFILE *streamFile, int *out_loop_start, int *out_loop_end) {
off_t cue_offset, list_offset;
size_t cue_size, list_size;
off_t offset, first_offset = 0x0c;
int i, cue_count, loop_id = 0, loop_start = 0, loop_end = 0;
/* unlooped files may contain LIST, but also may not */
if (!find_chunk(streamFile, 0x63756520,first_offset,0, &cue_offset,&cue_size, 0, 0)) /* "cue " */
goto fail;
if (!find_chunk(streamFile, 0x4C495354,first_offset,0, &list_offset,&list_size, 0, 0)) /* "LIST" */
goto fail;
offset = list_offset + 0x04;
while (offset < list_offset + list_size) {
uint32_t chunk_id = read_32bitBE(offset+0x00, streamFile);
uint32_t chunk_size = read_32bitLE(offset+0x04, streamFile);
offset += 0x08;
switch(chunk_id) {
case 0x6C61626C: /* "labl" */
if (read_32bitBE(offset+0x04, streamFile) == 0x6C6F6F70) /* "loop", actually an string tho */
loop_id = read_32bitLE(offset+0x00, streamFile);
chunk_size += (chunk_size % 2) ? 1 : 0; /* string is even-padded after size */
break;
case 0x6C747874: /* "ltxt" */
if (loop_id == read_32bitLE(offset+0x00, streamFile))
loop_end = read_32bitLE(offset+0x04, streamFile);
break;
default:
VGM_LOG("Jade: unknown LIST chunk at %lx\n", offset);
goto fail;
}
offset += chunk_size;
}
if (!loop_end)
return 0;
cue_count = read_32bitLE(cue_offset+0x00, streamFile);
for (i = 0; i < cue_count; i++) {
if (loop_id == read_32bitLE(cue_offset+0x04 + i*0x18 + 0x00, streamFile)) {
loop_start = read_32bitLE(cue_offset+0x04 + i*0x18 + 0x04, streamFile);
loop_end += loop_start;
break;
}
}
*out_loop_start = loop_start;
*out_loop_end = loop_end;
return 1;
fail:
return 0;
}
/* Jade RIFF in containers */
VGMSTREAM * init_vgmstream_ubi_jade_container(STREAMFILE *streamFile) {
VGMSTREAM *vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
off_t subfile_offset;
size_t subfile_size;
/* Jade packs files in bigfiles, and once extracted the sound files have extra engine data before
* the RIFF + padding after. Most extractors don't remove the padding correctly, so here we add support. */
/* checks */
/* standard Jade exts + .xma for padded XMA used in Beyond Good & Evil HD (X360) */
if (!check_extensions(streamFile,"waa,wac,wad,wam,wav,lwav,xma"))
goto fail;
if (read_32bitBE(0x04,streamFile) == 0x52494646 &&
read_32bitLE(0x00,streamFile)+0x04 == get_streamfile_size(streamFile)) {
/* data size + RIFF + padding */
subfile_offset = 0x04;
}
else if (read_32bitBE(0x00,streamFile) == 0x52494646 &&
read_32bitLE(0x04,streamFile)+0x04+0x04 < get_streamfile_size(streamFile) &&
(get_streamfile_size(streamFile) + 0x04) % 0x800 == 0) {
/* RIFF + padding with data size removed (bad extraction) */
subfile_offset = 0x00;
}
else if (read_32bitBE(0x04,streamFile) == 0x52494646 &&
read_32bitLE(0x00,streamFile) == get_streamfile_size(streamFile)) {
/* data_size + RIFF + padding - 0x04 (bad extraction) */
subfile_offset = 0x04;
}
else {
goto fail;
}
subfile_size = read_32bitLE(subfile_offset+0x04,streamFile) + 0x04+0x04;
temp_streamFile = setup_jade_streamfile(streamFile, subfile_offset,subfile_size, NULL);
if (!temp_streamFile) goto fail;
if (check_extensions(streamFile,"xma")) {
vgmstream = init_vgmstream_xma(temp_streamFile);
} else {
vgmstream = init_vgmstream_ubi_jade(temp_streamFile);
}
close_streamfile(temp_streamFile);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_vgmstream(vgmstream);
return NULL;
}
static STREAMFILE* setup_jade_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
if (fake_ext) {
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,fake_ext);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
}
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}

293
src/meta/ubi_lyn.c Normal file
View File

@ -0,0 +1,293 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../coding/coding.h"
#include "ubi_lyn_ogg_streamfile.h"
static STREAMFILE* setup_lyn_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size);
/* LyN RIFF - from Ubisoft LyN engine games [Red Steel 2 (Wii), Adventures of Tintin (Multi), From Dust (Multi), Just Dance 3/4 (multi)] */
VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, first_offset = 0xc;
off_t fmt_offset, data_offset, fact_offset;
size_t fmt_size, data_size, fact_size;
int loop_flag, channel_count, sample_rate, codec;
int num_samples;
/* checks */
/* .sns: Red Steel 2, .wav: Tintin, .son: From Dust */
if (!check_extensions(streamFile,"sns,wav,lwav,son"))
goto fail;
/* a slightly eccentric RIFF with custom codecs */
if (read_32bitBE(0x00,streamFile) != 0x52494646 || /* "RIFF" */
read_32bitBE(0x08,streamFile) != 0x57415645) /* "WAVE" */
goto fail;
if (read_32bitLE(0x04,streamFile)+0x04+0x04 != get_streamfile_size(streamFile))
goto fail;
if (!find_chunk(streamFile, 0x666d7420,first_offset,0, &fmt_offset,&fmt_size, 0, 0)) /* "fmt " */
goto fail;
if (!find_chunk(streamFile, 0x64617461,first_offset,0, &data_offset,&data_size, 0, 0)) /* "data" */
goto fail;
/* always found, even with PCM (LyN subchunk seems to contain the engine version, ex. 0x0d/10) */
if (!find_chunk(streamFile, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) /* "fact" */
goto fail;
if (fact_size != 0x10 || read_32bitBE(fact_offset+0x04, streamFile) != 0x4C794E20) /* "LyN " */
goto fail;
num_samples = read_32bitLE(fact_offset+0x00, streamFile);
/* sometimes there is a LySE chunk */
/* parse format */
{
if (fmt_size < 0x12)
goto fail;
codec = (uint16_t)read_16bitLE(fmt_offset+0x00,streamFile);
channel_count = read_16bitLE(fmt_offset+0x02,streamFile);
sample_rate = read_32bitLE(fmt_offset+0x04,streamFile);
/* 0x08: average bytes, 0x0c: block align, 0x0e: bps, etc */
/* fake WAVEFORMATEX, used with > 2ch */
if (codec == 0xFFFE) {
if (fmt_size < 0x28)
goto fail;
/* fake GUID with first value doubling as codec */
codec = read_32bitLE(fmt_offset+0x18,streamFile);
if (read_32bitBE(fmt_offset+0x1c,streamFile) != 0x00001000 &&
read_32bitBE(fmt_offset+0x20,streamFile) != 0x800000AA &&
read_32bitBE(fmt_offset+0x24,streamFile) != 0x00389B71) {
goto fail;
}
}
}
/* most songs simply repeat, loop if it looks long enough */
loop_flag = (num_samples > 20*sample_rate); /* in seconds */
start_offset = data_offset;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->meta_type = meta_UBI_LYN;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
switch(codec) {
case 0x0001: /* PCM */
vgmstream->coding_type = coding_PCM16LE; /* LE even in X360 */
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
break;
case 0x5050: /* DSP (Wii) */
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x08;
/* setup default Ubisoft coefs */
{
static const int16_t coef[16] = {
0x04ab,0xfced,0x0789,0xfedf,0x09a2,0xfae5,0x0c90,0xfac1,
0x084d,0xfaa4,0x0982,0xfdf7,0x0af6,0xfafa,0x0be6,0xfbf5
};
int i, ch;
for (ch = 0; ch < channel_count; ch++) {
for (i = 0; i < 16; i++) {
vgmstream->ch[ch].adpcm_coef[i] = coef[i];
}
}
}
break;
#ifdef VGM_USE_VORBIS
case 0x3157: { /* Ogg (PC), interleaved 1ch */
size_t interleave_size, stride_size;
layered_layout_data* data = NULL;
int i;
if (read_32bitLE(start_offset+0x00,streamFile) != 1) /* id? */
goto fail;
interleave_size = read_32bitLE(start_offset+0x04,streamFile);
stride_size = interleave_size * channel_count;
/* interleave is adjusted so there is no smaller last block, it seems */
vgmstream->coding_type = coding_OGG_VORBIS;
vgmstream->layout_type = layout_layered;
/* init layout */
data = init_layout_layered(channel_count);
if (!data) goto fail;
vgmstream->layout_data = data;
/* open each layer subfile */
for (i = 0; i < channel_count; i++) {
STREAMFILE* temp_streamFile = NULL;
size_t total_size = read_32bitLE(start_offset+0x08 + 0x04*i,streamFile);
off_t layer_offset = start_offset+0x08 + 0x04*channel_count + interleave_size*i;
temp_streamFile = setup_lyn_ogg_streamfile(streamFile, layer_offset, interleave_size, stride_size, total_size);
if (!temp_streamFile) goto fail;
data->layers[i] = init_vgmstream_ogg_vorbis(temp_streamFile);
close_streamfile(temp_streamFile);
if (!data->layers[i]) goto fail;
/* could validate between layers, meh */
}
/* setup layered VGMSTREAMs */
if (!setup_layout_layered(data))
goto fail;
break;
}
#endif
#ifdef VGM_USE_MPEG
case 0x5051: { /* MPEG (PS3/PC), interleaved 1ch */
mpeg_codec_data *mpeg_data = NULL;
mpeg_custom_config cfg = {0};
int i;
if (read_32bitLE(start_offset+0x00,streamFile) != 2) /* id? */
goto fail;
cfg.interleave = read_32bitLE(start_offset+0x04,streamFile);
cfg.chunk_size = read_32bitLE(start_offset+0x08,streamFile);
/* 0x08: frame size, 0x0c: frame per interleave, 0x10: samples per frame */
/* skip seek tables and find actual start */
start_offset += 0x14;
data_size -= 0x14;
for (i = 0; i < channel_count; i++) {
int entries = read_32bitLE(start_offset,streamFile);
start_offset += 0x04 + entries*0x08;
data_size -= 0x04 + entries*0x08;
}
cfg.data_size = data_size;
//todo data parsing looks correct but some files decode a bit wrong at the end (ex. Tintin: Music~Boss~Allan~Victory~02)
mpeg_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_LYN, &cfg);
if (!mpeg_data) goto fail;
vgmstream->codec_data = mpeg_data;
vgmstream->layout_type = layout_none;
break;
}
#endif
#ifdef VGM_USE_FFMPEG
case 0x0166: { /* XMA (X360), standard */
uint8_t buf[0x100];
int bytes;
off_t chunk_offset;
size_t chunk_size, seek_size;
if (read_32bitLE(start_offset+0x00,streamFile) != 3) /* id? */
goto fail;
/* skip standard XMA header + seek table */
chunk_offset = start_offset + 0x04 + 0x04;
chunk_size = read_32bitLE(start_offset + 0x04, streamFile);
seek_size = read_32bitLE(chunk_offset+chunk_size, streamFile);
start_offset += (0x04 + 0x04 + chunk_size + 0x04 + seek_size);
data_size -= (0x04 + 0x04 + chunk_size + 0x04 + seek_size);
bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile, 1);
vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size);
if ( !vgmstream->codec_data ) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
break;
}
#endif
default:
goto fail;
}
if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* LyN RIFF in containers */
VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE *streamFile) {
VGMSTREAM *vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
off_t subfile_offset;
size_t subfile_size;
/* LyN packs files in bigfiles, and once extracted the sound files have extra engine
* data before the RIFF. Might as well support them in case the RIFF wasn't extracted. */
/* checks */
if (!check_extensions(streamFile,"sns,wav,lwav,son"))
goto fail;
/* find "RIFF" position */
if (read_32bitBE(0x00,streamFile) == 0x4C795345 && /* "LySE" */
read_32bitBE(0x14,streamFile) == 0x52494646) { /* "RIFF" */
subfile_offset = 0x14; /* Adventures of Tintin */
}
else if (read_32bitLE(0x00,streamFile)+0x20 == get_streamfile_size(streamFile) &&
read_32bitBE(0x20,streamFile) == 0x52494646) { /* "RIFF" */
subfile_offset = 0x20; /* Red Steel 2, From Dust */
}
else {
goto fail;
}
subfile_size = read_32bitLE(subfile_offset+0x04,streamFile) + 0x04+0x04;
temp_streamFile = setup_lyn_streamfile(streamFile, subfile_offset,subfile_size);
if (!temp_streamFile) goto fail;
vgmstream = init_vgmstream_ubi_lyn(temp_streamFile);
close_streamfile(temp_streamFile);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_vgmstream(vgmstream);
return NULL;
}
static STREAMFILE* setup_lyn_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}

View File

@ -0,0 +1,92 @@
#ifndef _LYN_OGG_STREAMFILE_H_
#define _LYN_OGG_STREAMFILE_H_
#include "../streamfile.h"
typedef struct {
off_t start_physical_offset; /* interleaved data start, for this substream */
size_t interleave_block_size; /* max size that can be read before encountering other substreams */
size_t stride_size; /* step size between interleave blocks (interleave*channels) */
size_t total_size; /* final size of the deinterleaved substream */
} lyn_ogg_io_data;
/* Handles deinterleaving of complete files, skipping portions or other substreams. */
static size_t scd_dsp_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, lyn_ogg_io_data* data) {
size_t total_read = 0;
while (length > 0) {
size_t to_read;
size_t length_available;
off_t block_num;
off_t intrablock_offset;
off_t physical_offset;
block_num = offset / data->interleave_block_size;
intrablock_offset = offset % data->interleave_block_size;
physical_offset = data->start_physical_offset + block_num*data->stride_size + intrablock_offset;
length_available = data->interleave_block_size - intrablock_offset;
if (length < length_available) {
to_read = length;
}
else {
to_read = length_available;
}
if (to_read > 0) {
size_t bytes_read;
bytes_read = read_streamfile(dest, physical_offset, to_read, streamfile);
total_read += bytes_read;
if (bytes_read != to_read) {
return total_read;
}
dest += bytes_read;
offset += bytes_read;
length -= bytes_read;
}
}
return total_read;
}
static size_t scd_dsp_io_size(STREAMFILE *streamfile, lyn_ogg_io_data* data) {
return data->total_size;
}
static STREAMFILE* setup_lyn_ogg_streamfile(STREAMFILE *streamFile, off_t start_offset, size_t interleave_block_size, size_t stride_size, size_t total_size) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
lyn_ogg_io_data io_data = {0};
size_t io_data_size = sizeof(lyn_ogg_io_data);
io_data.start_physical_offset = start_offset;
io_data.interleave_block_size = interleave_block_size;
io_data.stride_size = stride_size;
io_data.total_size = total_size;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_io_streamfile(temp_streamFile, &io_data,io_data_size, scd_dsp_io_read,scd_dsp_io_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,"ogg");
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}
#endif /* _LYN_OGG_STREAMFILE_H_ */

View File

@ -1,182 +0,0 @@
#include "meta.h"
#include "../coding/coding.h"
/*
const short wad_coef[16][2] =
{
{0x4002,0x2003},
{0x2016,0xc600},
{0xC600,0x98ab},
{0x96bf,0x29c5},
{0x2003,0x0081},
{0x0e00,0x2004},
{0x8e01,0xc500},
{0x70bf,0x8128},
{0x288e,0xc600},
{0x016e,0x0e5b},
{0xbe20,0x2003},
{0x03c6,0xc600},
{0x0048,0xe85a},
{0xbe28,0x28c6},
{0xc600,0x00F6},
{0xbeab,0x5520}
};*/
const short wad_coef[16] =
{
0x04ab, 0xfced,
0x0789, 0xfedf,
0x09a2, 0xfae5,
0x0c90, 0xfac1,
0x084d, 0xfaa4,
0x0982, 0xfdf7,
0x0af6, 0xfafa,
0x0be6, 0xfbf5
};
/* WAC/WAD/WAM/WAA - from Beyond Good & Evil (PS2/Xbox/GC/Wii) */
VGMSTREAM * init_vgmstream_waa_wac_wad_wam(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int i;
off_t start_offset;
int loop_flag;
int channel_count;
int coef1_start;
int coef2_start;
int second_channel_start = -1;
// Check file extensions
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("waa",filename_extension(filename)) &&
strcasecmp("wac",filename_extension(filename)) &&
strcasecmp("wad",filename_extension(filename)) &&
strcasecmp("wam",filename_extension(filename))) goto fail;
/* check header */
if (read_32bitBE(0x00,streamFile) != 0x52494646 || /* "RIFF" */
read_32bitBE(0x08,streamFile) != 0x57415645 || /* "WAVE" */
read_32bitBE(0x0C,streamFile) != 0x666D7420 || /* "fmt " */
read_32bitBE(0x10,streamFile) != 0x12000000) /* "0x12000000" */
goto fail;
/* files don't contain looping information,
so the looping is not done depending on extension.
wam and waa contain ambient sounds and music, so often they contain
looped music. Change extension to wac or wad to make the sound non-looping.
*/
loop_flag = strcasecmp("wac",filename_extension(filename)) &&
strcasecmp("wad",filename_extension(filename));
channel_count = (uint16_t)read_16bitLE(0x16,streamFile);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
/* Check what encoder is needed */
//FIXME: //PC version uses pcm, but which encoder?
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x18,streamFile);
vgmstream->meta_type = meta_WAA_WAC_WAD_WAM;
vgmstream->layout_type = layout_none;
switch((uint16_t)read_16bitLE(0x14,streamFile)) {
case 0x0069: // XBOX IMA ADPCM
start_offset = 0x2E;
vgmstream->coding_type = coding_XBOX_IMA;
vgmstream->num_samples = xbox_ima_bytes_to_samples(read_32bitLE(0x2A,streamFile), channel_count);
if (loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = xbox_ima_bytes_to_samples(read_32bitLE(0x2A,streamFile),channel_count);
}
break;
case 0xFFFF: // PS2 ADPCM
start_offset = 0x2E;
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = (read_32bitLE(0x2A,streamFile))/16*28/channel_count;
if (loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = (read_32bitLE(0x2A,streamFile))/16*28/channel_count;
}
second_channel_start = (read_32bitLE(0x2A,streamFile)/2)+start_offset;
break;
case 0xFFFE: // GameCube/WII DSP
start_offset = 0x5C;
vgmstream->coding_type = coding_NGC_DSP;
vgmstream->num_samples = (read_32bitLE(0x2A,streamFile))*14/8/channel_count;
if (loop_flag) {
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = (read_32bitLE(0x2A,streamFile))*14/8/channel_count;
}
if(read_16bitLE(0x24,streamFile)==0x00)//is a wii file with no coeff table
{
//FIXME: WII version of WAM/WAD/WAC need some coeff table from somewhere
for (i=0;i<16;i++)
vgmstream->ch[0].adpcm_coef[i] = wad_coef[i];
if (channel_count == 2) {
for (i=0;i<16;i++)
vgmstream->ch[1].adpcm_coef[i] = wad_coef[i];
}
goto fail;
}
else
{
second_channel_start = (read_32bitLE(0x2A,streamFile)/2)+0x8A;
/* Retrieveing the coef tables */
coef1_start = 0x2E;
coef2_start = (read_32bitLE(0x2A,streamFile)/2)+0x5C;
{
int i;
for (i=0;i<16;i++)
vgmstream->ch[0].adpcm_coef[i] = read_16bitBE(coef1_start+i*2,streamFile);
if (channel_count == 2) {
for (i=0;i<16;i++)
vgmstream->ch[1].adpcm_coef[i] = read_16bitBE(coef2_start+i*2,streamFile);
}
}
}
break;
default:
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;
if (vgmstream->coding_type == coding_XBOX_IMA) {
/* xbox interleaving is a little odd */
vgmstream->ch[i].channel_start_offset=start_offset;
} else {
vgmstream->ch[0].channel_start_offset=start_offset;
if (channel_count == 2) {
if (second_channel_start == -1) goto fail;
vgmstream->ch[1].channel_start_offset=second_channel_start;
}
}
vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset;
}
}
return vgmstream;
fail:
/* clean up anything we may have opened */
if (vgmstream) close_vgmstream(vgmstream);
return NULL;
}

View File

@ -78,6 +78,18 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
}
#endif
/* ignore LyN RIFF */
{
off_t fact_offset;
size_t fact_size;
if (find_chunk(streamFile, 0x66616374,first_offset,0, &fact_offset,&fact_size, 0, 0)) { /* "fact" */
if (fact_size == 0x10 && read_32bitBE(fact_offset+0x04, streamFile) == 0x4C794E20) /* "LyN " */
goto fail; /* parsed elsewhere */
/* Wwise doesn't use "fact", though */
}
}
/* parse format (roughly spec-compliant but some massaging is needed) */
{

View File

@ -119,7 +119,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_scd_pcm,
init_vgmstream_ps2_pcm,
init_vgmstream_ps2_rkv,
init_vgmstream_ps2_psw,
init_vgmstream_ps2_vas,
init_vgmstream_ps2_tec,
init_vgmstream_ps2_enth,
@ -178,7 +177,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_bgw,
init_vgmstream_spw,
init_vgmstream_ps2_ass,
init_vgmstream_waa_wac_wad_wam,
init_vgmstream_ubi_jade,
init_vgmstream_ubi_jade_container,
init_vgmstream_seg,
init_vgmstream_nds_strm_ffta2,
init_vgmstream_str_asr,
@ -394,6 +394,9 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_sthd,
init_vgmstream_pcm_sre,
init_vgmstream_dsp_mcadpcm,
init_vgmstream_ubi_lyn,
init_vgmstream_ubi_lyn_container,
init_vgmstream_msb_msh,
init_vgmstream_txth, /* should go at the end (lower priority) */
#ifdef VGM_USE_FFMPEG
@ -2386,11 +2389,6 @@ static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM *
{
//AAX, AIX?
if (vgmstream->layout_type==layout_layered) {
layered_layout_data *data = (layered_layout_data *) vgmstream->layout_data;
return data->layers[channel]->ch[0].streamfile;
}
if (vgmstream->coding_type==coding_NWA) {
nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data;
if (data && data->nwa)
@ -2461,6 +2459,11 @@ int get_vgmstream_average_bitrate(VGMSTREAM * vgmstream) {
segmented_layout_data *data = (segmented_layout_data *) vgmstream->layout_data;
return get_vgmstream_average_bitrate(data->segments[0]);
}
if (vgmstream->layout_type==layout_layered) {
layered_layout_data *data = (layered_layout_data *) vgmstream->layout_data;
return get_vgmstream_average_bitrate(data->layers[0]);
}
channels = get_vgmstream_average_bitrate_channel_count(vgmstream);
if (!channels) return 0;

View File

@ -373,7 +373,6 @@ typedef enum {
meta_SCD_PCM, /* Lunar - Eternal Blue */
meta_PS2_PCM, /* Konami KCEJ East: Ephemeral Fantasia, Yu-Gi-Oh! The Duelists of the Roses, 7 Blades */
meta_PS2_RKV, /* Legacy of Kain - Blood Omen 2 (PS2) */
meta_PS2_PSW, /* Rayman Raving Rabbids */
meta_PS2_VAS, /* Pro Baseball Spirits 5 */
meta_PS2_TEC, /* TECMO badflagged stream */
meta_PS2_ENTH, /* Enthusia */
@ -395,13 +394,11 @@ typedef enum {
meta_GSP_GSB, /* Tecmo games (Super Swing Golf 1 & 2, Quamtum Theory) */
meta_YDSP, /* WWE Day of Reckoning */
meta_FFCC_STR, /* Final Fantasy: Crystal Chronicles */
meta_WAA_WAC_WAD_WAM, /* Beyond Good & Evil */
meta_UBI_JADE, /* Beyond Good & Evil, Rayman Raving Rabbids */
meta_GCA, /* Metal Slug Anthology */
meta_MSVP, /* Popcap Hits */
meta_NGC_SSM, /* Golden Gashbell Full Power */
meta_PS2_JOE, /* Wall-E / Pixar games */
meta_NGC_YMF, /* WWE WrestleMania X8 */
meta_SADL, /* .sad */
meta_PS2_CCC, /* Tokyo Xtreme Racer DRIFT 2 */
@ -479,7 +476,6 @@ typedef enum {
meta_RIFF_WAVE_smpl, /* RIFF w/ loop data in smpl chunk */
meta_RIFF_WAVE_wsmp, /* RIFF w/ loop data in wsmp chunk */
meta_RIFF_WAVE_MWV, /* .mwv RIFF w/ loop data in ctrl chunk pflt */
meta_RIFF_WAVE_SNS, /* .sns RIFF */
meta_RIFX_WAVE, /* RIFX, for big-endian WAVs */
meta_RIFX_WAVE_smpl, /* RIFX w/ loop data in smpl chunk */
meta_XNB, /* XNA Game Studio 4.0 */
@ -668,6 +664,9 @@ typedef enum {
meta_MP4, /* MP4/AAC */
meta_PCM_SRE, /* .PCM+SRE [Viewtiful Joe (PS2)] */
meta_DSP_MCADPCM, /* Skyrim (Switch) */
meta_UBI_LYN, /* Ubisoft LyN engine [The Adventures of Tintin (multi)] */
meta_MSB_MSH, /* sfx companion of MIH+MIB */
meta_OGG_RPGMV, /* Ogg Vorbis with encryption [RPG Maker MV games (PC)] */
#ifdef VGM_USE_FFMPEG
meta_FFmpeg,
@ -886,6 +885,8 @@ typedef struct {
off_t block_offset;
size_t block_size;
int prev_block_samples; /* count for optimization */
} vorbis_custom_codec_data;
#endif
@ -967,6 +968,7 @@ typedef struct {
mpeg_custom_t type; /* mpeg subtype */
mpeg_custom_config config; /* config depending on the mode */
size_t default_buffer_size;
mpeg_custom_stream **streams; /* array of MPEG streams (ex. 2ch+2ch) */
size_t streams_size;