diff --git a/src/coding/coding.h b/src/coding/coding.h
index 1c5813d6..2707b2d8 100644
--- a/src/coding/coding.h
+++ b/src/coding/coding.h
@@ -42,6 +42,7 @@ void decode_h4m_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
size_t ima_bytes_to_samples(size_t bytes, int channels);
size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels);
size_t xbox_ima_bytes_to_samples(size_t bytes, int channels);
+size_t dat4_ima_bytes_to_samples(size_t bytes, int channels);
size_t apple_ima4_bytes_to_samples(size_t bytes, int channels);
/* ngc_dsp_decoder */
@@ -87,6 +88,7 @@ int ps_find_loop_offsets_full(STREAMFILE *streamFile, off_t start_offset, size_t
size_t ps_find_padding(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, size_t interleave, int discard_empty);
size_t ps_bytes_to_samples(size_t bytes, int channels);
size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, int channels);
+int ps_check_format(STREAMFILE *streamFile, off_t offset, size_t max);
/* psv_decoder */
void decode_hevag(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
@@ -361,6 +363,7 @@ void xma2_parse_fmt_chunk_extra(STREAMFILE *streamFile, off_t chunk_offset, int
void xma2_parse_xma2_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * channels, int * sample_rate, int * loop_flag, int32_t * num_samples, int32_t * loop_start_sample, int32_t * loop_end_sample);
void xma_fix_raw_samples(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples);
+void xma_fix_raw_samples_hb(VGMSTREAM *vgmstream, STREAMFILE *headerFile, STREAMFILE *bodyFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples);
void xma_fix_raw_samples_ch(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, int channel_per_stream, int fix_num_samples, int fix_loop_samples);
int riff_get_fact_skip_samples(STREAMFILE * streamFile, off_t start_offset);
diff --git a/src/coding/coding_utils.c b/src/coding/coding_utils.c
index 6201d6ba..f9eca2b0 100644
--- a/src/coding/coding_utils.c
+++ b/src/coding/coding_utils.c
@@ -871,6 +871,11 @@ void xma_fix_raw_samples_ch(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t s
#endif
}
+void xma_fix_raw_samples_hb(VGMSTREAM *vgmstream, STREAMFILE *headerFile, STREAMFILE *bodyFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples) {
+ int channels_per_stream = xma_get_channels_per_stream(headerFile, chunk_offset, vgmstream->channels);
+ xma_fix_raw_samples_ch(vgmstream, bodyFile, stream_offset, stream_size, channels_per_stream, fix_num_samples, fix_loop_samples);
+}
+
void xma_fix_raw_samples(VGMSTREAM *vgmstream, STREAMFILE*streamFile, off_t stream_offset, size_t stream_size, off_t chunk_offset, int fix_num_samples, int fix_loop_samples) {
int channels_per_stream = xma_get_channels_per_stream(streamFile, chunk_offset, vgmstream->channels);
xma_fix_raw_samples_ch(vgmstream, streamFile, stream_offset, stream_size, channels_per_stream, fix_num_samples, fix_loop_samples);
diff --git a/src/coding/ima_decoder.c b/src/coding/ima_decoder.c
index fe56cd62..cd033875 100644
--- a/src/coding/ima_decoder.c
+++ b/src/coding/ima_decoder.c
@@ -1131,6 +1131,14 @@ size_t xbox_ima_bytes_to_samples(size_t bytes, int channels) {
+ ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); /* unlikely (encoder aligns) */
}
+size_t dat4_ima_bytes_to_samples(size_t bytes, int channels) {
+ int block_align = 0x20 * channels;
+ if (channels <= 0) return 0;
+ /* DAT4 IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */
+ return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels
+ + ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); /* unlikely (encoder aligns) */
+}
+
size_t apple_ima4_bytes_to_samples(size_t bytes, int channels) {
int block_align = 0x22 * channels;
if (channels <= 0) return 0;
diff --git a/src/coding/mpeg_custom_utils.c b/src/coding/mpeg_custom_utils.c
index f0c3a913..79d0502f 100644
--- a/src/coding/mpeg_custom_utils.c
+++ b/src/coding/mpeg_custom_utils.c
@@ -90,6 +90,8 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m
case MPEG_XVAG: /* set in header and needed for gapless looping */
data->skip_samples = data->config.skip_samples; break;
+ case MPEG_STANDARD:
+ data->skip_samples = data->config.skip_samples; break;
default:
break;
}
diff --git a/src/coding/psx_decoder.c b/src/coding/psx_decoder.c
index 884e419d..47c66ac7 100644
--- a/src/coding/psx_decoder.c
+++ b/src/coding/psx_decoder.c
@@ -342,3 +342,21 @@ size_t ps_cfg_bytes_to_samples(size_t bytes, size_t frame_size, int channels) {
return bytes / channels / frame_size * 28;
}
+/* test PS-ADPCM frames for correctness */
+int ps_check_format(STREAMFILE *streamFile, off_t offset, size_t max) {
+ off_t max_offset = offset + max;
+ if (max_offset > get_streamfile_size(streamFile))
+ max_offset = get_streamfile_size(streamFile);
+
+ while (offset < max_offset) {
+ uint8_t predictor = (read_8bit(offset+0x00,streamFile) >> 4) & 0x0f;
+ uint8_t flags = read_8bit(offset+0x01,streamFile);
+
+ if (predictor > 5 || flags > 7) {
+ return 0;
+ }
+ offset += 0x10;
+ }
+
+ return 1;
+}
diff --git a/src/formats.c b/src/formats.c
index e32ddd59..861d844e 100644
--- a/src/formats.c
+++ b/src/formats.c
@@ -125,6 +125,7 @@ static const char* extension_list[] = {
"cxs",
"da",
+ "data",
"dax",
"dbm",
"dcs",
@@ -188,9 +189,12 @@ static const char* extension_list[] = {
"iab",
"iadp",
+ "idmsf",
"idsp",
"idvi", //fake extension/header id for .pcm (renamed, to be removed)
+ "idwav",
"idx",
+ "idxma",
"ikm",
"ild",
"ilv", //txth/reserved [Star Wars Episode III (PS2)]
@@ -519,6 +523,7 @@ static const char* extension_list[] = {
"xa30",
"xag",
"xau",
+ "xav",
"xen",
"xma",
"xma2",
@@ -853,11 +858,7 @@ static const meta_info meta_info_list[] = {
{meta_XWB, "Microsoft XWB header"},
{meta_PS2_XA30, "Reflections XA30 PS2 header"},
{meta_MUSC, "Krome MUSC header"},
- {meta_MUSX_V004, "MUSX / Version 004 Header"},
- {meta_MUSX_V005, "MUSX / Version 005 Header"},
- {meta_MUSX_V006, "MUSX / Version 006 Header"},
- {meta_MUSX_V010, "MUSX / Version 010 Header"},
- {meta_MUSX_V201, "MUSX / Version 201 Header"},
+ {meta_MUSX, "Eurocom MUSX header"},
{meta_LEG, "Legaia 2 - Duel Saga LEG Header"},
{meta_FILP, "Bio Hazard - Gun Survivor FILp Header"},
{meta_IKM, "MiCROViSiON IKM header"},
@@ -1186,7 +1187,8 @@ static const meta_info meta_info_list[] = {
{meta_MSF_TAMASOFT, "Tama-Soft MSF header"},
{meta_XPS_DAT, "From Software .XPS+DAT header"},
{meta_ZSND, "Vicarious Visions ZSND header"},
- {meta_DSP_ADPCMX, "AQUASTYLE ADPY header"},
+ {meta_DSP_ADPY, "AQUASTYLE ADPY header"},
+ {meta_DSP_ADPX, "AQUASTYLE ADPX header"},
{meta_OGG_OPUS, "Ogg Opus header"},
{meta_IMC, "iNiS .IMC header"},
{meta_GIN, "Electronic Arts Gnsu header"},
@@ -1201,6 +1203,8 @@ static const meta_info meta_info_list[] = {
{meta_BWAV, "Nintendo BWAV header"},
{meta_RAD, "Traveller's Tales .RAD header"},
{meta_SMACKER, "RAD Game Tools SMACKER header"},
+ {meta_MZRT, "id Software MZRT header"},
+ {meta_XAVS, "Reflections XAVS header"},
};
diff --git a/src/libvgmstream.vcproj b/src/libvgmstream.vcproj
index 2f7fb9f7..aa7aa84e 100644
--- a/src/libvgmstream.vcproj
+++ b/src/libvgmstream.vcproj
@@ -280,6 +280,10 @@
RelativePath=".\meta\mta2_streamfile.h"
>
+
+
@@ -296,6 +300,10 @@
RelativePath=".\meta\vsv_streamfile.h"
>
+
+
@@ -802,10 +810,14 @@
RelativePath=".\meta\musx.c"
>
-
-
+
+
+
+
@@ -1701,6 +1713,10 @@
+
+
+
+
@@ -317,6 +319,7 @@
+
@@ -501,6 +504,7 @@
+
diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters
index f5222f62..454f77e6 100644
--- a/src/libvgmstream.vcxproj.filters
+++ b/src/libvgmstream.vcxproj.filters
@@ -107,6 +107,9 @@
meta\Header Files
+
+ meta\Header Files
+
meta\Header Files
@@ -119,6 +122,9 @@
meta\Header Files
+
+ meta\Header Files
+
meta\Header Files
@@ -493,6 +499,9 @@
meta\Source Files
+
+ meta\Source Files
+
meta\Source Files
@@ -1048,6 +1057,9 @@
meta\Source Files
+
+ meta\Source Files
+
meta\Source Files
diff --git a/src/meta/hca.c b/src/meta/hca.c
index 3afea632..095f9bc0 100644
--- a/src/meta/hca.c
+++ b/src/meta/hca.c
@@ -32,11 +32,14 @@ VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE *streamFile, uint16_t subkey) {
keysize = read_key_file(keybuf, 0x08+0x04, streamFile);
if (keysize == 0x08) { /* standard */
keycode = (uint64_t)get_64bitBE(keybuf+0x00);
+ if (subkey) {
+ keycode = keycode * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) );
+ }
}
else if (keysize == 0x08+0x02) { /* seed key + AWB subkey */
- uint64_t key = (uint64_t)get_64bitBE(keybuf+0x00);
- uint16_t sub = (uint16_t)get_16bitBE(keybuf+0x08);
- keycode = key * ( ((uint64_t)sub << 16u) | ((uint16_t)~sub + 2u) );
+ uint64_t file_key = (uint64_t)get_64bitBE(keybuf+0x00);
+ uint16_t file_sub = (uint16_t)get_16bitBE(keybuf+0x08);
+ keycode = file_key * ( ((uint64_t)file_sub << 16u) | ((uint16_t)~file_sub + 2u) );
}
else {
find_hca_key(hca_data, &keycode, subkey);
diff --git a/src/meta/meta.h b/src/meta/meta.h
index 68180571..e2a26c27 100644
--- a/src/meta/meta.h
+++ b/src/meta/meta.h
@@ -48,7 +48,8 @@ VGMSTREAM * init_vgmstream_dsp_mcadpcm(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_switch_audio(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_sps_n1(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_itl_ch(STREAMFILE *streamFile);
-VGMSTREAM * init_vgmstream_dsp_adpcmx(STREAMFILE *streamFile);
+VGMSTREAM * init_vgmstream_dsp_adpy(STREAMFILE *streamFile);
+VGMSTREAM * init_vgmstream_dsp_adpx(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_dsp_ds2(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_csmp(STREAMFILE *streamFile);
@@ -202,11 +203,7 @@ VGMSTREAM * init_vgmstream_ps2_xa30(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_musc(STREAMFILE * streamFile);
-VGMSTREAM * init_vgmstream_musx_v004(STREAMFILE * streamFile);
-VGMSTREAM * init_vgmstream_musx_v005(STREAMFILE * streamFile);
-VGMSTREAM * init_vgmstream_musx_v006(STREAMFILE * streamFile);
-VGMSTREAM * init_vgmstream_musx_v010(STREAMFILE * streamFile);
-VGMSTREAM * init_vgmstream_musx_v201(STREAMFILE * streamFile);
+VGMSTREAM * init_vgmstream_musx(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_leg(STREAMFILE * streamFile);
@@ -869,4 +866,8 @@ VGMSTREAM * init_vgmstream_rad(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_smk(STREAMFILE * streamFile);
+VGMSTREAM * init_vgmstream_mzrt(STREAMFILE * streamFile);
+
+VGMSTREAM * init_vgmstream_xavs(STREAMFILE * streamFile);
+
#endif /*_META_H*/
diff --git a/src/meta/musx.c b/src/meta/musx.c
index b3f211fd..9a7dd977 100644
--- a/src/meta/musx.c
+++ b/src/meta/musx.c
@@ -1,224 +1,131 @@
#include "meta.h"
-#include "../util.h"
+#include "../coding/coding.h"
+typedef enum { MFX, MFX_BANK, SFX_BANK, SBNK, FORM } musx_form;
+typedef enum { PSX, DSP, XBOX, IMA, DAT } musx_codec;
+typedef struct {
+ int big_endian;
+ int version;
+ size_t file_size;
-/* MUSX (Version 004) */
-VGMSTREAM * init_vgmstream_musx_v004(STREAMFILE *streamFile) {
+ int total_subsongs;
+ int is_old;
+
+ off_t tables_offset;
+ off_t loops_offset;
+ off_t stream_offset;
+ size_t stream_size;
+ off_t coefs_offset;
+
+ musx_form form;
+ musx_codec codec;
+ uint32_t platform;
+
+ int channels;
+ int sample_rate;
+ int loop_flag;
+ int32_t loop_start;
+ int32_t loop_end;
+ int32_t num_samples;
+ int32_t loop_start_sample;
+ int32_t loop_end_sample;
+} musx_header;
+
+static int parse_musx(STREAMFILE *streamFile, musx_header *musx);
+
+/* MUSX - from Eurocom's games */
+VGMSTREAM * init_vgmstream_musx(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
- int loop_flag, channel_count;
+ musx_header musx = {0};
/* checks */
- if (!check_extensions(streamFile, "musx"))
- goto fail;
- if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */
- goto fail;
- if (read_32bitBE(0x08,streamFile) != 0x04000000)
- goto fail;
-
- loop_flag = (read_32bitLE(0x840,streamFile) != 0xFFFFFFFF);
- channel_count = 2;
-
- /* build the VGMSTREAM */
- vgmstream = allocate_vgmstream(channel_count,loop_flag);
- if (!vgmstream) goto fail;
-
- switch (read_32bitBE(0x10,streamFile)) {
- case 0x5053325F: /* PS2_ */
- start_offset = read_32bitLE(0x28,streamFile);
- vgmstream->channels = channel_count;
- vgmstream->sample_rate = 32000;
- vgmstream->coding_type = coding_PSX;
- vgmstream->num_samples = (read_32bitLE(0x0C,streamFile))/16/channel_count*28;
- vgmstream->layout_type = layout_interleave;
- vgmstream->interleave_block_size = 0x80;
- if (loop_flag) {
- vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))/16/channel_count*28;
- vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))/16/channel_count*28;
- }
- break;
- case 0x47435F5F: /* GC__ */
- start_offset = read_32bitBE(0x28,streamFile);
- vgmstream->channels = channel_count;
- vgmstream->sample_rate = 32000;
- vgmstream->coding_type = coding_DAT4_IMA;
- vgmstream->num_samples = (read_32bitBE(0x2C,streamFile))/16/channel_count*28;
- vgmstream->layout_type = layout_interleave;
- vgmstream->interleave_block_size = 0x20;
- if (loop_flag) {
- vgmstream->loop_start_sample = (read_32bitBE(0x890,streamFile))/16/channel_count*28;
- vgmstream->loop_end_sample = (read_32bitBE(0x89C,streamFile))/16/channel_count*28;
- }
- break;
- case 0x58425F5F: /* XB__ */
- start_offset = read_32bitLE(0x28,streamFile);
- vgmstream->channels = channel_count;
- vgmstream->sample_rate = 44100;
- vgmstream->coding_type = coding_DAT4_IMA;
- vgmstream->num_samples = (read_32bitLE(0x2C,streamFile))/16/channel_count*28;
- vgmstream->layout_type = layout_interleave;
- vgmstream->interleave_block_size = 0x20;
- if (loop_flag) {
- vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))/16/channel_count*28;
- vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))/16/channel_count*28;
- }
- break;
- default:
- goto fail;
- }
-
- vgmstream->meta_type = meta_MUSX_V004;
-
- if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
- goto fail;
- return vgmstream;
-
-fail:
- close_vgmstream(vgmstream);
- return NULL;
-}
-
-
-/* MUSX (Version 005) [Predator: Concrete Jungle (PS2/Xbox) ] */
-VGMSTREAM * init_vgmstream_musx_v005(STREAMFILE *streamFile) {
- VGMSTREAM * vgmstream = NULL;
- off_t start_offset;
- int loop_flag, channel_count;
-
-
- /* checks */
- /* .sfx: Batman Begins, .musx: header id */
- if (!check_extensions(streamFile, "musx,sfx"))
- goto fail;
- if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */
- goto fail;
- if (read_32bitBE(0x08,streamFile) != 0x05000000)
- goto fail;
-
- loop_flag = (read_32bitLE(0x840,streamFile) != 0xFFFFFFFF);
- channel_count = 2;
-
- /* build the VGMSTREAM */
- vgmstream = allocate_vgmstream(channel_count,loop_flag);
- if (!vgmstream) goto fail;
-
- switch (read_32bitBE(0x10,streamFile)) {
- case 0x5053325F: /* PS2_ */
- start_offset = read_32bitLE(0x28,streamFile);
- vgmstream->channels = channel_count;
- vgmstream->sample_rate = 32000;
- vgmstream->coding_type = coding_PSX;
- vgmstream->num_samples = (read_32bitLE(0x0C,streamFile))/16/channel_count*28;
- vgmstream->layout_type = layout_interleave;
- vgmstream->interleave_block_size = 0x80;
- if (loop_flag) {
- vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))/16/channel_count*28;
- vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))/16/channel_count*28;
- }
- break;
- case 0x47435F5F: /* GC__ */
- start_offset = read_32bitBE(0x28,streamFile);
- vgmstream->channels = channel_count;
- vgmstream->sample_rate = 32000;
- vgmstream->coding_type = coding_DAT4_IMA;
- vgmstream->num_samples = (read_32bitBE(0x2C,streamFile))/16/channel_count*28;
- vgmstream->layout_type = layout_interleave;
- vgmstream->interleave_block_size = 0x20;
- if (loop_flag) {
- vgmstream->loop_start_sample = (read_32bitBE(0x890,streamFile))/16/channel_count*28;
- vgmstream->loop_end_sample = (read_32bitBE(0x89C,streamFile))/16/channel_count*28;
- }
- break;
- case 0x58425F5F: /* XB__ */
- start_offset = read_32bitLE(0x28,streamFile);
- vgmstream->channels = channel_count;
- vgmstream->sample_rate = 44100;
- vgmstream->coding_type = coding_DAT4_IMA;
- vgmstream->num_samples = (read_32bitLE(0x2C,streamFile))/16/channel_count*28;
- vgmstream->layout_type = layout_interleave;
- vgmstream->interleave_block_size = 0x20;
- if (loop_flag) {
- vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))/16/channel_count*28;
- vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))/16/channel_count*28;
- }
- break;
- default:
- goto fail;
- }
-
- vgmstream->meta_type = meta_MUSX_V005;
-
- if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
- goto fail;
- return vgmstream;
-
-fail:
- close_vgmstream(vgmstream);
- return NULL;
-}
-
-
-/* MUSX (Version 006) [Batman Begins (GC)] */
-VGMSTREAM * init_vgmstream_musx_v006(STREAMFILE *streamFile) {
- VGMSTREAM * vgmstream = NULL;
- off_t start_offset;
- int loop_flag, channel_count;
-
-
- /* checks */
- /* .sfx: Batman Begins, .musx: header id */
+ /* .sfx: actual extension (extracted from bigfiles with sometimes encrypted names)
+ * .musx: header id */
if (!check_extensions(streamFile, "sfx,musx"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */
goto fail;
- if (read_32bitBE(0x08,streamFile) != 0x06000000)
+
+ if (!parse_musx(streamFile, &musx))
goto fail;
- loop_flag = (read_32bitLE(0x840,streamFile)!=0xFFFFFFFF);
- channel_count = 2;
- //todo some files (ex. Batman Begins) have a subsong table at 0x800, not sure what signals it (flags at 0x04/0x14?)
+ start_offset = musx.stream_offset;
/* build the VGMSTREAM */
- vgmstream = allocate_vgmstream(channel_count,loop_flag);
+ vgmstream = allocate_vgmstream(musx.channels, musx.loop_flag);
if (!vgmstream) goto fail;
- switch (read_32bitBE(0x10,streamFile)) {
- case 0x5053325F: /* PS2_ */
- start_offset = read_32bitLE(0x28,streamFile);
- vgmstream->channels = channel_count;
- vgmstream->sample_rate = 32000;
+ vgmstream->meta_type = meta_MUSX;
+ vgmstream->sample_rate = musx.sample_rate;
+ vgmstream->num_streams = musx.total_subsongs;
+ vgmstream->stream_size = musx.stream_size;
+
+ switch (musx.codec) {
+ case PSX:
+ vgmstream->num_samples = ps_bytes_to_samples(musx.stream_size, musx.channels);
+ vgmstream->loop_start_sample = ps_bytes_to_samples(musx.loop_start, musx.channels);
+ vgmstream->loop_end_sample = ps_bytes_to_samples(musx.loop_end, musx.channels);
+
vgmstream->coding_type = coding_PSX;
- vgmstream->num_samples = (read_32bitLE(0x0C,streamFile))*28/16/channel_count;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x80;
- vgmstream->meta_type = meta_MUSX_V006;
- if (loop_flag) {
- vgmstream->loop_start_sample = (read_32bitLE(0x890,streamFile))*28/16/channel_count;
- vgmstream->loop_end_sample = (read_32bitLE(0x89C,streamFile))*28/16/channel_count;
- }
break;
- case 0x47435F5F: /* GC__ */
- start_offset = read_32bitBE(0x28,streamFile);
- if (start_offset == 0 || start_offset == 0xABABABAB) goto fail; /* some are empty */
- vgmstream->channels = channel_count;
- vgmstream->sample_rate = 32000;
+ case DAT:
+ vgmstream->num_samples = dat4_ima_bytes_to_samples(musx.stream_size, musx.channels);
+ vgmstream->loop_start_sample = dat4_ima_bytes_to_samples(musx.loop_start, musx.channels);
+ vgmstream->loop_end_sample = dat4_ima_bytes_to_samples(musx.loop_end, musx.channels);
+
vgmstream->coding_type = coding_DAT4_IMA;
- vgmstream->num_samples = (read_32bitBE(0x2C,streamFile))*28/16/channel_count;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x20;
- vgmstream->meta_type = meta_MUSX_V006;
- if (loop_flag) {
- vgmstream->loop_start_sample = (read_32bitBE(0x890,streamFile))*28/16/channel_count;
- vgmstream->loop_end_sample = (read_32bitBE(0x89C,streamFile))*28/16/channel_count;
- }
break;
+
+ case IMA:
+ vgmstream->num_samples = ima_bytes_to_samples(musx.stream_size, musx.channels);
+ vgmstream->loop_start_sample = musx.loop_start / 4; /* weird but needed */
+ vgmstream->loop_end_sample = musx.loop_end / 4;
+
+ vgmstream->coding_type = coding_DVI_IMA_int;
+ vgmstream->layout_type = layout_interleave;
+ vgmstream->interleave_block_size = 0x01;
+ break;
+
+ case XBOX:
+ vgmstream->num_samples = xbox_ima_bytes_to_samples(musx.stream_size, musx.channels);
+ vgmstream->loop_start_sample = xbox_ima_bytes_to_samples(musx.loop_start, musx.channels);
+ vgmstream->loop_end_sample = xbox_ima_bytes_to_samples(musx.loop_end, musx.channels);
+
+ vgmstream->coding_type = coding_XBOX_IMA;
+ vgmstream->layout_type = layout_none;
+ break;
+
+ case DSP:
+ vgmstream->num_samples = dsp_bytes_to_samples(musx.stream_size, musx.channels);
+ vgmstream->loop_start_sample = dsp_bytes_to_samples(musx.loop_start, musx.channels);
+ vgmstream->loop_end_sample = dsp_bytes_to_samples(musx.loop_end, musx.channels);
+
+ vgmstream->coding_type = coding_NGC_DSP;
+ vgmstream->layout_type = layout_interleave;
+ vgmstream->interleave_block_size = 0x08;
+
+ dsp_read_coefs(vgmstream,streamFile,musx.coefs_offset+0x1c, 0x60, musx.big_endian);
+ dsp_read_hist(vgmstream,streamFile,musx.coefs_offset+0x40, 0x60, musx.big_endian);
+ break;
+
default:
goto fail;
}
+ if (musx.num_samples)
+ vgmstream->num_samples = musx.num_samples;
+ if (musx.loop_flag && musx.loop_start_sample)
+ vgmstream->loop_start_sample = musx.loop_start_sample;
+ if (musx.loop_flag && musx.loop_end_sample)
+ vgmstream->loop_end_sample = musx.loop_end_sample;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
@@ -230,258 +137,416 @@ fail:
}
-/* MUSX (Version 010) [Dead Space: Extraction (Wii), Rio (PS3), Pirates of the Caribbean: At World's End (PSP)] */
-VGMSTREAM * init_vgmstream_musx_v010(STREAMFILE *streamFile) {
- VGMSTREAM * vgmstream = NULL;
- char filename[PATH_LIMIT];
- off_t start_offset;
- int musx_type; /* determining the decoder by strings like "PS2_", "GC__" and so on */
- //int musx_version; /* 0x08 provides a "version" byte */
- int loop_flag = 0;
- int channel_count;
+static int parse_musx_stream(STREAMFILE *streamFile, musx_header *musx) {
+ int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
+ int default_channels, default_sample_rate;
- /* check extension, case insensitive */
- streamFile->get_name(streamFile,filename,sizeof(filename));
- if (strcasecmp("musx",filename_extension(filename))) goto fail;
-
- /* check header */
- if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */
- goto fail;
- if (read_32bitBE(0x800,streamFile) == 0x53424E4B) /* "SBNK", */ // SoundBank, refuse
- goto fail;
- if (read_32bitBE(0x08,streamFile) != 0x0A000000) /* "0x0A000000" */
- goto fail;
-
- loop_flag = ((read_32bitLE(0x34,streamFile)!=0x00000000) &&
- (read_32bitLE(0x34,streamFile)!=0xABABABAB));
- channel_count = 2;
-
- musx_type=(read_32bitBE(0x10,streamFile));
-
- if (musx_type == 0x5749495F && /* WII_ */
- (read_16bitBE(0x40,streamFile) == 0x4441) && /* DA */
- (read_8bit(0x42,streamFile) == 0x54)) /* T */
- {
- channel_count = read_32bitLE(0x48,streamFile);
- loop_flag = (read_32bitLE(0x64,streamFile) != -1);
- }
- if (musx_type == 0x5053335F && /* PS3_ */
- (read_16bitBE(0x40,streamFile) == 0x4441) && /* DA */
- (read_8bit(0x42,streamFile) == 0x54)) /* T */
- {
- channel_count = read_32bitLE(0x48,streamFile);
- loop_flag = (read_32bitLE(0x64,streamFile) != -1);
- }
- if (0x58455F5F == musx_type) /* XE__ */
- {
- loop_flag = 0;
+ if (musx->big_endian) {
+ read_32bit = read_32bitBE;
+ } else {
+ read_32bit = read_32bitLE;
}
- /* build the VGMSTREAM */
- vgmstream = allocate_vgmstream(channel_count,loop_flag);
- if (!vgmstream) goto fail;
- /* fill in the vital statistics */
- switch (musx_type) {
- case 0x5053325F: /* PS2_ */
- start_offset = 0x800;
- vgmstream->channels = channel_count;
- vgmstream->sample_rate = 32000;
- vgmstream->coding_type = coding_PSX;
- vgmstream->num_samples = read_32bitLE(0x40,streamFile);
- vgmstream->layout_type = layout_interleave;
- vgmstream->interleave_block_size = 0x80;
- vgmstream->meta_type = meta_MUSX_V010;
- if (loop_flag)
- {
- vgmstream->loop_start_sample = read_32bitLE(0x44,streamFile);
- vgmstream->loop_end_sample = read_32bitLE(0x40,streamFile);
+ /* autodetect for older versions that have no info */
+ if (musx->platform == 0) {
+ if (musx->big_endian) {
+ musx->platform = 0x47433032; /* "GC02" (fake) */
+ }
+ else {
+ off_t offset = musx->stream_offset;
+ size_t max = 0x5000;
+ if (max > musx->stream_size)
+ max = musx->stream_size;
+
+ if (ps_check_format(streamFile, offset, max)) {
+ musx->platform = 0x5053325F; /* "PS2_" */
+ } else {
+ musx->platform = 0x58423032; /* "XB02" (fake) */
}
- break;
- case 0x5053505F: /* PSP_ */
- start_offset = 0x800;
- vgmstream->channels = channel_count;
- vgmstream->sample_rate = 32768;
- vgmstream->coding_type = coding_PSX;
- vgmstream->num_samples = (read_32bitLE(0xC,streamFile))*28/32;
- vgmstream->layout_type = layout_interleave;
- vgmstream->interleave_block_size = 0x80;
- vgmstream->meta_type = meta_MUSX_V010;
- break;
- case 0x5053335F: /* PS3_ */
- start_offset = 0x800;
- vgmstream->channels = channel_count;
- vgmstream->coding_type = coding_DAT4_IMA;
- vgmstream->layout_type = layout_interleave;
- vgmstream->interleave_block_size = 0x20;
- vgmstream->meta_type = meta_MUSX_V010;
+ }
+ }
- if (read_32bitBE(0x40,streamFile)==0x44415438){
- vgmstream->num_samples = read_32bitLE(0x60,streamFile);
- vgmstream->sample_rate = read_32bitLE(0x4C,streamFile);
- if (loop_flag)
- {
- vgmstream->loop_start_sample = read_32bitLE(0x64,streamFile);
- vgmstream->loop_end_sample = read_32bitLE(0x60,streamFile);
+
+ /* defaults */
+ switch(musx->platform) {
+
+ case 0x5053325F: /* "PS2_" */
+ default_channels = 2;
+ default_sample_rate = 32000;
+ musx->codec = PSX;
+ break;
+
+ case 0x47435F5F: /* "GC__" */
+ default_channels = 2;
+ default_sample_rate = 32000;
+ musx->codec = DAT;
+ break;
+
+ case 0x47433032: /* "GC02" */
+ default_channels = 2;
+ default_sample_rate = 32000;
+ if (musx->coefs_offset)
+ musx->codec = DSP;
+ else
+ musx->codec = IMA;
+ break;
+
+ case 0x58425F5F: /* "XB__" */
+ default_channels = 2;
+ default_sample_rate = 44100;
+ musx->codec = DAT;
+ break;
+
+ case 0x58423032: /* "XB02" */
+ default_channels = 2;
+ default_sample_rate = 44100;
+ musx->codec = XBOX;
+ break;
+
+ case 0x5053505F: /* "PSP_" */
+ default_channels = 2;
+ default_sample_rate = 32768;
+ musx->codec = PSX;
+ break;
+
+ case 0x5749495F: /* "WII_" */
+ default_channels = 2;
+ default_sample_rate = 44100;
+ musx->codec = DAT;
+ break;
+
+ case 0x5053335F: /* "PS3_" */
+ default_channels = 2;
+ default_sample_rate = 44100;
+ musx->codec = DAT;
+ break;
+
+ case 0x58455F5F: /* "XE__" */
+ default_channels = 2;
+ default_sample_rate = 32000;
+ musx->codec = DAT;
+ break;
+
+ default:
+ VGM_LOG("MUSX: unknown platform %x\n", musx->platform);
+ goto fail;
+ }
+
+ if (musx->channels == 0)
+ musx->channels = default_channels;
+ if (musx->sample_rate == 0)
+ musx->sample_rate = default_sample_rate;
+
+
+ /* parse loops and other info */
+ if (musx->tables_offset && musx->loops_offset) {
+ int i, cues2_count;
+ off_t cues2_offset;
+
+ /* cue/stream position table thing */
+ /* 0x00: cues1 entries (entry size 0x34 or 0x18)
+ * 0x04: cues2 entries (entry size 0x20 or 0x14)
+ * 0x08: header size (always 0x14)
+ * 0x0c: cues2 start
+ * 0x10: volume? (usually <= 100) */
+
+ /* find loops (cues1 also seems to have this info but this looks ok) */
+ cues2_count = read_32bit(musx->loops_offset+0x04, streamFile);
+ cues2_offset = musx->loops_offset + read_32bit(musx->loops_offset+0x0c, streamFile);
+ for (i = 0; i < cues2_count; i++) {
+ uint32_t type, offset1, offset2;
+
+ if (musx->is_old) {
+ offset1 = read_32bit(cues2_offset + i*0x20 + 0x04, streamFile);
+ type = read_32bit(cues2_offset + i*0x20 + 0x08, streamFile);
+ offset2 = read_32bit(cues2_offset + i*0x20 + 0x14, streamFile);
+ } else {
+ offset1 = read_32bit(cues2_offset + i*0x14 + 0x04, streamFile);
+ type = read_32bit(cues2_offset + i*0x14 + 0x08, streamFile);
+ offset2 = read_32bit(cues2_offset + i*0x14 + 0x0c, streamFile);
}
+
+ /* other types (0x0a, 0x09) look like section/end markers, 0x06/07 only seems to exist once */
+ if (type == 0x06 || type == 0x07) {
+ musx->loop_start = offset2;
+ musx->loop_end = offset1;
+ musx->loop_flag = 1;
+ break;
+ }
+ }
+ }
+ else if (musx->loops_offset && read_32bitBE(musx->loops_offset, streamFile) != 0xABABABAB) {
+ /* parse loop table (loop starts are -1 if non-looping)
+ * 0x00: version?
+ * 0x04: flags? (&1=loops)
+ * 0x08: loop start offset?
+ * 0x0c: loop end offset?
+ * 0x10: loop end sample
+ * 0x14: loop start sample
+ * 0x18: loop end offset
+ * 0x1c: loop start offset */
+ musx->loop_end_sample = read_32bitLE(musx->loops_offset+0x10, streamFile);
+ musx->loop_start_sample = read_32bitLE(musx->loops_offset+0x14, streamFile);
+ musx->loop_end = read_32bitLE(musx->loops_offset+0x18, streamFile);
+ musx->loop_start = read_32bitLE(musx->loops_offset+0x1c, streamFile);
+ musx->num_samples = musx->loop_end_sample; /* preferable even if not looping as some files have padding */
+ musx->loop_flag = (musx->loop_start_sample >= 0);
+ }
+
+ /* fix some v10 sizes */
+ if (musx->stream_size == 0) {
+ off_t offset;
+ musx->stream_size = musx->file_size - musx->stream_offset;
+
+ /* remove padding */
+ offset = musx->stream_offset + musx->stream_size - 0x04;
+ while (offset > 0) {
+ if (read_32bit(offset, streamFile) != 0xABABABAB)
+ break;
+ musx->stream_size -= 0x04;
+ offset -= 0x04;
+ }
+ }
+
+ return 1;
+fail:
+ return 0;
+}
+
+static int parse_musx(STREAMFILE *streamFile, musx_header *musx) {
+ int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
+ int target_subsong = streamFile->stream_index;
+
+
+ /* base header is LE */
+ /* 0x04: file ID (referenced in external .h, sometimes files are named HC(id).sfx too) */
+ musx->version = read_32bitLE(0x08,streamFile);
+ musx->file_size = read_32bitLE(0x0c,streamFile);
+
+
+ switch(musx->version) {
+ case 201: /* Sphinx and the Cursed Mummy (PS2), Buffy the Vampire Slayer: Chaos Bleeds (PS2/GC/Xbox) */
+ case 1: /* Athens 2004 (PS2) */
+ musx->platform = 0; /* guess later */
+ musx->tables_offset = 0x10;
+ musx->big_endian = guess_endianness32bit(0x10, streamFile);
+ musx->is_old = 1;
+ break;
+
+ case 4: /* Spyro: A Hero's Tail (PS2/Xbox/GC) */
+ case 5: /* Predator: Concrete Jungle (PS2/Xbox), Robots (GC) */
+ case 6: /* Batman Begins (GC), Ice Age 2 (PS2) */
+ musx->platform = read_32bitBE(0x10,streamFile);
+ /* 0x14: some id/hash? */
+ /* 0x18: platform number? */
+ /* 0x1c: null */
+ musx->tables_offset = 0x20;
+ musx->big_endian = (musx->platform == 0x47435F5F); /* "GC__" */
+ break;
+
+ case 10: /* 007: Quantum of Solace (PS2), Pirates of the Caribbean: At World's End (PSP), GoldenEye 007 (Wii), Rio (PS3) */
+ musx->platform = read_32bitBE(0x10,streamFile);
+ /* 0x14: null */
+ /* 0x18: platform number? */
+ /* 0x1c: null */
+ /* 0x20: hash */
+ musx->tables_offset = 0; /* no tables */
+ musx->big_endian = (musx->platform == 0x5749495F || musx->platform == 0x5053335F); /* "GC__" / "PS3_" (only after header) */
+ break;
+
+ default:
+ goto fail;
+ }
+
+ if (musx->big_endian) {
+ read_32bit = read_32bitBE;
+ } else {
+ read_32bit = read_32bitLE;
+ }
+
+
+ /* MUSX has multiple forms which seem external/implicit info so we need some crude autodetection */
+ if (musx->tables_offset) {
+ off_t table1_offset, table2_offset /*, table3_offset, table4_offset*/;
+ size_t /*table1_size, table2_size, table3_size,*/ table4_size;
+
+ table1_offset = read_32bit(musx->tables_offset+0x00, streamFile);
+ //table1_size = read_32bit(musx->tables_offset+0x04, streamFile);
+ table2_offset = read_32bit(musx->tables_offset+0x08, streamFile);
+ //table2_size = read_32bit(musx->tables_offset+0x0c, streamFile);
+ //table3_offset = read_32bit(musx->tables_offset+0x10, streamFile);
+ //table3_size = read_32bit(musx->tables_offset+0x14, streamFile);
+ //table4_offset = read_32bit(musx->tables_offset+0x18, streamFile);
+ table4_size = read_32bit(musx->tables_offset+0x1c, streamFile);
+
+ if (table2_offset == 0 || table2_offset == 0xABABABAB) {
+ /* possibly a collection of IDs */
+ goto fail;
+ }
+ else if (table4_size != 0 && table4_size != 0xABABABAB) {
+ /* sfx banks have table1=id table? table2=header table, table=DSP coefs table (optional), table4=data */
+ musx->form = SFX_BANK;
+ }
+ else if (read_32bit(table1_offset+0x00, streamFile) < 0x9 &&
+ read_32bit(table1_offset+0x08, streamFile) == 0x14 &&
+ read_32bit(table1_offset+0x10, streamFile) <= 100) {
+ /* streams have a info table with a certain format */
+ musx->form = MFX;
+ }
+ else if (read_32bit(table1_offset+0x00, streamFile) == 0 &&
+ read_32bit(table1_offset+0x04, streamFile) > read_32bit(table1_offset+0x00, streamFile) &&
+ read_32bit(table1_offset+0x08, streamFile) > read_32bit(table1_offset+0x04, streamFile)) {
+ /* a list of offsets starting from 0, each stream then has info table at offset */
+ musx->form = MFX_BANK;
+ }
+ else {
+ goto fail;
+ }
+ }
+ else {
+ if (read_32bitBE(0x800,streamFile) == 0x53424E4B) { /* "SBNK" */
+ /* SFX_BANK-like sound bank found on v10 Wii games */
+ musx->form = SBNK;
+ }
+ else if (read_32bitBE(0x800,streamFile) == 0x464F524D) { /* "FORM" */
+ /* RIFF-like sound bank found on v10 PSP games */
+ musx->form = FORM;
+ }
+ else if (read_32bitBE(0x800,streamFile) == 0x45535044) { /* "ESPD" */
+ /* projectdetails.sfx */
+ goto fail;
+ }
+ else {
+ musx->form = MFX;
+ }
+ }
+
+
+ /* parse known forms */
+ switch(musx->form) {
+ case MFX:
+
+ if (musx->tables_offset) {
+ musx->loops_offset = read_32bit(musx->tables_offset+0x00, streamFile);
+
+ musx->stream_offset = read_32bit(musx->tables_offset+0x08, streamFile);
+ musx->stream_size = read_32bit(musx->tables_offset+0x0c, streamFile);
}
else {
- vgmstream->sample_rate = 44100;
- vgmstream->num_samples = (get_streamfile_size(streamFile)-0x800)/2/(0x20)*((0x20-4)*2);
- if (loop_flag)
- {
- vgmstream->loop_start_sample = read_32bitLE(0x44,streamFile);
- vgmstream->loop_end_sample = read_32bitLE(0x40,streamFile);
- }
- }
- break;
- case 0x5749495F: /* WII_ */
- start_offset = 0x800;
- vgmstream->channels = channel_count;
- vgmstream->sample_rate = read_32bitLE(0x4C,streamFile);
- switch (read_32bitBE(0x40,streamFile))
- {
- case 0x44415434: /* DAT4 */
- case 0x44415438: /* DAT8 [GoldenEye 007 (Wii)] */
- vgmstream->coding_type = coding_DAT4_IMA;
- break;
- default:
- goto fail;
- }
- vgmstream->num_samples = read_32bitLE(0x60,streamFile);
- vgmstream->layout_type = layout_interleave;
- vgmstream->interleave_block_size = 0x20;
- vgmstream->meta_type = meta_MUSX_V010;
- if (loop_flag)
- {
- vgmstream->loop_start_sample = read_32bitLE(0x64,streamFile);
- vgmstream->loop_end_sample = read_32bitLE(0x60,streamFile);
+ if (read_32bitBE(0x30, streamFile) != 0xABABABAB) {
+ uint32_t miniheader = read_32bitBE(0x40, streamFile);
+ switch(miniheader) {
+ case 0x44415434: /* "DAT4" */
+ case 0x44415438: /* "DAT8" */
+ /* found on PS3/Wii (but not always?) */
+ musx->stream_size = read_32bitLE(0x44, streamFile);
+ musx->channels = read_32bitLE(0x48, streamFile);
+ musx->sample_rate = read_32bitLE(0x4c, streamFile);
+ musx->loops_offset = 0x50;
+ break;
+ default:
+ musx->loops_offset = 0x30;
+ break;
+ }
+ }
+
+ musx->stream_offset = 0x800;
+ musx->stream_size = 0; /* read later */
}
+ if (!parse_musx_stream(streamFile, musx))
+ goto fail;
break;
- case 0x58455F5F: /* XE__ */
- start_offset = 0x800;
- vgmstream->sample_rate = 32000;
- vgmstream->coding_type = coding_DAT4_IMA;
- vgmstream->num_samples = (get_streamfile_size(streamFile)-0x800)/2/(0x20)*((0x20-4)*2);
- vgmstream->layout_type = layout_interleave;
- vgmstream->interleave_block_size = 0x20;
- vgmstream->meta_type = meta_MUSX_V010;
-
+
+ case MFX_BANK: {
+ off_t target_offset, base_offset, data_offset;
+
+ musx->total_subsongs = read_32bit(musx->tables_offset+0x04, streamFile) / 0x04; /* size */
+ if (target_subsong == 0) target_subsong = 1;
+ if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail;
+
+ base_offset = read_32bit(musx->tables_offset+0x00, streamFile);
+ data_offset = read_32bit(musx->tables_offset+0x08, streamFile);
+
+ target_offset = read_32bit(base_offset + (target_subsong - 1)*0x04, streamFile) + data_offset;
+
+ /* 0x00: id? */
+ musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset;
+ musx->stream_size = read_32bit(target_offset + 0x08, streamFile);
+ musx->loops_offset = target_offset + 0x0c;
+
+ /* this looks correct for PS2 and Xbox, not sure about GC */
+ musx->channels = 1;
+ musx->sample_rate = 22050;
+
+ if (!parse_musx_stream(streamFile, musx))
+ goto fail;
break;
+ }
+
+ case SFX_BANK: {
+ off_t target_offset, head_offset, coef_offset, data_offset;
+ size_t coef_size;
+
+ //unk_offset = read_32bit(musx->tables_offset+0x00, streamFile); /* ids and cue-like config? */
+ head_offset = read_32bit(musx->tables_offset+0x08, streamFile);
+ coef_offset = read_32bit(musx->tables_offset+0x10, streamFile);
+ coef_size = read_32bit(musx->tables_offset+0x14, streamFile);
+ data_offset = read_32bit(musx->tables_offset+0x18, streamFile);
+
+ musx->total_subsongs = read_32bit(head_offset+0x00, streamFile);
+ if (target_subsong == 0) target_subsong = 1;
+ if (target_subsong > musx->total_subsongs || musx->total_subsongs <= 0) goto fail;
+
+
+ if (musx->is_old) {
+ target_offset = head_offset + 0x04 + (target_subsong - 1)*0x28;
+
+ /* 0x00: flag? */
+ musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset;
+ musx->stream_size = read_32bit(target_offset + 0x08, streamFile);
+ musx->sample_rate = read_32bit(target_offset + 0x0c, streamFile);
+ /* 0x10: size? */
+ /* 0x14: channels? */
+ /* 0x18: always 4? */
+ musx->coefs_offset = read_32bit(target_offset + 0x1c, streamFile) + coef_offset; /* may be set even for non-GC */
+ /* 0x20: null? */
+ /* 0x24: sub-id? */
+ musx->channels = 1;
+ }
+ else {
+ target_offset = head_offset + 0x04 + (target_subsong - 1)*0x20;
+
+ /* 0x00: flag? */
+ musx->stream_offset = read_32bit(target_offset + 0x04, streamFile) + data_offset;
+ musx->stream_size = read_32bit(target_offset + 0x08, streamFile);
+ musx->sample_rate = read_32bit(target_offset + 0x0c, streamFile);
+ /* 0x10: size? */
+ musx->coefs_offset = read_32bit(target_offset + 0x14, streamFile) + coef_offset; /* may be set even for non-GC */
+ /* 0x18: null? */
+ /* 0x1c: sub-id? */
+ musx->channels = 1;
+ }
+
+
+ VGM_LOG("to=%lx, %lx, %x\n", target_offset, musx->stream_offset, musx->stream_size);
+
+ if (coef_size == 0)
+ musx->coefs_offset = 0; /* disable for later detection */
+
+ if (!parse_musx_stream(streamFile, musx))
+ goto fail;
+ break;
+ }
+
default:
+ VGM_LOG("MUSX: unknown form\n");
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;
+ return 1;
fail:
- close_vgmstream(vgmstream);
- return NULL;
-}
-
-
-/* MUSX (Version 201) */
-VGMSTREAM * init_vgmstream_musx_v201(STREAMFILE *streamFile) {
- VGMSTREAM * vgmstream = NULL;
- char filename[PATH_LIMIT];
- off_t start_offset;
- //int musx_version; /* 0x08 provides a "version" byte */
- int loop_flag;
- int channel_count;
- int loop_detect;
- int loop_offsets;
-
- /* check extension, case insensitive */
- streamFile->get_name(streamFile,filename,sizeof(filename));
- if (strcasecmp("musx",filename_extension(filename))) goto fail;
-
- /* check header */
- if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */
- goto fail;
- if ((read_32bitBE(0x08,streamFile) != 0xC9000000) &&
- (read_32bitLE(0x08,streamFile) != 0xC9000000)) /* "0xC9000000" */
- goto fail;
-
- channel_count = 2;
-
- loop_detect = read_32bitBE(0x800,streamFile);
- switch (loop_detect) {
- case 0x02000000:
- loop_offsets = 0x8E0;
- break;
- case 0x03000000:
- loop_offsets = 0x880;
- break;
- case 0x04000000:
- loop_offsets = 0x8B4;
- break;
- case 0x05000000:
- loop_offsets = 0x8E8;
- break;
- case 0x06000000:
- loop_offsets = 0x91C;
- break;
- default:
- goto fail;
- }
-
- loop_flag = (read_32bitLE(loop_offsets+0x04,streamFile) !=0x00000000);
- start_offset = read_32bitLE(0x18,streamFile);
-
- /* build the VGMSTREAM */
- vgmstream = allocate_vgmstream(channel_count,loop_flag);
- if (!vgmstream) goto fail;
-
- {
- vgmstream->channels = channel_count;
- vgmstream->sample_rate = 32000;
- vgmstream->coding_type = coding_PSX;
- vgmstream->num_samples = read_32bitLE(loop_offsets,streamFile)*28/16/channel_count;
- if (loop_flag) {
- vgmstream->loop_start_sample = read_32bitLE(loop_offsets+0x10,streamFile)*28/16/channel_count;
- vgmstream->loop_end_sample = read_32bitLE(loop_offsets,streamFile)*28/16/channel_count;
- }
- vgmstream->layout_type = layout_interleave;
- vgmstream->interleave_block_size = 0x80;
- vgmstream->meta_type = meta_MUSX_V201;
- }
-
- /* 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;
-
-fail:
- close_vgmstream(vgmstream);
- return NULL;
+ return 0;
}
diff --git a/src/meta/mzrt.c b/src/meta/mzrt.c
new file mode 100644
index 00000000..6acb6250
--- /dev/null
+++ b/src/meta/mzrt.c
@@ -0,0 +1,144 @@
+#include "meta.h"
+#include "../coding/coding.h"
+#include "mzrt_streamfile.h"
+
+
+/* mzrt - idTech "4.5" audio found in .resource bigfiles (w/ internal filenames) [Doom 3 BFG edition (PC/PS3/X360)] */
+VGMSTREAM * init_vgmstream_mzrt(STREAMFILE *streamFile) {
+ VGMSTREAM * vgmstream = NULL;
+ off_t start_offset;
+ int loop_flag = 0, channel_count, codec, sample_rate, block_size = 0, bps = 0, num_samples;
+ STREAMFILE *temp_streamFile = NULL;
+
+
+ /* checks */
+ if (!check_extensions(streamFile, "idwav,idmsf,idxma"))
+ goto fail;
+
+ if (read_32bitBE(0x00,streamFile) != 0x6D7A7274) /* "mzrt" */
+ goto fail;
+
+ /* this format is bizarrely mis-aligned (and mis-designed too) */
+
+ num_samples = read_32bitBE(0x11,streamFile);
+ codec = read_16bitLE(0x15,streamFile);
+ switch(codec) {
+ case 0x0001:
+ case 0x0002:
+ case 0x0166:
+ channel_count = read_16bitLE(0x17,streamFile);
+ sample_rate = read_32bitLE(0x19, streamFile);
+ block_size = read_16bitLE(0x21, streamFile);
+ bps = read_16bitLE(0x23,streamFile);
+
+ start_offset = 0x25;
+ break;
+
+ case 0x0000:
+ sample_rate = read_32bitBE(0x1D, streamFile);
+ channel_count = read_32bitBE(0x21, streamFile);
+
+ start_offset = 0x29;
+ break;
+
+ default:
+ goto fail;
+ }
+
+ /* skip fmt-extra data */
+ if (codec == 0x0002 || codec == 0x0166) {
+ start_offset += 0x02 + read_16bitLE(start_offset, streamFile);
+ }
+
+ /* skip unknown table */
+ if (codec == 0x0000) {
+ start_offset += 0x04 + read_32bitBE(start_offset, streamFile) * 0x04;
+
+ }
+
+ /* skip unknown table */
+ start_offset += 0x04 + read_32bitBE(start_offset, streamFile);
+
+ /* skip block info */
+ if (codec != 0x0000) {
+ /* 0x00: de-blocked size
+ * 0x04: block count*/
+ start_offset += 0x08;
+
+ /* idwav only uses 1 super-block though */
+ temp_streamFile = setup_mzrt_streamfile(streamFile, start_offset);
+ if (!temp_streamFile) goto fail;
+ }
+ else {
+ /* 0x00: de-blocked size */
+ start_offset += 0x04;
+ }
+
+
+ /* build the VGMSTREAM */
+ vgmstream = allocate_vgmstream(channel_count, loop_flag);
+ if (!vgmstream) goto fail;
+
+ vgmstream->meta_type = meta_MZRT;
+ vgmstream->sample_rate = sample_rate;
+ vgmstream->num_samples = num_samples;
+
+ switch(codec) {
+ case 0x0001:
+ if (bps != 16) goto fail;
+ vgmstream->coding_type = coding_PCM16LE;
+ vgmstream->layout_type = layout_interleave;
+ vgmstream->interleave_block_size = block_size / channel_count;
+ break;
+
+ case 0x0002:
+ if (bps != 4) goto fail;
+ vgmstream->coding_type = coding_MSADPCM;
+ vgmstream->layout_type = layout_none;
+ vgmstream->interleave_block_size = block_size;
+ break;
+
+#ifdef VGM_USE_FFMPEG
+ case 0x0166: {
+ uint8_t buf[0x100];
+ int bytes;
+ size_t stream_size = get_streamfile_size(temp_streamFile);
+
+ bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,sizeof(buf), 0x15,0x34, stream_size, streamFile, 0);
+ vgmstream->codec_data = init_ffmpeg_header_offset(temp_streamFile, buf,bytes, 0x00,stream_size);
+ if (!vgmstream->codec_data) goto fail;
+ vgmstream->coding_type = coding_FFmpeg;
+ vgmstream->layout_type = layout_none;
+
+ xma_fix_raw_samples_hb(vgmstream, streamFile, temp_streamFile, 0x00,stream_size, 0x15, 0,0);
+ break;
+ }
+#endif
+
+#ifdef VGM_USE_MPEG
+ case 0x0000: {
+ mpeg_custom_config cfg = {0};
+
+ cfg.skip_samples = 576; /* assumed */
+
+ vgmstream->codec_data = init_mpeg_custom(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
+ if (!vgmstream->codec_data) goto fail;
+ vgmstream->layout_type = layout_none;
+ break;
+ }
+#endif
+
+ default:
+ goto fail;
+ }
+
+ if (!vgmstream_open_stream(vgmstream,temp_streamFile == NULL ? streamFile : temp_streamFile,temp_streamFile == NULL ? start_offset : 0x00))
+ goto fail;
+ close_streamfile(temp_streamFile);
+ return vgmstream;
+
+fail:
+ close_streamfile(temp_streamFile);
+ close_vgmstream(vgmstream);
+ return NULL;
+}
diff --git a/src/meta/mzrt_streamfile.h b/src/meta/mzrt_streamfile.h
new file mode 100644
index 00000000..f9483d37
--- /dev/null
+++ b/src/meta/mzrt_streamfile.h
@@ -0,0 +1,124 @@
+#ifndef _MZRT_STREAMFILE_H_
+#define _MZRT_STREAMFILE_H_
+#include "../streamfile.h"
+
+
+typedef struct {
+ /* config */
+ off_t stream_offset;
+ size_t stream_size;
+
+ /* state */
+ off_t logical_offset; /* fake offset */
+ off_t physical_offset; /* actual offset */
+ size_t block_size; /* current size */
+ size_t skip_size; /* size from block start to reach data */
+ size_t data_size; /* usable size in a block */
+
+ size_t logical_size;
+} mzrt_io_data;
+
+
+static size_t mzrt_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, mzrt_io_data* data) {
+ size_t total_read = 0;
+
+
+ /* re-start when previous offset (can't map logical<>physical offsets) */
+ if (data->logical_offset < 0 || offset < data->logical_offset) {
+ data->physical_offset = data->stream_offset;
+ data->logical_offset = 0x00;
+ data->data_size = 0;
+ }
+
+ /* read blocks */
+ while (length > 0) {
+
+ /* ignore EOF */
+ if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) {
+ break;
+ }
+
+ /* process new block */
+ if (data->data_size == 0) {
+ /* 0x00: samples in this block */
+ data->data_size = read_32bitBE(data->stream_offset + 0x04, streamfile);
+ data->skip_size = 0x08;
+ data->block_size = data->skip_size + data->data_size;
+ }
+
+ /* move to next block */
+ if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) {
+ data->physical_offset += data->block_size;
+ data->logical_offset += data->data_size;
+ data->data_size = 0;
+ continue;
+ }
+
+ /* read data */
+ {
+ size_t bytes_consumed, bytes_done, to_read;
+
+ bytes_consumed = offset - data->logical_offset;
+ to_read = data->data_size - bytes_consumed;
+ if (to_read > length)
+ to_read = length;
+ bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
+
+ total_read += bytes_done;
+ dest += bytes_done;
+ offset += bytes_done;
+ length -= bytes_done;
+
+ if (bytes_done != to_read || bytes_done == 0) {
+ break; /* error/EOF */
+ }
+ }
+ }
+
+ return total_read;
+}
+
+static size_t mzrt_io_size(STREAMFILE *streamfile, mzrt_io_data* data) {
+ uint8_t buf[1];
+
+ if (data->logical_size)
+ return data->logical_size;
+
+ /* force a fake read at max offset, to get max logical_offset (will be reset next read) */
+ mzrt_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
+ data->logical_size = data->logical_offset;
+
+ return data->logical_size;
+}
+
+/* Handles deinterleaving of MZRT blocked streams */
+static STREAMFILE* setup_mzrt_streamfile(STREAMFILE *streamFile, off_t stream_offset) {
+ STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
+ mzrt_io_data io_data = {0};
+ size_t io_data_size = sizeof(mzrt_io_data);
+
+ io_data.stream_offset = stream_offset;
+ io_data.stream_size = get_streamfile_size(streamFile) - stream_offset;
+ io_data.logical_offset = -1; /* force phys offset reset */
+
+ /* setup subfile */
+ new_streamFile = open_wrap_streamfile(streamFile);
+ if (!new_streamFile) goto fail;
+ temp_streamFile = new_streamFile;
+
+ new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, mzrt_io_read,mzrt_io_size);
+ if (!new_streamFile) goto fail;
+ temp_streamFile = new_streamFile;
+
+ new_streamFile = open_buffer_streamfile(new_streamFile,0);
+ if (!new_streamFile) goto fail;
+ temp_streamFile = new_streamFile;
+
+ return temp_streamFile;
+
+fail:
+ close_streamfile(temp_streamFile);
+ return NULL;
+}
+
+#endif /* _MZRT_STREAMFILE_H_ */
diff --git a/src/meta/ngc_dsp_std.c b/src/meta/ngc_dsp_std.c
index 77a6c07f..f00024c4 100644
--- a/src/meta/ngc_dsp_std.c
+++ b/src/meta/ngc_dsp_std.c
@@ -1187,8 +1187,8 @@ fail:
return NULL;
}
-/* .adpcmx - AQUASTYLE wrapper [Touhou Genso Wanderer -Reloaded- (Switch)] */
-VGMSTREAM * init_vgmstream_dsp_adpcmx(STREAMFILE *streamFile) {
+/* ADPY - AQUASTYLE wrapper [Touhou Genso Wanderer -Reloaded- (Switch)] */
+VGMSTREAM * init_vgmstream_dsp_adpy(STREAMFILE *streamFile) {
dsp_meta dspm = {0};
/* checks */
@@ -1196,6 +1196,7 @@ VGMSTREAM * init_vgmstream_dsp_adpcmx(STREAMFILE *streamFile) {
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x41445059) /* "ADPY" */
goto fail;
+
/* 0x04(2): 1? */
/* 0x08: some size? */
/* 0x0c: null */
@@ -1209,7 +1210,36 @@ VGMSTREAM * init_vgmstream_dsp_adpcmx(STREAMFILE *streamFile) {
dspm.start_offset = dspm.header_offset + dspm.header_spacing*dspm.channel_count;
dspm.interleave = 0x08;
- dspm.meta_type = meta_DSP_ADPCMX;
+ dspm.meta_type = meta_DSP_ADPY;
+ return init_vgmstream_dsp_common(streamFile, &dspm);
+fail:
+ return NULL;
+}
+
+/* ADPX - AQUASTYLE wrapper [Fushigi no Gensokyo: Lotus Labyrinth (Switch)] */
+VGMSTREAM * init_vgmstream_dsp_adpx(STREAMFILE *streamFile) {
+ dsp_meta dspm = {0};
+
+ /* checks */
+ if (!check_extensions(streamFile, "adpcmx"))
+ goto fail;
+ if (read_32bitBE(0x00,streamFile) != 0x41445058) /* "ADPX" */
+ goto fail;
+
+ /* from 0x04 *6 are probably channel sizes, so max would be 6ch; this assumes 2ch */
+ if (read_32bitLE(0x04,streamFile) != read_32bitLE(0x08,streamFile) &&
+ read_32bitLE(0x0c,streamFile) != 0)
+ goto fail;
+ dspm.channel_count = 2;
+ dspm.max_channels = 2;
+ dspm.little_endian = 1;
+
+ dspm.header_offset = 0x1c;
+ dspm.header_spacing = read_32bitLE(0x04,streamFile);
+ dspm.start_offset = dspm.header_offset + 0x60;
+ dspm.interleave = dspm.header_spacing;
+
+ dspm.meta_type = meta_DSP_ADPX;
return init_vgmstream_dsp_common(streamFile, &dspm);
fail:
return NULL;
diff --git a/src/meta/ogg_vorbis.c b/src/meta/ogg_vorbis.c
index c7cd4896..6847a6d1 100644
--- a/src/meta/ogg_vorbis.c
+++ b/src/meta/ogg_vorbis.c
@@ -172,6 +172,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
int is_gwm = 0;
int is_mus = 0;
int is_lse = 0;
+ int is_bgm = 0;
/* check extension */
@@ -201,6 +202,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
is_mus = 1;
} else if (check_extensions(streamFile,"lse")) { /* .lse: Labyrinth of Refrain: Coven of Dusk (PC) */
is_lse = 1;
+ } else if (check_extensions(streamFile,"bgm")) { /* .bgm: Fortissimo (PC) */
+ is_bgm = 1;
} else {
goto fail;
}
@@ -413,6 +416,23 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
}
}
+ if (is_bgm) { /* [Fortissimo (PC)] */
+ size_t file_size = get_streamfile_size(streamFile);
+ uint8_t key[0x04];
+ uint32_t xor_be;
+
+ put_32bitLE(key, (uint32_t)file_size);
+ xor_be = (uint32_t)get_32bitBE(key);
+ if ((read_32bitBE(0x00,streamFile) ^ xor_be) == 0x4F676753) { /* "OggS" */
+ int i;
+ cfg.key_len = 4;
+ for (i = 0; i < cfg.key_len; i++) {
+ cfg.key[i] = key[i];
+ }
+ cfg.is_encrypted = 1;
+ }
+ }
+
if (cfg.is_encrypted) {
ovmi.meta_type = meta_OGG_encrypted;
diff --git a/src/meta/ps_headerless.c b/src/meta/ps_headerless.c
index 8a4a7d97..30dd869e 100644
--- a/src/meta/ps_headerless.c
+++ b/src/meta/ps_headerless.c
@@ -1,7 +1,5 @@
#include "meta.h"
-#include "../util.h"
-
-static int check_psadpcm(STREAMFILE *streamFile);
+#include "../coding/coding.h"
/* headerless PS-ADPCM - from Katamary Damacy (PS2), Air (PS2), Aladdin: Nasira's Revenge (PS1)
@@ -51,7 +49,7 @@ VGMSTREAM * init_vgmstream_ps_headerless(STREAMFILE *streamFile) {
goto fail;
/* test if raw PS-ADPCM */
- if (!check_psadpcm(streamFile))
+ if (!ps_check_format(streamFile, 0x00, 0x2000))
goto fail;
@@ -286,26 +284,3 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
-
-/* tests some PS-ADPCM frames */
-static int check_psadpcm(STREAMFILE *streamFile) {
- off_t offset, max_offset;
-
- max_offset = get_streamfile_size(streamFile);
- if (max_offset > 0x2000)
- max_offset = 0x2000;
-
- offset = 0x00;
- while (offset < max_offset) {
- uint8_t predictor = (read_8bit(offset+0x00,streamFile) >> 4) & 0x0f;
- uint8_t flags = read_8bit(offset+0x01,streamFile);
-
- if (predictor > 5 || flags > 7)
- goto fail;
- offset += 0x10;
- }
-
- return 1;
-fail:
- return 0;
-}
diff --git a/src/meta/str_wav.c b/src/meta/str_wav.c
index aa72e9e2..8cfd0976 100644
--- a/src/meta/str_wav.c
+++ b/src/meta/str_wav.c
@@ -2,7 +2,7 @@
#include "../coding/coding.h"
-typedef enum { PSX, DSP, XBOX, WMA, IMA } strwav_codec;
+typedef enum { PSX, DSP, XBOX, WMA, IMA, XMA2 } strwav_codec;
typedef struct {
int32_t channels;
int32_t sample_rate;
@@ -32,12 +32,15 @@ VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) {
/* checks */
- if (!check_extensions(streamFile, "str"))
+ if (!check_extensions(streamFile, "str,data"))
goto fail;
/* get external header (extracted with filenames from bigfiles) */
{
- /* try with standard file.wav.str=body + file.wav=header (or file.wma.str + file.wma for Fuzion Frenzy (Xbox)) */
+ /* try body+header combos:
+ * - file.wav.str=body + file.wav=header [common]
+ * - file.wma.str + file.wma [Fuzion Frenzy (Xbox)]
+ * - file.data + file (extensionless) [SpongeBob's Surf & Skate Roadtrip (X360)] */
char basename[PATH_LIMIT];
get_streamfile_basename(streamFile,basename,PATH_LIMIT);
streamHeader = open_streamfile_by_filename(streamFile, basename);
@@ -51,7 +54,8 @@ VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) {
}
}
else {
- if (!check_extensions(streamHeader, "wav,wma"))
+ /* header must have known extensions */
+ if (!check_extensions(streamHeader, "wav,wma,"))
goto fail;
}
}
@@ -66,7 +70,7 @@ VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) {
goto fail;
/* &0x01: loop?, &0x02: non-mono?, &0x04: stream???, &0x08: unused? */
- if (strwav.flags != 0x07 && strwav.flags != 0x06 && strwav.flags != 0x05 && strwav.flags != 0x04 && strwav.flags != 0x02) {
+ if (strwav.flags > 0x07) {
VGM_LOG("STR+WAV: unknown flags\n");
goto fail;
}
@@ -150,6 +154,27 @@ VGMSTREAM * init_vgmstream_str_wav(STREAMFILE *streamFile) {
}
#endif
+#ifdef VGM_USE_FFMPEG
+ case XMA2: {
+ uint8_t buf[0x100];
+ size_t stream_size;
+ size_t bytes, block_size, block_count;
+
+ stream_size = get_streamfile_size(streamFile);
+ block_size = 0x10000;
+ block_count = stream_size / block_size; /* not accurate? */
+
+ bytes = ffmpeg_make_riff_xma2(buf,0x100, strwav.num_samples, stream_size, strwav.channels, strwav.sample_rate, block_count, block_size);
+ vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, 0x00,stream_size);
+ if (!vgmstream->codec_data) goto fail;
+ vgmstream->coding_type = coding_FFmpeg;
+ vgmstream->layout_type = layout_none;
+
+ xma_fix_raw_samples(vgmstream, streamFile, 0x00,stream_size, 0, 0,0);
+ break;
+ }
+#endif
+
default:
goto fail;
}
@@ -431,7 +456,7 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
if ((read_32bitBE(0x04,streamHeader) == 0x00000800 ||
read_32bitBE(0x04,streamHeader) == 0x00000700) && /* rare? */
read_32bitLE(0x08,streamHeader) != 0x00000000 &&
- read_32bitBE(0x0c,streamHeader) == header_size && /* variable per DSP header */
+ read_32bitBE(0x0c,streamHeader) == header_size && /* variable per header */
read_32bitBE(0x7c,streamHeader) != 0 && /* has DSP header */
read_32bitBE(0x38,streamHeader) == read_32bitBE(read_32bitBE(0x7c,streamHeader)+0x38,streamHeader) /* sample rate vs 1st DSP header */
) {
@@ -455,7 +480,7 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
if ((read_32bitBE(0x04,streamHeader) == 0x00000800 ||
read_32bitBE(0x04,streamHeader) == 0x00000700) && /* rare? */
read_32bitLE(0x08,streamHeader) != 0x00000000 &&
- read_32bitBE(0x0c,streamHeader) == header_size && /* variable per DSP header */
+ read_32bitBE(0x0c,streamHeader) == header_size && /* variable per header */
read_32bitBE(0x7c,streamHeader) == 0 /* not DSP header */
) {
strwav->loop_start = 0; //read_32bitLE(0x24,streamHeader); //not ok?
@@ -473,6 +498,28 @@ static int parse_header(STREAMFILE* streamHeader, strwav_header* strwav) {
return 1;
}
+ /* SpongeBob's Surf & Skate Roadtrip (X360)[2011] */
+ if ((read_32bitBE(0x04,streamHeader) == 0x00000800 || /* used? */
+ read_32bitBE(0x04,streamHeader) == 0x00000700) &&
+ read_32bitLE(0x08,streamHeader) != 0x00000000 &&
+ read_32bitBE(0x0c,streamHeader) == 0x124 && /* variable, not sure about final calc */
+ read_32bitBE(0x8c,streamHeader) == 0x180 /* encoder delay actually */
+ //0x4c is data_size + 0x210
+ ) {
+ strwav->loop_start = 0; //read_32bitLE(0x24,streamHeader); //not ok?
+ strwav->num_samples = read_32bitBE(0x30,streamHeader);//todo sometimes wrong?
+ strwav->loop_end = read_32bitBE(0x34,streamHeader);
+ strwav->sample_rate = read_32bitBE(0x38,streamHeader);
+ strwav->flags = read_32bitBE(0x3c,streamHeader);
+
+ strwav->channels = read_32bitBE(0x70,streamHeader); /* multichannel XMA */
+ strwav->loop_flag = strwav->flags & 0x01;
+
+ strwav->codec = XMA2;
+ //;VGM_LOG("STR+WAV: header SBSSR (X360)\n");
+ return 1;
+ }
+
/* unknown */
goto fail;
diff --git a/src/meta/ubi_sb.c b/src/meta/ubi_sb.c
index 46a5f522..296e4b51 100644
--- a/src/meta/ubi_sb.c
+++ b/src/meta/ubi_sb.c
@@ -7,7 +7,7 @@
#define SB_MAX_LAYER_COUNT 16 /* arbitrary max */
#define SB_MAX_CHAIN_COUNT 256 /* +150 exist in Tonic Trouble */
-typedef enum { UBI_IMA, UBI_ADPCM, RAW_PCM, RAW_PSX, RAW_DSP, RAW_XBOX, FMT_VAG, FMT_AT3, RAW_AT3, FMT_XMA1, RAW_XMA1, FMT_OGG, FMT_CWAV, FMT_APM } ubi_sb_codec;
+typedef enum { UBI_IMA, UBI_ADPCM, RAW_PCM, RAW_PSX, RAW_DSP, RAW_XBOX, FMT_VAG, FMT_AT3, RAW_AT3, FMT_XMA1, RAW_XMA1, FMT_OGG, FMT_CWAV, FMT_APM, FMT_MPDX } ubi_sb_codec;
typedef enum { UBI_PC, UBI_PS2, UBI_XBOX, UBI_GC, UBI_X360, UBI_PSP, UBI_PS3, UBI_WII, UBI_3DS } ubi_sb_platform;
typedef enum { UBI_NONE = 0, UBI_AUDIO, UBI_LAYER, UBI_SEQUENCE, UBI_SILENCE } ubi_sb_type;
@@ -733,6 +733,15 @@ static VGMSTREAM * init_vgmstream_ubi_sb_base(ubi_sb_header *sb, STREAMFILE *str
}
break;
+ case FMT_MPDX:
+ /* a custom, chunked MPEG format (sigh)
+ * 0x00: samples? (related to size)
+ * 0x04: "2RUS" (apparently "1RUS" for mono files)
+ * Rest is a MPEG-like sync but not an actual MPEG header? (DLLs do refer it as MPEG)
+ * Files may have multiple "2RUS" or just a big one
+ * A companion .csb has some not-too-useful info */
+ goto fail;
+
default:
VGM_LOG("UBI SB: unknown codec\n");
goto fail;
@@ -1520,6 +1529,9 @@ static int parse_stream_codec(ubi_sb_header * sb) {
case 0x02:
switch (sb->version) {
+ case 0x00000000: /* Tonic Trouble Special Edition */
+ sb->codec = FMT_MPDX;
+ break;
case 0x00000007: /* Splinter Cell, Splinter Cell: Pandora Tomorrow */
case 0x00120012: /* Myst IV: Exile */
//todo splinter Cell Essentials
@@ -1769,7 +1781,7 @@ static int parse_sb(ubi_sb_header * sb, STREAMFILE *streamFile, int target_subso
/*header_id =*/ read_32bit(offset + 0x00, streamFile); /* forces buffer read */
header_type = read_32bit(offset + 0x04, streamFile);
- if (header_type <= 0x00 || header_type >= 0x10 || header_type == 0x09) {
+ if (header_type <= 0x00 || header_type >= 0x10) {
VGM_LOG("UBI SB: unknown type %x at %x\n", header_type, (uint32_t)offset);
goto fail;
}
@@ -1962,6 +1974,7 @@ static void config_sb_random_old(ubi_sb_header * sb, off_t sequence_count, off_t
static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
int is_dino_pc = 0;
+ int is_ttse_pc = 0;
int is_bia_ps2 = 0, is_biadd_psp = 0;
int is_sc2_ps2_gc = 0;
int is_sc4_pc_online = 0;
@@ -2051,7 +2064,10 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
* 9: TYPE_THEME_OLD2
* A: TYPE_RANDOM
* B: TYPE_THEME0 (sequence)
- * (only 1, 4, A and B are known)
+ * Only 1, 2, 4, 9, A and B are known.
+ * 2 is used rarely in Donald Duck's demo and point to a .mdx (midi?)
+ * 9 is used in Tonic Trouble Special Edition
+ * Others are common.
*/
/* All types may contain memory garbage, making it harder to identify fields (platforms
@@ -2120,6 +2136,7 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
if (sb->is_bnm) {
//sb->allowed_types[0x0a] = 1; /* only needed inside sequences */
sb->allowed_types[0x0b] = 1;
+ sb->allowed_types[0x09] = 1;
}
#if 0
@@ -2151,6 +2168,36 @@ static int config_sb_version(ubi_sb_header * sb, STREAMFILE *streamFile) {
is_dino_pc = 1;
}
+ /* Tonic Touble beta has garbage instead of version */
+ if (sb->is_bnm && sb->version > 0x00000000 && sb->platform == UBI_PC) {
+ STREAMFILE * streamTest = open_streamfile_by_filename(streamFile, "ED_MAIN.LCB");
+ if (streamTest) {
+ is_ttse_pc = 1;
+ sb->version = 0x00000000;
+ close_streamfile(streamTest);
+ }
+ }
+
+
+ /* Tonic Trouble Special Edition (1999)(PC)-bnm */
+ if (sb->version == 0x00000000 && sb->platform == UBI_PC && is_ttse_pc) {
+ config_sb_entry(sb, 0x20, 0x5c);
+
+ config_sb_audio_fs(sb, 0x2c, 0x2c, 0x30); /* no group id */
+ config_sb_audio_hs(sb, 0x42, 0x3c, 0x38, 0x38, 0x48, 0x44);
+ sb->cfg.audio_has_internal_names = 1;
+
+ config_sb_sequence(sb, 0x24, 0x18);
+
+ //config_sb_random_old(sb, 0x18, 0x0c);
+
+ /* no layers */
+ //todo type 9 needed
+ //todo MPX don't set stream size?
+ return 1;
+ }
+
+
/* Rayman 2: The Great Escape (1999)(PC)-bnm */
/* Tonic Trouble (1999)(PC)-bnm */
/* Donald Duck: Goin' Quackers (2000)(PC)-bnm */
diff --git a/src/meta/xavs.c b/src/meta/xavs.c
new file mode 100644
index 00000000..6e464326
--- /dev/null
+++ b/src/meta/xavs.c
@@ -0,0 +1,87 @@
+#include "meta.h"
+#include "../coding/coding.h"
+#include "../layout/layout.h"
+#include "xavs_streamfile.h"
+
+/* XAVS - Reflections audio and video+audio container [Stuntman (PS2)] */
+VGMSTREAM * init_vgmstream_xavs(STREAMFILE *streamFile) {
+ VGMSTREAM * vgmstream = NULL;
+ off_t start_offset;
+ int loop_flag, channel_count;
+ int total_subsongs, target_subsong = streamFile->stream_index;
+ STREAMFILE *temp_streamFile = NULL;
+
+
+ /* checks */
+ if (!check_extensions(streamFile, "xav"))
+ goto fail;
+ if (read_32bitBE(0x00, streamFile) != 0x58415653) /* "XAVS" */
+ goto fail;
+
+ loop_flag = 0;
+ channel_count = 2;
+ start_offset = 0x00;
+
+ /* 0x04: 16b width + height (0 if file has no video) */
+ /* 0x08: related to video (0 if file has no video) */
+ total_subsongs = read_16bitLE(0x0c, streamFile);
+ /* 0x0c: volume? (0x50, 0x4e) */
+ /* 0x10: biggest video chunk? (0 if file has no video) */
+ /* 0x14: biggest audio chunk? */
+
+ if (target_subsong == 0) target_subsong = 1;
+ if (target_subsong > total_subsongs || total_subsongs <= 0) goto fail;
+
+ /* could use a blocked layout, but this needs interleaved PCM within blocks which can't be done ATM */
+ temp_streamFile = setup_xavs_streamfile(streamFile, 0x18, target_subsong - 1);
+ if (!temp_streamFile) goto fail;
+
+
+ /* build the VGMSTREAM */
+ vgmstream = allocate_vgmstream(channel_count, loop_flag);
+ if (!vgmstream) goto fail;
+
+ vgmstream->meta_type = meta_XAVS;
+ vgmstream->num_streams = total_subsongs;
+
+ vgmstream->coding_type = coding_PCM16LE;
+ vgmstream->layout_type = layout_interleave;
+
+ /* no apparent flags, most videos use 0x41 but not all */
+ {
+ off_t offset = 0x18;
+ while (offset < get_streamfile_size(streamFile)) {
+ uint32_t chunk_id = read_32bitLE(offset+0x00, streamFile) & 0xFF;
+ uint32_t chunk_size = read_32bitLE(offset+0x00, streamFile) >> 8;
+
+ if ((chunk_id & 0xF0) == 0x40) {
+ vgmstream->sample_rate = 48000;
+ vgmstream->interleave_block_size = 0x200;
+ break;
+ } else if ((chunk_id & 0xF0) == 0x60) {
+ vgmstream->sample_rate = 24000;
+ vgmstream->interleave_block_size = 0x100;
+ break;
+ } else if (chunk_id == 0x56) {
+ offset += 0x04 + chunk_size;
+ } else if (chunk_id == 0x21) {
+ offset += 0x04;
+ } else {
+ goto fail;
+ }
+ }
+ }
+
+ vgmstream->num_samples = pcm_bytes_to_samples(get_streamfile_size(temp_streamFile), channel_count, 16);
+
+ if (!vgmstream_open_stream(vgmstream,temp_streamFile,start_offset))
+ goto fail;
+
+ close_streamfile(temp_streamFile);
+ return vgmstream;
+
+fail:
+ close_streamfile(temp_streamFile);
+ close_vgmstream(vgmstream);
+ return NULL;
+}
diff --git a/src/meta/xavs_streamfile.h b/src/meta/xavs_streamfile.h
new file mode 100644
index 00000000..ac5e7d3b
--- /dev/null
+++ b/src/meta/xavs_streamfile.h
@@ -0,0 +1,160 @@
+#ifndef _XAVS_STREAMFILE_H_
+#define _XAVS_STREAMFILE_H_
+#include "../streamfile.h"
+
+
+typedef struct {
+ /* config */
+ off_t stream_offset;
+ size_t stream_size;
+ int stream_number;
+
+ /* state */
+ off_t logical_offset; /* fake offset */
+ off_t physical_offset; /* actual offset */
+ size_t block_size; /* current size */
+ size_t skip_size; /* size from block start to reach data */
+ size_t data_size; /* usable size in a block */
+
+ size_t logical_size;
+} xavs_io_data;
+
+
+static size_t xavs_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset, size_t length, xavs_io_data* data) {
+ size_t total_read = 0;
+
+
+ /* re-start when previous offset (can't map logical<>physical offsets) */
+ if (data->logical_offset < 0 || offset < data->logical_offset) {
+ data->physical_offset = data->stream_offset;
+ data->logical_offset = 0x00;
+ data->data_size = 0;
+ }
+
+ /* read blocks */
+ while (length > 0) {
+
+ /* ignore EOF */
+ if (offset < 0 || data->physical_offset >= data->stream_offset + data->stream_size) {
+ break;
+ }
+
+ /* process new block */
+ if (data->data_size == 0) {
+ uint32_t chunk_id = read_32bitLE(data->physical_offset+0x00, streamfile) & 0xFF;
+ uint32_t chunk_size = read_32bitLE(data->physical_offset+0x00, streamfile) >> 8;
+
+ data->skip_size = 0x04;
+
+ switch(chunk_id) {
+ /* audio */
+ case 0x41:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ data->block_size = 0x04 + chunk_size;
+ if (data->stream_number + 1 == (chunk_id & 0x0F)) {
+ data->data_size = chunk_size;
+ } else {
+ data->data_size = 0; /* ignore other subsongs */
+ }
+ break;
+
+ /* video */
+ case 0x56:
+ data->block_size = 0x04 + chunk_size;
+ data->data_size = 0;
+ break;
+
+ /* empty */
+ case 0x21: /* related to video */
+ case 0x5F: /* "_EOS" */
+ data->block_size = 0x04;
+ data->data_size = 0;
+ break;
+
+ default:
+ VGM_LOG("XAVS: unknown type at %lx\n", data->physical_offset);
+ data->block_size = 0x04;
+ data->data_size = 0;
+ break;
+ }
+ }
+
+ /* move to next block */
+ if (data->data_size == 0 || offset >= data->logical_offset + data->data_size) {
+ data->physical_offset += data->block_size;
+ data->logical_offset += data->data_size;
+ data->data_size = 0;
+ continue;
+ }
+
+ /* read data */
+ {
+ size_t bytes_consumed, bytes_done, to_read;
+
+ bytes_consumed = offset - data->logical_offset;
+ to_read = data->data_size - bytes_consumed;
+ if (to_read > length)
+ to_read = length;
+ bytes_done = read_streamfile(dest, data->physical_offset + data->skip_size + bytes_consumed, to_read, streamfile);
+
+ total_read += bytes_done;
+ dest += bytes_done;
+ offset += bytes_done;
+ length -= bytes_done;
+
+ if (bytes_done != to_read || bytes_done == 0) {
+ break; /* error/EOF */
+ }
+ }
+ }
+
+ return total_read;
+}
+
+static size_t xavs_io_size(STREAMFILE *streamfile, xavs_io_data* data) {
+ uint8_t buf[1];
+
+ if (data->logical_size)
+ return data->logical_size;
+
+ /* force a fake read at max offset, to get max logical_offset (will be reset next read) */
+ xavs_io_read(streamfile, buf, 0x7FFFFFFF, 1, data);
+ data->logical_size = data->logical_offset;
+
+ return data->logical_size;
+}
+
+/* Handles deinterleaving of XAVS blocked streams */
+static STREAMFILE* setup_xavs_streamfile(STREAMFILE *streamFile, off_t stream_offset, int stream_number) {
+ STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
+ xavs_io_data io_data = {0};
+ size_t io_data_size = sizeof(xavs_io_data);
+
+ io_data.stream_offset = stream_offset;
+ io_data.stream_size = get_streamfile_size(streamFile) - stream_offset;
+ io_data.stream_number = stream_number;
+ io_data.logical_offset = -1; /* force phys offset reset */
+
+ /* setup subfile */
+ new_streamFile = open_wrap_streamfile(streamFile);
+ if (!new_streamFile) goto fail;
+ temp_streamFile = new_streamFile;
+
+ new_streamFile = open_io_streamfile(new_streamFile, &io_data,io_data_size, xavs_io_read,xavs_io_size);
+ if (!new_streamFile) goto fail;
+ temp_streamFile = new_streamFile;
+
+ new_streamFile = open_buffer_streamfile(new_streamFile,0);
+ if (!new_streamFile) goto fail;
+ temp_streamFile = new_streamFile;
+
+ return temp_streamFile;
+
+fail:
+ close_streamfile(temp_streamFile);
+ return NULL;
+}
+
+#endif /* _XAVS_STREAMFILE_H_ */
diff --git a/src/streamfile.c b/src/streamfile.c
index 472726e9..6ce19f75 100644
--- a/src/streamfile.c
+++ b/src/streamfile.c
@@ -771,12 +771,23 @@ STREAMFILE * open_streamfile(STREAMFILE *streamFile, const char * pathname) {
}
STREAMFILE * open_streamfile_by_ext(STREAMFILE *streamFile, const char * ext) {
- char filename_ext[PATH_LIMIT];
+ char filename[PATH_LIMIT];
+ int filename_len, fileext_len;
- streamFile->get_name(streamFile,filename_ext,sizeof(filename_ext));
- strcpy(filename_ext + strlen(filename_ext) - strlen(filename_extension(filename_ext)), ext);
+ streamFile->get_name(streamFile,filename,sizeof(filename));
- return streamFile->open(streamFile,filename_ext,STREAMFILE_DEFAULT_BUFFER_SIZE);
+ filename_len = strlen(filename);
+ fileext_len = strlen(filename_extension(filename));
+
+ if (fileext_len == 0) {/* extensionless */
+ strcat(filename,".");
+ strcat(filename,ext);
+ }
+ else {
+ strcpy(filename + filename_len - fileext_len, ext);
+ }
+
+ return streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
}
STREAMFILE * open_streamfile_by_filename(STREAMFILE *streamFile, const char * name) {
diff --git a/src/vgmstream.c b/src/vgmstream.c
index fe1307a0..87454406 100644
--- a/src/vgmstream.c
+++ b/src/vgmstream.c
@@ -98,11 +98,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_xwb,
init_vgmstream_ps2_xa30,
init_vgmstream_musc,
- init_vgmstream_musx_v004,
- init_vgmstream_musx_v005,
- init_vgmstream_musx_v006,
- init_vgmstream_musx_v010,
- init_vgmstream_musx_v201,
+ init_vgmstream_musx,
init_vgmstream_leg,
init_vgmstream_filp,
init_vgmstream_ikm_ps2,
@@ -461,7 +457,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_xps,
init_vgmstream_zsnd,
init_vgmstream_opus_opusx,
- init_vgmstream_dsp_adpcmx,
+ init_vgmstream_dsp_adpy,
+ init_vgmstream_dsp_adpx,
init_vgmstream_ogg_opus,
init_vgmstream_nus3audio,
init_vgmstream_imc,
@@ -486,6 +483,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_acb,
init_vgmstream_rad,
init_vgmstream_smk,
+ init_vgmstream_mzrt,
+ init_vgmstream_xavs,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
diff --git a/src/vgmstream.h b/src/vgmstream.h
index 9b1c9177..e2ced4bc 100644
--- a/src/vgmstream.h
+++ b/src/vgmstream.h
@@ -371,11 +371,7 @@ typedef enum {
meta_XWB, /* Microsoft XACT framework (Xbox, X360, Windows) */
meta_PS2_XA30, /* Driver - Parallel Lines (PS2) */
meta_MUSC, /* Krome PS2 games */
- meta_MUSX_V004, /* Spyro Games, possibly more */
- meta_MUSX_V005, /* Spyro Games, possibly more */
- meta_MUSX_V006, /* Spyro Games, possibly more */
- meta_MUSX_V010, /* Spyro Games, possibly more */
- meta_MUSX_V201, /* Sphinx and the cursed Mummy */
+ meta_MUSX,
meta_LEG, /* Legaia 2 [no header_id] */
meta_FILP, /* Resident Evil - Dead Aim */
meta_IKM,
@@ -718,7 +714,8 @@ typedef enum {
meta_MSF_TAMASOFT,
meta_XPS_DAT,
meta_ZSND,
- meta_DSP_ADPCMX,
+ meta_DSP_ADPY,
+ meta_DSP_ADPX,
meta_OGG_OPUS,
meta_IMC,
meta_GIN,
@@ -733,6 +730,8 @@ typedef enum {
meta_BWAV,
meta_RAD,
meta_SMACKER,
+ meta_MZRT,
+ meta_XAVS,
} meta_t;