Merge pull request #270 from bnnm/xmd-xsew-xwb-ogg

XMD, XSEW, XWB, OGG
This commit is contained in:
Christopher Snowhill 2018-07-27 13:26:24 -07:00 committed by GitHub
commit 094d95e4de
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 206 additions and 29 deletions

View File

@ -1,9 +1,8 @@
#include "coding.h" #include "coding.h"
/* Decodes Argonaut's ASF ADPCM codec. Algorithm follows Croc2_asf2raw.exe, and the waveform /* Decodes Argonaut's ASF ADPCM codec, used in some of their PC games.
* looks almost correct, but should reverse engineer asfcodec.adl (DLL) for accuracy. */ * Algorithm should be accurate (reverse engineered from asfcodec.adl DLL). */
#define carry(a, b) (((uint32_t)(a) > (uint32_t)((a) + (b))))
void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
off_t frame_offset; off_t frame_offset;
int i, frames_in, sample_count = 0; int i, frames_in, sample_count = 0;
@ -31,25 +30,19 @@ void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
new_sample = i&1 ? /* high nibble first */ new_sample = i&1 ? /* high nibble first */
get_low_nibble_signed(nibbles): get_low_nibble_signed(nibbles):
get_high_nibble_signed(nibbles); get_high_nibble_signed(nibbles);
new_sample = new_sample << (shift + 6); /* move sample to upper nibble, then shift + 2 (IOW: shift + 6) */
new_sample = (new_sample << 4) << (shift + 2);
switch(mode) { /* mode is checked as a flag, so there are 2 modes only, but lower nibble
case 0x00: * may have other values at last frame (ex 0x02/09), could be control flags (loop related?) */
new_sample = (new_sample + (hist1 << 6)) >> 6; if (mode & 0x4) { /* ~filters: 2, -1 */
break; new_sample = (new_sample + (hist1 << 7) - (hist2 << 6)) >> 6;
}
case 0x04: else { /* ~filters: 1, 0 */
new_sample = (new_sample + (hist1 << 7) - (hist2 << 6)) >> 6; new_sample = (new_sample + (hist1 << 6)) >> 6;
break;
default: /* other modes (ex 0x02/09) seem only at last frame as 0 */
//VGM_LOG("ASF: unknown mode %x at %lx\n", mode,frame_offset);
//new_sample = 0; /* maybe? */
break;
} }
//new_sample = clamp16(new_sample); /* must not */ //new_sample = clamp16(new_sample); /* must not */
outbuf[sample_count] = (int16_t)new_sample; outbuf[sample_count] = (int16_t)new_sample;
sample_count += channelspacing; sample_count += channelspacing;

View File

@ -151,6 +151,9 @@ void decode_fadpcm(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacin
/* asf_decoder */ /* asf_decoder */
void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_asf(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* xmd_decoder */
void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size);
/* ea_mt_decoder*/ /* ea_mt_decoder*/
ea_mt_codec_data *init_ea_mt(int channel_count, int type); ea_mt_codec_data *init_ea_mt(int channel_count, int type);
void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);

63
src/coding/xmd_decoder.c Normal file
View File

@ -0,0 +1,63 @@
#include "coding.h"
/* Decodes Konami XMD from Xbox games (algorithm info from xmd2wav/xmddecode.dll). */
void decode_xmd(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, size_t frame_size) {
off_t frame_offset;
int i, frames_in, sample_count = 0, samples_done = 0;
size_t bytes_per_frame, samples_per_frame;
int16_t hist1, hist2;
uint16_t scale;
/* external interleave (variable size), mono */
bytes_per_frame = frame_size;
samples_per_frame = 2 + (frame_size - 0x06) * 2;
frames_in = first_sample / samples_per_frame;
first_sample = first_sample % samples_per_frame;
/* parse frame header */
frame_offset = stream->offset + bytes_per_frame*frames_in;
hist2 = read_16bitLE(frame_offset+0x00,stream->streamfile);
hist1 = read_16bitLE(frame_offset+0x02,stream->streamfile);
scale = (uint16_t)read_16bitLE(frame_offset+0x04,stream->streamfile); /* scale doesn't go too high though */
/* write header samples (needed) */
if (sample_count >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = hist2;
samples_done++;
}
sample_count++;
if (sample_count >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = hist1;
samples_done++;
}
sample_count++;
/* decode nibbles */
for (i = first_sample; i < first_sample + samples_to_do; i++) {
int32_t new_sample;
uint8_t nibbles = (uint8_t)read_8bit(frame_offset+0x06 + i/2,stream->streamfile);
new_sample = i&1 ? /* low nibble first */
get_high_nibble_signed(nibbles):
get_low_nibble_signed(nibbles);
new_sample = (new_sample*(scale<<14) + (hist1*0x7298) - (hist2*0x3350)) >> 14;
/* Coefs are similar to XA's filter 2, but using those creates hissing in some songs.
* ex. 1.796875 * (1 << 14) = 0x7300, -0.8125 * (1 << 14) = -0x3400 */
//new_sample = (int32_t)(new_sample*scale + hist1*1.796875 + hist2*-0.8125);
//new_sample = clamp16(new_sample); /* not needed */
if (sample_count >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = (int16_t)new_sample;
samples_done++;
}
sample_count++;
hist2 = hist1;
hist1 = new_sample;
}
//stream->adpcm_history1_32 = hist1;
//stream->adpcm_history2_32 = hist2;
}

View File

@ -432,11 +432,13 @@ static const char* extension_list[] = {
"xmu", "xmu",
"xnb", "xnb",
"xsf", "xsf",
"xsew",
"xss", "xss",
"xvag", "xvag",
"xvas", "xvas",
"xwav",//fake, to be removed "xwav",//fake, to be removed
"xwb", "xwb",
"xmd",
"xwc", "xwc",
"xwm", //FFmpeg, not parsed (XWMA) "xwm", //FFmpeg, not parsed (XWMA)
"xwma", //FFmpeg, not parsed (XWMA) "xwma", //FFmpeg, not parsed (XWMA)
@ -555,6 +557,7 @@ static const coding_info coding_info_list[] = {
{coding_MC3, "Paradigm MC3 3-bit ADPCM"}, {coding_MC3, "Paradigm MC3 3-bit ADPCM"},
{coding_FADPCM, "FMOD FADPCM 4-bit ADPCM"}, {coding_FADPCM, "FMOD FADPCM 4-bit ADPCM"},
{coding_ASF, "Argonaut ASF 4-bit ADPCM"}, {coding_ASF, "Argonaut ASF 4-bit ADPCM"},
{coding_XMD, "Konami XMD 4-bit ADPCM"},
{coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"}, {coding_SDX2, "Squareroot-delta-exact (SDX2) 8-bit DPCM"},
{coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"}, {coding_SDX2_int, "Squareroot-delta-exact (SDX2) 8-bit DPCM with 1 byte interleave"},
@ -1041,6 +1044,7 @@ static const meta_info meta_info_list[] = {
{meta_H4M, "Hudson HVQM4 header"}, {meta_H4M, "Hudson HVQM4 header"},
{meta_OGG_MUS, "Ogg Vorbis (MUS header)"}, {meta_OGG_MUS, "Ogg Vorbis (MUS header)"},
{meta_ASF, "Argonaut ASF header"}, {meta_ASF, "Argonaut ASF header"},
{meta_XMD, "Konami XMD header"},
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
{meta_FFmpeg, "FFmpeg supported file format"}, {meta_FFmpeg, "FFmpeg supported file format"},

View File

@ -1473,6 +1473,10 @@
<File <File
RelativePath=".\meta\xvag.c" RelativePath=".\meta\xvag.c"
> >
</File>
<File
RelativePath=".\meta\xmd.c"
>
</File> </File>
<File <File
RelativePath=".\meta\xwb.c" RelativePath=".\meta\xwb.c"
@ -1714,6 +1718,10 @@
RelativePath=".\coding\xa_decoder.c" RelativePath=".\coding\xa_decoder.c"
> >
</File> </File>
<File
RelativePath=".\coding\xmd_decoder.c"
>
</File>
<File <File
RelativePath=".\coding\vorbis_custom_decoder.c" RelativePath=".\coding\vorbis_custom_decoder.c"
> >

View File

@ -442,6 +442,7 @@
<ClCompile Include="meta\xnb.c" /> <ClCompile Include="meta\xnb.c" />
<ClCompile Include="meta\xss.c" /> <ClCompile Include="meta\xss.c" />
<ClCompile Include="meta\xvag.c" /> <ClCompile Include="meta\xvag.c" />
<ClCompile Include="meta\xmd.c" />
<ClCompile Include="meta\xwb.c" /> <ClCompile Include="meta\xwb.c" />
<ClCompile Include="meta\xwc.c" /> <ClCompile Include="meta\xwc.c" />
<ClCompile Include="meta\ydsp.c" /> <ClCompile Include="meta\ydsp.c" />
@ -487,6 +488,7 @@
<ClCompile Include="coding\vorbis_custom_utils_wwise.c" /> <ClCompile Include="coding\vorbis_custom_utils_wwise.c" />
<ClCompile Include="coding\ws_decoder.c" /> <ClCompile Include="coding\ws_decoder.c" />
<ClCompile Include="coding\xa_decoder.c" /> <ClCompile Include="coding\xa_decoder.c" />
<ClCompile Include="coding\xmd_decoder.c" />
<ClCompile Include="layout\segmented.c" /> <ClCompile Include="layout\segmented.c" />
<ClCompile Include="layout\aix_layout.c" /> <ClCompile Include="layout\aix_layout.c" />
<ClCompile Include="layout\blocked_ast.c" /> <ClCompile Include="layout\blocked_ast.c" />

View File

@ -895,6 +895,9 @@
<ClCompile Include="meta\xvag.c"> <ClCompile Include="meta\xvag.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="meta\xmd.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\xwb.c"> <ClCompile Include="meta\xwb.c">
<Filter>meta\Source Files</Filter> <Filter>meta\Source Files</Filter>
</ClCompile> </ClCompile>
@ -1027,6 +1030,9 @@
<ClCompile Include="coding\xa_decoder.c"> <ClCompile Include="coding\xa_decoder.c">
<Filter>coding\Source Files</Filter> <Filter>coding\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="coding\xmd_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="layout\segmented.c"> <ClCompile Include="layout\segmented.c">
<Filter>layout\Source Files</Filter> <Filter>layout\Source Files</Filter>
</ClCompile> </ClCompile>

View File

@ -213,7 +213,7 @@ VGMSTREAM * init_vgmstream_ea_hdr_sth_dat(STREAMFILE *streamFile) {
if (i == target_stream - 1) if (i == target_stream - 1)
break; break;
while (true) { while (1) {
if (sns_offset >= file_size) if (sns_offset >= file_size)
goto fail; goto fail;
@ -254,13 +254,12 @@ fail:
/* EA ABK - ABK header seems to be same as in the old games but the sound table is different and it contains SNR/SNS sounds instead */ /* EA ABK - ABK header seems to be same as in the old games but the sound table is different and it contains SNR/SNS sounds instead */
VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) {
int is_dupe, total_sounds = 0, target_stream = streamFile->stream_index; int is_dupe, total_sounds = 0, target_stream = streamFile->stream_index;
off_t bnk_offset, header_table_offset, base_offset, unk_struct_offset, table_offset, snd_entry_offset, ast_offset; off_t bnk_offset, header_table_offset, base_offset, unk_struct_offset, table_offset, snd_entry_offset, ast_offset = 0;
off_t num_entries_off, base_offset_off, entries_off, sound_table_offset_off; off_t num_entries_off, base_offset_off, entries_off, sound_table_offset_off;
uint32_t i, j, k, version, num_sounds, total_sound_tables; uint32_t i, j, k, version, num_sounds, total_sound_tables;
uint16_t num_tables, bnk_index, bnk_target_index; uint16_t num_tables, bnk_index, bnk_target_index;
uint8_t num_entries, extra_entries; uint8_t num_entries, extra_entries;
off_t sound_table_offsets[0x2000]; off_t sound_table_offsets[0x2000];
STREAMFILE *astData = NULL;
VGMSTREAM *vgmstream; VGMSTREAM *vgmstream;
int32_t (*read_32bit)(off_t,STREAMFILE*); int32_t (*read_32bit)(off_t,STREAMFILE*);
int16_t (*read_16bit)(off_t,STREAMFILE*); int16_t (*read_16bit)(off_t,STREAMFILE*);
@ -365,7 +364,7 @@ VGMSTREAM * init_vgmstream_ea_abk_new(STREAMFILE *streamFile) {
header_table_offset += entries_off + num_entries * 0x04 + extra_entries * 0x04; header_table_offset += entries_off + num_entries * 0x04 + extra_entries * 0x04;
} }
if (bnk_target_index == 0xFFFF) if (bnk_target_index == 0xFFFF || ast_offset == 0)
goto fail; goto fail;
vgmstream = parse_s10a_header(streamFile, bnk_offset, bnk_target_index, ast_offset); vgmstream = parse_s10a_header(streamFile, bnk_offset, bnk_target_index, ast_offset);

View File

@ -765,4 +765,6 @@ VGMSTREAM * init_vgmstream_h4m(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_asf(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_asf(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_xmd(STREAMFILE *streamFile);
#endif /*_META_H*/ #endif /*_META_H*/

View File

@ -265,8 +265,8 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
/* check extension */ /* check extension */
/* .ogg: standard/various, .logg: renamed for plugins, /* .ogg: standard/various, .logg: renamed for plugins
* .adx: KID [Remember11 (PC)], * .adx: KID [Remember11 (PC)]
* .rof: The Rhythm of Fighters (Mobile) * .rof: The Rhythm of Fighters (Mobile)
* .acm: Planescape Torment Enhanced Edition (PC) */ * .acm: Planescape Torment Enhanced Edition (PC) */
if (check_extensions(streamFile,"ogg,logg,adx,rof,acm")) { if (check_extensions(streamFile,"ogg,logg,adx,rof,acm")) {
@ -285,7 +285,7 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
is_eno = 1; is_eno = 1;
} else if (check_extensions(streamFile,"gwm")) { /* .gwm: Adagio: Cloudburst (PC) */ } else if (check_extensions(streamFile,"gwm")) { /* .gwm: Adagio: Cloudburst (PC) */
is_gwm = 1; is_gwm = 1;
} else if (check_extensions(streamFile,"mus")) { /* .mus: Redux - Dark Matters (PC) */ } else if (check_extensions(streamFile,"mus")) { /* .mus: Redux - Dark Matters (PC) */
is_mus = 1; is_mus = 1;
} else { } else {
goto fail; goto fail;
@ -545,12 +545,25 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, ov_callb
loop_length = atol(strrchr(user_comment, '=') + 1) - loop_start; loop_length = atol(strrchr(user_comment, '=') + 1) - loop_start;
loop_length_found = 1; loop_length_found = 1;
} }
} }
else if (strstr(user_comment, "omment=") == user_comment) { /* Air (Android) */ else if (strstr(user_comment, "omment=") == user_comment) { /* Air (Android) */
sscanf(strstr(user_comment, "=LOOPSTART=") + 11, "%d,LOOPEND=%d", &loop_start, &loop_end); sscanf(strstr(user_comment, "=LOOPSTART=") + 11, "%d,LOOPEND=%d", &loop_start, &loop_end);
loop_flag = 1; loop_flag = 1;
loop_end_found = 1; loop_end_found = 1;
} }
else if (strstr(user_comment,"MarkerNum=0002")==user_comment) { /* Megaman X Legacy Collection: MMX1/2/3 (PC) flag */
/* uses LoopStart=-1 LoopEnd=-1, then 3 secuential comments: "MarkerNum" + "M=7F(start)" + "M=7F(end)" */
loop_flag = 1;
}
else if (strstr(user_comment,"M=7F")==user_comment) { /* Megaman X Legacy Collection: MMX1/2/3 (PC) start/end */
if (loop_flag && loop_start < 0) { /* LoopStart should set as -1 before */
sscanf(user_comment,"M=7F%x", &loop_start);
}
else if (loop_flag && loop_start >= 0) {
sscanf(user_comment,"M=7F%x", &loop_end);
loop_end_found = 1;
}
}
//;VGM_LOG("OGG: user_comment=%s\n", user_comment); //;VGM_LOG("OGG: user_comment=%s\n", user_comment);
} }

View File

@ -258,8 +258,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
/* .da: The Great Battle VI (PS), .cd: Exector (PS), .med: Psi Ops (PC), .snd: Layton Brothers (iOS/Android), /* .da: The Great Battle VI (PS), .cd: Exector (PS), .med: Psi Ops (PC), .snd: Layton Brothers (iOS/Android),
* .adx: Remember11 (PC) sfx * .adx: Remember11 (PC) sfx
* .adp: Headhunter (DC) * .adp: Headhunter (DC)
* .xss: Spider-Man The Movie (Xbox) */ * .xss: Spider-Man The Movie (Xbox)
if ( check_extensions(streamFile, "wav,lwav,xwav,da,cd,med,snd,adx,adp,xss") ) { * .xsew: Mega Man X Legacy Collections (PC) */
if ( check_extensions(streamFile, "wav,lwav,xwav,da,cd,med,snd,adx,adp,xss,xsew") ) {
; ;
} }
else if ( check_extensions(streamFile, "mwv") ) { else if ( check_extensions(streamFile, "mwv") ) {

69
src/meta/xmd.c Normal file
View File

@ -0,0 +1,69 @@
#include "meta.h"
#include "../coding/coding.h"
/* XMD - from Konami Xbox games [Silent Hill 4 (Xbox), Castlevania Curse of Darkness (Xbox)] */
VGMSTREAM * init_vgmstream_xmd(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, sample_rate;
size_t data_size, loop_start, frame_size;
/* checks (.xmd comes from bigfiles with filenames) */
if (!check_extensions(streamFile, "xmd"))
goto fail;
if ((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) == 0x786D6400) { /* "xmd\0" */
/* v2 from Castlevania: Curse of Darkness */
channel_count = read_8bit(0x03,streamFile);
sample_rate = (uint16_t)read_16bitLE(0x04, streamFile);
data_size = read_32bitLE(0x06, streamFile);
loop_flag = read_8bit(0x0a,streamFile);
loop_start = read_32bitLE(0x0b, streamFile);
/* 0x0f(2): unknown+config? */
frame_size = 0x15;
start_offset = 0x11;
}
else {
/* v1 from Silent Hill 4 */
channel_count = read_8bit(0x00,streamFile);
sample_rate = (uint16_t)read_16bitLE(0x01, streamFile);
data_size = read_32bitLE(0x03, streamFile);
loop_flag = read_8bit(0x07,streamFile);
loop_start = read_32bitLE(0x08, streamFile);
frame_size = 0x0d;
start_offset = 0x0c;
}
/* extra checks just in case */
if (data_size > get_streamfile_size(streamFile))
goto fail; /* v1 .xmd are sector-aligned with padding */
if (channel_count > 2)
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = data_size / frame_size / channel_count * ((frame_size-0x06)*2 + 2); /* bytes to samples */
if (loop_flag) {
vgmstream->loop_start_sample = loop_start / frame_size / channel_count * ((frame_size-0x06)*2 + 2); /* bytes to samples */
vgmstream->loop_end_sample = vgmstream->num_samples;
}
vgmstream->meta_type = meta_XMD;
vgmstream->coding_type = coding_XMD;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = frame_size;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -111,8 +111,8 @@ VGMSTREAM * init_vgmstream_xwb(STREAMFILE *streamFile) {
xwb.base_offset = 0; xwb.base_offset = 0;
xwb.base_size = 0; xwb.base_size = 0;
xwb.entry_offset = 0x50; xwb.entry_offset = 0x50;
xwb.entry_size = xwb.entry_elem_size * xwb.total_subsongs;
xwb.entry_elem_size = 0x14; xwb.entry_elem_size = 0x14;
xwb.entry_size = xwb.entry_elem_size * xwb.total_subsongs;
xwb.data_offset = xwb.entry_offset + xwb.entry_size; xwb.data_offset = xwb.entry_offset + xwb.entry_size;
xwb.data_size = get_streamfile_size(streamFile) - xwb.data_offset; xwb.data_size = get_streamfile_size(streamFile) - xwb.data_offset;

View File

@ -419,6 +419,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_h4m, init_vgmstream_h4m,
init_vgmstream_ps2_ads_container, init_vgmstream_ps2_ads_container,
init_vgmstream_asf, init_vgmstream_asf,
init_vgmstream_xmd,
init_vgmstream_txth, /* should go at the end (lower priority) */ init_vgmstream_txth, /* should go at the end (lower priority) */
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
@ -1147,6 +1148,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
return 256; /* (0x8c - 0xc) * 2 */ return 256; /* (0x8c - 0xc) * 2 */
case coding_ASF: case coding_ASF:
return 32; /* (0x11 - 0x1) * 2 */ return 32; /* (0x11 - 0x1) * 2 */
case coding_XMD:
return (vgmstream->interleave_block_size - 0x06)*2 + 2;
case coding_EA_MT: case coding_EA_MT:
return 432; return 432;
case coding_CRI_HCA: case coding_CRI_HCA:
@ -1305,6 +1308,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
return 0x8c; return 0x8c;
case coding_ASF: case coding_ASF:
return 0x11; return 0x11;
case coding_XMD:
return vgmstream->interleave_block_size;
case coding_EA_MT: case coding_EA_MT:
return 0; /* variable (frames of bit counts or PCM frames) */ return 0; /* variable (frames of bit counts or PCM frames) */
#ifdef VGM_USE_ATRAC9 #ifdef VGM_USE_ATRAC9
@ -1943,6 +1948,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
samples_to_do); samples_to_do);
} }
break; break;
case coding_XMD:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_xmd(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
vgmstream->channels,vgmstream->samples_into_block,
samples_to_do, vgmstream->interleave_block_size);
}
break;
case coding_EA_MT: case coding_EA_MT:
for (chan=0;chan<vgmstream->channels;chan++) { for (chan=0;chan<vgmstream->channels;chan++) {
decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+chan, decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+chan,

View File

@ -156,6 +156,7 @@ typedef enum {
coding_MC3, /* Paradigm MC3 3-bit ADPCM */ coding_MC3, /* Paradigm MC3 3-bit ADPCM */
coding_FADPCM, /* FMOD FADPCM 4-bit ADPCM */ coding_FADPCM, /* FMOD FADPCM 4-bit ADPCM */
coding_ASF, /* Argonaut ASF 4-bit ADPCM */ coding_ASF, /* Argonaut ASF 4-bit ADPCM */
coding_XMD, /* Konami XMD 4-bit ADPCM */
/* others */ /* others */
coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */ coding_SDX2, /* SDX2 2:1 Squareroot-Delta-Exact compression DPCM */
@ -683,6 +684,7 @@ typedef enum {
meta_H4M, /* Hudson HVQM4 video [Resident Evil 0 (GC), Tales of Symphonia (GC)] */ meta_H4M, /* Hudson HVQM4 video [Resident Evil 0 (GC), Tales of Symphonia (GC)] */
meta_OGG_MUS, /* Ogg Vorbis with encryption [Redux - Dark Matters (PC)] */ meta_OGG_MUS, /* Ogg Vorbis with encryption [Redux - Dark Matters (PC)] */
meta_ASF, /* Argonaut ASF [Croc 2 (PC)] */ meta_ASF, /* Argonaut ASF [Croc 2 (PC)] */
meta_XMD, /* Konami XMD [Silent Hill 4 (Xbox), Castlevania: Curse of Darkness (Xbox)] */
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
meta_FFmpeg, meta_FFmpeg,