mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-14 10:37:38 +01:00
commit
e114566be3
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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)"},
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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++) {
|
||||
|
@ -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) */
|
||||
|
Loading…
Reference in New Issue
Block a user