Merge pull request #660 from bnnm/cdima-wmid

cdima wmid
This commit is contained in:
bnnm 2020-07-05 22:02:27 +02:00 committed by GitHub
commit e114566be3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 170 additions and 31 deletions

View File

@ -37,6 +37,7 @@ void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t
void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int codec_config);
void decode_h4m_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, uint16_t frame_format);
void decode_cd_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
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);

View File

@ -285,6 +285,60 @@ static void mtf_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset,
if (*step_index > 88) *step_index=88;
}
/* IMA table pre-modified like this:
for i=0..89
adpcm = clamp(adpcm[i], 0x1fff) * 4;
*/
static const int16_t mul_adpcm_table[89] = {
28, 32, 36, 40, 44, 48, 52, 56,
64, 68, 76, 84, 92, 100, 112, 124,
136, 148, 164, 180, 200, 220, 240, 264,
292, 320, 352, 388, 428, 472, 520, 572,
628, 692, 760, 836, 920, 1012, 1116, 1228,
1348, 1484, 1632, 1796, 1976, 2176, 2392, 2632,
2896, 3184, 3504, 3852, 4240, 4664, 5128, 5644,
6208, 6828, 7512, 8264, 9088, 9996, 10996, 12096,
13308, 14640, 16104, 17712, 19484, 21432, 23576, 25936,
28528, 31380, 32764, 32764, 32764, 32764, 32764, 32764,
32764, 32764, 32764, 32764, 32764, 32764, 32764, 32764,
32764
};
/* step table is the same */
/* ops per code, generated like this:
for i=0..15
v = 0x800
if (i & 1) v = 0x1800
if (i & 2) v += 0x2000
if (i & 4) v += 0x4000
if (i & 8) v = -v;
mul_op_table[i] = v;
*/
static const int16_t mul_delta_table[16] = {
0x0800, 0x1800, 0x2800, 0x3800, 0x4800, 0x5800, 0x6800, 0x7800,
-0x0800,-0x1800,-0x2800,-0x3800,-0x4800,-0x5800,-0x6800,-0x7800
};
/* Crystal Dynamics IMA, reverse engineered from the exe, also info: https://github.com/sephiroth99/MulDeMu */
static void cd_ima_expand_nibble(VGMSTREAMCHANNEL* stream, off_t byte_offset, int shift, int32_t* hist1, int32_t* index) {
int code, sample, step, delta;
/* could do the above table calcs during decode too */
code = (read_8bit(byte_offset,stream->streamfile) >> shift) & 0xf;
sample = *hist1;
step = mul_adpcm_table[*index];
delta = (int16_t)((step * mul_delta_table[code]) >> 16);
sample += delta;
*hist1 = clamp16(sample);
*index += IMA_IndexTable[code];
if (*index < 0) *index=0;
if (*index > 88) *index=88;
}
/* ************************************ */
/* DVI/IMA */
/* ************************************ */
@ -1156,6 +1210,57 @@ void decode_h4m_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
stream->adpcm_step_index = step_index;
}
/* Crystal Dynamics IMA. Original code uses mind-bending intrinsics, so this may not be fully accurate.
* Has another table with delta_table MMX combos, and using header sample and clamps seems necessary. */
void decode_cd_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, frames_in, sample_pos = 0, block_samples, frame_size;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
off_t frame_offset;
/* external interleave (fixed size), mono */
block_samples = (0x24 - 0x4) * 2;
frames_in = first_sample / block_samples;
first_sample = first_sample % block_samples;
frame_size = 0x24;
frame_offset = stream->offset + frame_size*frames_in;
/* normal header (hist+step+reserved), mono */
if (first_sample == 0) {
off_t header_offset = frame_offset + 0x00;
hist1 = read_16bitLE(header_offset+0x00,stream->streamfile);
step_index = read_8bit(header_offset+0x02,stream->streamfile);
if (step_index < 0) step_index=0;
if (step_index > 88) step_index=88;
/* write header sample (even samples per block, skips last nibble) */
outbuf[sample_pos] = (short)(hist1);
sample_pos += channelspacing;
first_sample += 1;
samples_to_do -= 1;
}
/* decode nibbles (layout: straight in mono ) */
for (i = first_sample; i < first_sample + samples_to_do; i++) {
off_t byte_offset = frame_offset + 0x04 + (i-1)/2;
int nibble_shift = (!((i-1)&1) ? 0:4); /* low first */
/* must skip last nibble per spec, rarely needed though (ex. Gauntlet Dark Legacy) */
if (i < block_samples) {
cd_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
outbuf[sample_pos] = (short)(hist1);
sample_pos += channelspacing;
}
}
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
/* ************************************************************* */
size_t ima_bytes_to_samples(size_t bytes, int channels) {

View File

@ -66,6 +66,8 @@ void decode_ptadpcm(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspaci
index = frame[0x04];
VGM_ASSERT_ONCE(index > 12, "PTADPCM: incorrect index at %x\n", (uint32_t)frame_offset);
if (index > 12)
index = 12;
/* write header samples (needed) */
if (sample_count >= first_sample && samples_done < samples_to_do) {

View File

@ -737,6 +737,7 @@ static const coding_info coding_info_list[] = {
{coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"},
{coding_UBI_IMA, "Ubisoft 4-bit IMA ADPCM"},
{coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"},
{coding_CD_IMA, "Crystal Dynamics 4-bit IMA ADPCM"},
{coding_MSADPCM, "Microsoft 4-bit ADPCM"},
{coding_MSADPCM_int, "Microsoft 4-bit ADPCM (mono/interleave)"},

View File

@ -8,9 +8,10 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
STREAMFILE* temp_sf = NULL;
off_t subfile_offset, base_offset = 0;
size_t subfile_size;
uint32_t subfile_id, header_id;
int big_endian, version, is_dummy = 0;
uint32_t subfile_id;
int big_endian, version, is_riff = 0, is_dummy = 0, is_wmid = 0;
uint32_t (*read_u32)(off_t,STREAMFILE*);
float (*read_f32)(off_t,STREAMFILE*);
int total_subsongs, target_subsong = sf->stream_index;
@ -24,6 +25,7 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
goto fail;
big_endian = guess_endianness32bit(base_offset + 0x04, sf);
read_u32 = big_endian ? read_u32be : read_u32le;
read_f32 = big_endian ? read_f32be : read_f32le;
/* Wwise banks have event/track/sequence/etc info in the HIRC chunk, as well
@ -90,27 +92,42 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
subfile_offset = read_u32(offset + 0x04, sf) + data_offset;
subfile_size = read_u32(offset + 0x08, sf);
}
//;VGM_LOG("BKHD: %lx, %x\n", subfile_offset, subfile_size);
/* some indexes don't have that, but for now leave a dummy song for easier HIRC mapping */
/* detect format */
if (subfile_offset <= 0 || subfile_size <= 0) {
//;VGM_LOG("BKHD: dummy entry");
temp_sf = setup_subfile_streamfile(sf, 0x00, 0x10, "raw");
/* some indexes don't have data */
is_dummy = 1;
}
else if (read_u32be(subfile_offset + 0x00, sf) == 0x52494646 || /* "RIFF" */
read_u32be(subfile_offset + 0x00, sf) == 0x52494658) { /* "RIFX" */
is_riff = 1;
}
else if (read_f32(subfile_offset + 0x02, sf) >= 30.0 &&
read_f32(subfile_offset + 0x02, sf) <= 250.0) {
/* ignore Wwise's custom .wmid (similar to a regular midi but with simplified
* chunks and custom fields: 0x00=MThd's division, 0x02: bpm (new), etc) */
is_wmid = 1;
}
/* default is sfx */
if (is_dummy || is_wmid) {
/* for now leave a dummy song for easier .bnk index-to-subsong mapping */
temp_sf = setup_subfile_streamfile(sf, 0x00, 1000 * 0x02 * 2, "raw");
if (!temp_sf) goto fail;
//todo make some better silent entry
vgmstream = init_vgmstream_raw_pcm(temp_sf);
if (!vgmstream) goto fail;
is_dummy = 1;
}
else {
//;VGM_LOG("BKHD: %lx, %x\n", subfile_offset, subfile_size);
/* could pass .wem but few files need memory .wem detection */
temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, NULL);
if (!temp_sf) goto fail;
header_id = read_u32be(0x00, temp_sf);
if (header_id == 0x52494646 || header_id == 0x52494658) { /* "RIFF" / "RIFX" */
if (is_riff) {
vgmstream = init_vgmstream_wwise(temp_sf);
if (!vgmstream) goto fail;
}
@ -124,7 +141,9 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
vgmstream->num_streams = total_subsongs;
if (is_dummy)
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%s", "dummy");
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u/dummy", subfile_id);
else if (is_wmid)
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u/wmid", subfile_id);
else if (subfile_id != 0xFFFFFFFF)
snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "%u", subfile_id);

View File

@ -3,7 +3,7 @@
#include "../coding/coding.h"
#include "mul_streamfile.h"
typedef enum { PSX, DSP, XBOX, XMA1 } mul_codec;
typedef enum { PSX, DSP, IMA, XMA1 } mul_codec;
static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec* p_codec, off_t* p_extra_offset);
@ -86,8 +86,8 @@ VGMSTREAM * init_vgmstream_mul(STREAMFILE *sf) {
dsp_read_hist_be (vgmstream,sf,coefs_offset+0x24,0x2e);
break;
case XBOX:
vgmstream->coding_type = coding_XBOX_IMA_int;
case IMA:
vgmstream->coding_type = coding_CD_IMA;
vgmstream->layout_type = layout_blocked_mul;
break;
@ -195,7 +195,7 @@ static int guess_codec(STREAMFILE* sf, int big_endian, int channels, mul_codec*
break;
}
if (i == data_size / frame_size) {
*p_codec = XBOX;
*p_codec = IMA;
return 1;
}

View File

@ -3844,22 +3844,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
return 1;
}
/* TMNT (2007)(GC)-bank */
if (sb->version == 0x00190002 && sb->platform == UBI_GC) {
config_sb_entry(sb, 0x68, 0x6c);
config_sb_audio_fs(sb, 0x28, 0x2c, 0x30); /* assumed groud_id */
config_sb_audio_he(sb, 0x3c, 0x40, 0x48, 0x50, 0x58, 0x5c);
config_sb_sequence(sb, 0x2c, 0x14);
config_sb_layer_he(sb, 0x20, 0x34, 0x38, 0x40);
config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10);
config_sb_silence_f(sb, 0x1c);
return 1;
}
/* TMNT (2007)(PS2)-bank */
if (sb->version == 0x00190002 && sb->platform == UBI_PS2) {
config_sb_entry(sb, 0x48, 0x5c);
@ -3876,6 +3860,24 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
return 1;
}
/* TMNT (2007)(GC)-bank */
/* Surf's Up (2007)(GC)-bank 0x00190005 */
if ((sb->version == 0x00190002 && sb->platform == UBI_GC) ||
(sb->version == 0x00190005 && sb->platform == UBI_GC)) {
config_sb_entry(sb, 0x68, 0x6c);
config_sb_audio_fs(sb, 0x28, 0x2c, 0x30); /* assumed groud_id */
config_sb_audio_he(sb, 0x3c, 0x40, 0x48, 0x50, 0x58, 0x5c);
config_sb_sequence(sb, 0x2c, 0x14);
config_sb_layer_he(sb, 0x20, 0x34, 0x38, 0x40);
config_sb_layer_sh(sb, 0x30, 0x00, 0x04, 0x08, 0x10);
config_sb_silence_f(sb, 0x1c);
return 1;
}
/* TMNT (2007)(X360)-bank 0x00190002 */
/* My Word Coach (2007)(Wii)-bank 0x00190002 */
/* Prince of Persia: Rival Swords (2007)(Wii)-bank 0x00190003 */

View File

@ -1229,6 +1229,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_XBOX_IMA_int:
case coding_FSB_IMA:
case coding_WWISE_IMA:
case coding_CD_IMA:
return 64;
case coding_APPLE_IMA4:
return 64;
@ -1433,6 +1434,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
return 0x24; //vgmstream->channels==1 ? 0x24 : 0x48;
case coding_XBOX_IMA_int:
case coding_WWISE_IMA:
case coding_CD_IMA:
return 0x24;
case coding_XBOX_IMA_mch:
case coding_FSB_IMA:
@ -1993,6 +1995,12 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
frame_format);
}
break;
case coding_CD_IMA:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_cd_ima(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch);
}
break;
case coding_WS:
for (ch = 0; ch < vgmstream->channels; ch++) {

View File

@ -141,6 +141,7 @@ typedef enum {
coding_UBI_IMA, /* Ubisoft IMA ADPCM */
coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */
coding_MTF_IMA, /* Capcom MT Framework IMA ADPCM */
coding_CD_IMA, /* Crystal Dynamics IMA ADPCM */
coding_MSADPCM, /* Microsoft ADPCM (stereo/mono) */
coding_MSADPCM_int, /* Microsoft ADPCM (mono) */