diff --git a/cli/vgmstream_cli.c b/cli/vgmstream_cli.c
index 1a4ee775..1fe89833 100644
--- a/cli/vgmstream_cli.c
+++ b/cli/vgmstream_cli.c
@@ -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 */
diff --git a/src/coding/mpeg_custom_utils.c b/src/coding/mpeg_custom_utils.c
index f225f931..a7737ddf 100644
--- a/src/coding/mpeg_custom_utils.c
+++ b/src/coding/mpeg_custom_utils.c
@@ -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;
}
diff --git a/src/coding/mpeg_custom_utils_ealayer3.c b/src/coding/mpeg_custom_utils_ealayer3.c
index c8d827f1..a4f4e9c6 100644
--- a/src/coding/mpeg_custom_utils_ealayer3.c
+++ b/src/coding/mpeg_custom_utils_ealayer3.c
@@ -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;
diff --git a/src/coding/mpeg_decoder.c b/src/coding/mpeg_decoder.c
index 165c21cd..7f0ca49c 100644
--- a/src/coding/mpeg_decoder.c
+++ b/src/coding/mpeg_decoder.c
@@ -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;
}
diff --git a/src/formats.c b/src/formats.c
index 015fdf57..2fcf59db 100644
--- a/src/formats.c
+++ b/src/formats.c
@@ -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"},
diff --git a/src/layout/blocked_ea_schl.c b/src/layout/blocked_ea_schl.c
index 68e4ffef..9a9c5f3b 100644
--- a/src/layout/blocked_ea_schl.c
+++ b/src/layout/blocked_ea_schl.c
@@ -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;
}
diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj
index eeac4134..d8db6afd 100644
--- a/src/libvgmstream.vcproj
+++ b/src/libvgmstream.vcproj
@@ -231,6 +231,10 @@
+
+
+
+
+
+
@@ -958,10 +970,6 @@
RelativePath=".\meta\ps2_psh.c"
>
-
-
@@ -1278,6 +1286,10 @@
RelativePath=".\meta\ubi_ckd.c"
>
+
+
@@ -1307,7 +1319,7 @@
>
+
@@ -143,6 +144,7 @@
+
@@ -334,7 +336,6 @@
-
@@ -400,13 +401,14 @@
+
-
+
diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters
index 03b28dfb..d327ac76 100644
--- a/src/libvgmstream.vcxproj.filters
+++ b/src/libvgmstream.vcxproj.filters
@@ -80,6 +80,9 @@
meta\Header Files
+
+ meta\Header Files
+
meta\Header Files
@@ -583,9 +586,6 @@
meta\Source Files
-
- meta\Source Files
-
meta\Source Files
@@ -781,6 +781,9 @@
meta\Source Files
+
+ meta\Source Files
+
meta\Source Files
@@ -799,7 +802,7 @@
meta\Source Files
-
+
meta\Source Files
@@ -1231,6 +1234,9 @@
meta\Source Files
+
+ meta\Source Files
+
coding\Source Files
diff --git a/src/meta/meta.h b/src/meta/meta.h
index d22ca4b0..0499902a 100644
--- a/src/meta/meta.h
+++ b/src/meta/meta.h
@@ -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*/
diff --git a/src/meta/msb_msh.c b/src/meta/msb_msh.c
new file mode 100644
index 00000000..a5abc447
--- /dev/null
+++ b/src/meta/msb_msh.c
@@ -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;
+}
diff --git a/src/meta/ogg_vorbis.c b/src/meta/ogg_vorbis.c
index fc030dab..dc32db64 100644
--- a/src/meta/ogg_vorbis.c
+++ b/src/meta/ogg_vorbis.c
@@ -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;
}
diff --git a/src/meta/ps2_psw.c b/src/meta/ps2_psw.c
deleted file mode 100644
index 6ac4ffa2..00000000
--- a/src/meta/ps2_psw.c
+++ /dev/null
@@ -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;ich[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;
-}
diff --git a/src/meta/ps2_sl3.c b/src/meta/ps2_sl3.c
index b145cc50..69fc81d3 100644
--- a/src/meta/ps2_sl3.c
+++ b/src/meta/ps2_sl3.c
@@ -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;ich[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;
}
diff --git a/src/meta/ps2_tk5.c b/src/meta/ps2_tk5.c
index bf7efc3a..f0df29e1 100644
--- a/src/meta/ps2_tk5.c
+++ b/src/meta/ps2_tk5.c
@@ -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;ich[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;
}
diff --git a/src/meta/riff.c b/src/meta/riff.c
index 9a9c9a6b..a7f489c8 100644
--- a/src/meta/riff.c
+++ b/src/meta/riff.c
@@ -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;
diff --git a/src/meta/ubi_jade.c b/src/meta/ubi_jade.c
new file mode 100644
index 00000000..0feb6cc4
--- /dev/null
+++ b/src/meta/ubi_jade.c
@@ -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;
+}
diff --git a/src/meta/ubi_lyn.c b/src/meta/ubi_lyn.c
new file mode 100644
index 00000000..77c714a1
--- /dev/null
+++ b/src/meta/ubi_lyn.c
@@ -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;
+}
diff --git a/src/meta/ubi_lyn_ogg_streamfile.h b/src/meta/ubi_lyn_ogg_streamfile.h
new file mode 100644
index 00000000..aeec84f8
--- /dev/null
+++ b/src/meta/ubi_lyn_ogg_streamfile.h
@@ -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_ */
diff --git a/src/meta/waa_wac_wad_wam.c b/src/meta/waa_wac_wad_wam.c
deleted file mode 100644
index 0b7b50d4..00000000
--- a/src/meta/waa_wac_wad_wam.c
+++ /dev/null
@@ -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;ich[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;
-}
-
-
-
-
diff --git a/src/meta/wwise.c b/src/meta/wwise.c
index 04e720d2..53d1e3e3 100644
--- a/src/meta/wwise.c
+++ b/src/meta/wwise.c
@@ -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) */
{
diff --git a/src/vgmstream.c b/src/vgmstream.c
index 3af15724..57db021d 100644
--- a/src/vgmstream.c
+++ b/src/vgmstream.c
@@ -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;
diff --git a/src/vgmstream.h b/src/vgmstream.h
index b73a692b..7a9b1510 100644
--- a/src/vgmstream.h
+++ b/src/vgmstream.h
@@ -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;