mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-29 19:37:30 +01:00
Fix UE4 MSADPCM .adpcm [Heroes of Ark (iOS), Angels in the Sky (iOS)]
This commit is contained in:
parent
da9f9a5572
commit
8f1ec86bda
@ -620,6 +620,7 @@ static const coding_info coding_info_list[] = {
|
||||
{coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"},
|
||||
|
||||
{coding_MSADPCM, "Microsoft 4-bit ADPCM"},
|
||||
{coding_MSADPCM_int, "Microsoft 4-bit ADPCM (mono/interleave)"},
|
||||
{coding_MSADPCM_ck, "Microsoft 4-bit ADPCM (Cricket Audio)"},
|
||||
{coding_WS, "Westwood Studios VBR ADPCM"},
|
||||
{coding_AICA, "Yamaha AICA 4-bit ADPCM"},
|
||||
|
@ -10,6 +10,7 @@
|
||||
static VGMSTREAM *parse_riff_ogg(STREAMFILE * streamFile, off_t start_offset, size_t data_size);
|
||||
#endif
|
||||
|
||||
|
||||
/* return milliseconds */
|
||||
static long parse_adtl_marker(unsigned char * marker) {
|
||||
long hh,mm,ss,ms;
|
||||
@ -236,6 +237,9 @@ fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt_chunk* fmt, int fact_sample_count, off_t start_offset);
|
||||
|
||||
|
||||
VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
riff_fmt_chunk fmt = {0};
|
||||
@ -682,17 +686,74 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
|
||||
vgmstream->meta_type = meta_RIFF_WAVE_MWV;
|
||||
}
|
||||
|
||||
|
||||
if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) )
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
/* UE4 uses half-interleave mono MSADPCM, try to autodetect without breaking normal MSADPCM */
|
||||
if (fmt.coding_type == coding_MSADPCM && is_ue4_msadpcm(vgmstream, streamFile, &fmt, fact_sample_count, start_offset)) {
|
||||
int ch;
|
||||
size_t half_interleave = data_size / vgmstream->channels;
|
||||
|
||||
vgmstream->coding_type = coding_MSADPCM_int;
|
||||
|
||||
/* only works with half-interleave as frame_size and interleave are merged ATM*/
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
vgmstream->ch[ch].channel_start_offset =
|
||||
vgmstream->ch[ch].offset = start_offset + half_interleave*ch;
|
||||
}
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* UE4 MSADPCM is quite normal but has a few minor quirks we can use to detect it */
|
||||
static int is_ue4_msadpcm(VGMSTREAM* vgmstream, STREAMFILE* streamFile, riff_fmt_chunk* fmt, int fact_sample_count, off_t start_offset) {
|
||||
|
||||
/* stereo only */
|
||||
if (fmt->channel_count != 2)
|
||||
goto fail;
|
||||
|
||||
/* UE4 class is "ADPCM", assume it's the extension too */
|
||||
if (!check_extensions(streamFile, "adpcm"))
|
||||
goto fail;
|
||||
|
||||
/* UE4 encoder doesn't add "fact" */
|
||||
if (fact_sample_count != 0)
|
||||
goto fail;
|
||||
|
||||
/* fixed block size */
|
||||
if (fmt->block_size != 0x200)
|
||||
goto fail;
|
||||
|
||||
/* later UE4 versions use 0x36 (at 0x32 may be fact_samples?) */
|
||||
if (fmt->size != 0x32 && fmt->size != 0x36)
|
||||
goto fail;
|
||||
|
||||
/* size 0x32 in older UE4 matches standard MSADPCM, so add extra detection */
|
||||
if (fmt->size == 0x32) {
|
||||
off_t offset = start_offset;
|
||||
off_t max_offset = 5 * fmt->block_size; /* try N blocks */
|
||||
if (max_offset > get_streamfile_size(streamFile))
|
||||
max_offset = get_streamfile_size(streamFile);
|
||||
|
||||
/* their encoder doesn't calculate optimal coefs and uses fixed values every frame
|
||||
* (could do it for fmt size 0x36 too but maybe they'll fix it in the future) */
|
||||
while (offset <= max_offset) {
|
||||
if (read_8bit(offset+0x00, streamFile) != 0 || read_16bitLE(offset+0x01, streamFile) != 0x00E6)
|
||||
goto fail;
|
||||
offset += fmt->block_size;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
VGMSTREAM * init_vgmstream_rifx(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
riff_fmt_chunk fmt = {0};
|
||||
|
@ -1199,6 +1199,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||
|
||||
case coding_MSADPCM:
|
||||
return (vgmstream->interleave_block_size - 0x07*vgmstream->channels)*2 / vgmstream->channels + 2;
|
||||
case coding_MSADPCM_int:
|
||||
case coding_MSADPCM_ck:
|
||||
return (vgmstream->interleave_block_size - 0x07)*2 + 2;
|
||||
case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */
|
||||
@ -1383,6 +1384,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
||||
return 0x4c*vgmstream->channels;
|
||||
|
||||
case coding_MSADPCM:
|
||||
case coding_MSADPCM_int:
|
||||
case coding_MSADPCM_ck:
|
||||
return vgmstream->interleave_block_size;
|
||||
case coding_WS:
|
||||
@ -1950,14 +1952,17 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
||||
buffer+samples_written*vgmstream->channels, samples_to_do);
|
||||
break;
|
||||
case coding_MSADPCM:
|
||||
if (vgmstream->channels == 2) {
|
||||
case coding_MSADPCM_int:
|
||||
if (vgmstream->channels == 1 || vgmstream->coding_type == coding_MSADPCM_int) {
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_msadpcm_mono(vgmstream,buffer+samples_written*vgmstream->channels+ch,
|
||||
vgmstream->channels,vgmstream->samples_into_block, samples_to_do, ch);
|
||||
}
|
||||
}
|
||||
else if (vgmstream->channels == 2) {
|
||||
decode_msadpcm_stereo(vgmstream,buffer+samples_written*vgmstream->channels,
|
||||
vgmstream->samples_into_block,samples_to_do);
|
||||
}
|
||||
else if (vgmstream->channels == 1) {
|
||||
decode_msadpcm_mono(vgmstream,buffer+samples_written*vgmstream->channels,
|
||||
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, 0);
|
||||
}
|
||||
break;
|
||||
case coding_MSADPCM_ck:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
@ -2342,6 +2347,7 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
|
||||
if (vgmstream->layout_type == layout_none && vgmstream->interleave_block_size > 0) {
|
||||
switch (vgmstream->coding_type) {
|
||||
case coding_MSADPCM:
|
||||
case coding_MSADPCM_int:
|
||||
case coding_MSADPCM_ck:
|
||||
case coding_MS_IMA:
|
||||
case coding_MC3:
|
||||
@ -2756,14 +2762,13 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
|
||||
if (!file) goto fail;
|
||||
}
|
||||
|
||||
for (ch=0; ch < vgmstream->channels; ch++) {
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
off_t offset;
|
||||
if (use_same_offset_per_channel) {
|
||||
offset = start_offset;
|
||||
} else if (is_stereo_codec) {
|
||||
int ch_mod = (ch & 1) ? ch - 1 : ch; /* adjust odd channels (ch 0,1,2,3,4,5 > ch 0,0,2,2,4,4) */
|
||||
offset = start_offset + vgmstream->interleave_block_size*ch_mod;
|
||||
//VGM_LOG("ch%i offset=%lx\n", ch,offset);
|
||||
} else {
|
||||
offset = start_offset + vgmstream->interleave_block_size*ch;
|
||||
}
|
||||
@ -2774,6 +2779,7 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
|
||||
if (!file) goto fail;
|
||||
}
|
||||
|
||||
VGM_LOG("ch%i offset=%lx\n", ch,offset);
|
||||
vgmstream->ch[ch].streamfile = file;
|
||||
vgmstream->ch[ch].channel_start_offset =
|
||||
vgmstream->ch[ch].offset = offset;
|
||||
|
@ -137,6 +137,7 @@ typedef enum {
|
||||
coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */
|
||||
|
||||
coding_MSADPCM, /* Microsoft ADPCM (stereo/mono) */
|
||||
coding_MSADPCM_int, /* Microsoft ADPCM (mono) */
|
||||
coding_MSADPCM_ck, /* Microsoft ADPCM (Cricket Audio variation) */
|
||||
coding_WS, /* Westwood Studios VBR ADPCM */
|
||||
coding_AICA, /* Yamaha AICA ADPCM (stereo) */
|
||||
|
Loading…
x
Reference in New Issue
Block a user