Merge pull request #776 from NicknineTheEagle/ubi

Ubi
This commit is contained in:
NicknineTheEagle 2020-12-06 23:52:20 +03:00 committed by GitHub
commit 7af4314261
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 64 additions and 35 deletions

View File

@ -37,7 +37,8 @@ void decode_apple_ima4(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelsp
void decode_fsb_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_fsb_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_wwise_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_wwise_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_awc_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); 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_ubi_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ubi_sce_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
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_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); 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 ima_bytes_to_samples(size_t bytes, int channels);

View File

@ -1065,9 +1065,8 @@ void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
/* DVI stereo/mono with some mini header and sample output */ /* DVI stereo/mono with some mini header and sample output */
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_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, sample_count = 0; int i, sample_count = 0;
int has_header = (codec_config & 0x80) == 0;
int32_t hist1 = stream->adpcm_history1_32; int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index; int step_index = stream->adpcm_step_index;
@ -1075,7 +1074,7 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
//internal interleave //internal interleave
//header in the beginning of the stream //header in the beginning of the stream
if (has_header && stream->channel_start_offset == stream->offset) { if (stream->channel_start_offset == stream->offset) {
int version, big_endian, header_samples, max_samples_to_do; int version, big_endian, header_samples, max_samples_to_do;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
off_t offset = stream->offset; off_t offset = stream->offset;
@ -1108,16 +1107,10 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
} }
} }
if (has_header) { first_sample -= 10; //todo fix hack (needed to adjust nibble offset below)
first_sample -= 10; //todo fix hack (needed to adjust nibble offset below)
if (step_index < 0) step_index = 0; if (step_index < 0) step_index = 0;
if (step_index > 88) step_index = 88; if (step_index > 88) step_index = 88;
} else {
if (step_index < 0) step_index = 0;
if (step_index > 89) step_index = 89;
}
for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) { for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) {
off_t byte_offset = channelspacing == 1 ? off_t byte_offset = channelspacing == 1 ?
@ -1131,12 +1124,39 @@ void decode_ubi_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspa
outbuf[sample_count] = (short)(hist1); /* all samples are written */ outbuf[sample_count] = (short)(hist1); /* all samples are written */
} }
//external interleave stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
/* standard IMA but with a tweak for Ubi's encoder bug with step index (see blocked_ubi_sce.c) */
void decode_ubi_sce_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int i, sample_count = 0;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
//internal interleave
if (step_index < 0) step_index = 0;
if (step_index > 89) step_index = 89;
for (i = first_sample; i < first_sample + samples_to_do; i++, sample_count += channelspacing) {
off_t byte_offset = channelspacing == 1 ?
stream->offset + i/2 : /* mono mode */
stream->offset + i; /* stereo mode */
int nibble_shift = channelspacing == 1 ?
(!(i%2) ? 4:0) : /* mono mode (high first) */
(channel==0 ? 4:0); /* stereo mode (high=L,low=R) */
std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
outbuf[sample_count] = (short)(hist1); /* all samples are written */
}
stream->adpcm_history1_32 = hist1; stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index; stream->adpcm_step_index = step_index;
} }
/* IMA with variable frame formats controlled by the block layout. The original code uses /* IMA with variable frame formats controlled by the block layout. The original code uses
* tables mapping all standard IMA combinations (to optimize calculations), but decodes the same. * tables mapping all standard IMA combinations (to optimize calculations), but decodes the same.
* Based on HCS's and Nisto's reverse engineering in h4m_audio_decode. */ * Based on HCS's and Nisto's reverse engineering in h4m_audio_decode. */

View File

@ -355,6 +355,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) {
case coding_SNDS_IMA: case coding_SNDS_IMA:
case coding_OTNS_IMA: case coding_OTNS_IMA:
case coding_UBI_IMA: case coding_UBI_IMA:
case coding_UBI_SCE_IMA:
case coding_OKI16: case coding_OKI16:
case coding_OKI4S: case coding_OKI4S:
case coding_MTF_IMA: case coding_MTF_IMA:
@ -580,6 +581,8 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) {
return 0; //todo: 0x01? return 0; //todo: 0x01?
case coding_UBI_IMA: /* variable (PCM then IMA) */ case coding_UBI_IMA: /* variable (PCM then IMA) */
return 0; return 0;
case coding_UBI_SCE_IMA:
return 0;
case coding_XBOX_IMA: case coding_XBOX_IMA:
//todo should be 0x48 when stereo, but blocked/interleave layout don't understand stereo codecs //todo should be 0x48 when stereo, but blocked/interleave layout don't understand stereo codecs
return 0x24; //vgmstream->channels==1 ? 0x24 : 0x48; return 0x24; //vgmstream->channels==1 ? 0x24 : 0x48;
@ -1145,8 +1148,13 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
case coding_UBI_IMA: case coding_UBI_IMA:
for (ch = 0; ch < vgmstream->channels; ch++) { for (ch = 0; ch < vgmstream->channels; ch++) {
decode_ubi_ima(&vgmstream->ch[ch], buffer+ch, decode_ubi_ima(&vgmstream->ch[ch], buffer+ch,
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch);
vgmstream->codec_config); }
break;
case coding_UBI_SCE_IMA:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_ubi_sce_ima(&vgmstream->ch[ch], buffer+ch,
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch);
} }
break; break;
case coding_H4M_IMA: case coding_H4M_IMA:

View File

@ -749,6 +749,7 @@ static const coding_info coding_info_list[] = {
{coding_REF_IMA, "Reflections 4-bit IMA ADPCM"}, {coding_REF_IMA, "Reflections 4-bit IMA ADPCM"},
{coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"}, {coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"},
{coding_UBI_IMA, "Ubisoft 4-bit IMA ADPCM"}, {coding_UBI_IMA, "Ubisoft 4-bit IMA ADPCM"},
{coding_UBI_SCE_IMA, "Ubisoft 4-bit SCE IMA ADPCM"},
{coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"}, {coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"},
{coding_CD_IMA, "Crystal Dynamics 4-bit IMA ADPCM"}, {coding_CD_IMA, "Crystal Dynamics 4-bit IMA ADPCM"},

View File

@ -1,4 +1,5 @@
#include "layout.h" #include "layout.h"
#include "../coding/coding.h"
#include "../vgmstream.h" #include "../vgmstream.h"
/* weird mix of Ubi ADPCM format with Ubi IMA, found in Splinter Cell Essentials (PSP) */ /* weird mix of Ubi ADPCM format with Ubi IMA, found in Splinter Cell Essentials (PSP) */
@ -18,8 +19,6 @@ void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream) {
* (higher bit has a special meaning) * (higher bit has a special meaning)
*/ */
vgmstream->codec_config |= 0x80; /* flag for decoder, ugly I know */
if ((vgmstream->codec_config & 1) == 0) { if ((vgmstream->codec_config & 1) == 0) {
header_size = 0x34; /* read header in first subframe */ header_size = 0x34; /* read header in first subframe */
} }
@ -34,8 +33,8 @@ void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream) {
padding_size = 0x01; padding_size = 0x01;
vgmstream->current_block_offset = block_offset; vgmstream->current_block_offset = block_offset;
vgmstream->current_block_size = subframe_size;
vgmstream->next_block_offset = block_offset + header_size * vgmstream->channels + subframe_size + padding_size; vgmstream->next_block_offset = block_offset + header_size * vgmstream->channels + subframe_size + padding_size;
vgmstream->current_block_samples = ima_bytes_to_samples(subframe_size, channels);
for (i = 0; i < vgmstream->channels; i++) { for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = block_offset + header_size * channels; vgmstream->ch[i].offset = block_offset + header_size * channels;
@ -44,11 +43,11 @@ void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream) {
vgmstream->ch[i].adpcm_step_index = read_32bitLE(block_offset + header_size * i + 0x04, sf); vgmstream->ch[i].adpcm_step_index = read_32bitLE(block_offset + header_size * i + 0x04, sf);
vgmstream->ch[i].adpcm_history1_32 = read_32bitLE(block_offset + header_size * i + 0x08, sf); vgmstream->ch[i].adpcm_history1_32 = read_32bitLE(block_offset + header_size * i + 0x08, sf);
/* First step is always 0x500, not sure if it's a bug or a feature but the game just takes it as is and /* First step is always 0x500, not sure if it's a bug or a feature but the game just takes it as is and
* ends up reading 0 from out-of-bounds memory area which causes a pop at the start. Yikes. * ends up reading 0 from out-of-bounds memory area which causes a pop at the start. Yikes.
* It gets clampled later so the rest of the sound plays ok. * It gets clampled later so the rest of the sound plays ok.
* We put 89 here as our special index which contains 0 to simulate this. * We put 89 here as our special index which contains 0 to simulate this.
*/ */
if (vgmstream->ch[i].adpcm_step_index == 0x500) { if (vgmstream->ch[i].adpcm_step_index == 0x500) {
vgmstream->ch[i].adpcm_step_index = 89; vgmstream->ch[i].adpcm_step_index = 89;
} }

View File

@ -167,7 +167,6 @@ typedef struct {
int num_samples; /* should match manually calculated samples */ int num_samples; /* should match manually calculated samples */
int sample_rate; int sample_rate;
int channels; int channels;
int is_stereo; /* found in old PS2 games */
off_t xma_header_offset; /* some XMA have extra header stuff */ off_t xma_header_offset; /* some XMA have extra header stuff */
int layer_count; /* number of layers in a layer type */ int layer_count; /* number of layers in a layer type */
@ -984,7 +983,7 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h
break; break;
case UBI_IMA_SCE: case UBI_IMA_SCE:
vgmstream->coding_type = coding_UBI_IMA; vgmstream->coding_type = coding_UBI_SCE_IMA;
vgmstream->layout_type = layout_blocked_ubi_sce; vgmstream->layout_type = layout_blocked_ubi_sce;
vgmstream->full_block_size = read_32bitLE(0x18, sf_data); vgmstream->full_block_size = read_32bitLE(0x18, sf_data);
@ -998,7 +997,7 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h
* - Splinter Cell (PC): 4-bit w/ 1ch/2ch (all streams + menu music) * - Splinter Cell (PC): 4-bit w/ 1ch/2ch (all streams + menu music)
* - Batman: Vengeance (PC): 4-bit w/ 1ch/2ch (all streams) * - Batman: Vengeance (PC): 4-bit w/ 1ch/2ch (all streams)
* - Myst IV (PC/Xbox): 4-bit w/ 1ch (amb), some files only (ex. sfx_si_puzzle_stream.sb2) * - Myst IV (PC/Xbox): 4-bit w/ 1ch (amb), some files only (ex. sfx_si_puzzle_stream.sb2)
* - The Jungle Book: Rhythm n' Groove (PC): 4-bit w/ 2ch (music/amb), 6-bit w/ 1ch (speech) * - The Jungle Book: Rhythm N'Groove (PC): 4-bit w/ 2ch (music/amb), 6-bit w/ 1ch (speech)
* - possibly others * - possibly others
* internal extension is .adp, maybe this can be called FMT_ADP */ * internal extension is .adp, maybe this can be called FMT_ADP */
@ -1753,6 +1752,7 @@ static int parse_type_audio_ps2_old(ubi_sb_header* sb, off_t offset, STREAMFILE*
int32_t(*read_32bit)(off_t, STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; int32_t(*read_32bit)(off_t, STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE;
uint32_t pitch; uint32_t pitch;
uint32_t test_sample_rate; uint32_t test_sample_rate;
int is_stereo;
sb->stream_size = read_32bit(offset + sb->cfg.audio_stream_size, sf); sb->stream_size = read_32bit(offset + sb->cfg.audio_stream_size, sf);
sb->stream_offset = read_32bit(offset + sb->cfg.audio_stream_offset, sf); sb->stream_offset = read_32bit(offset + sb->cfg.audio_stream_offset, sf);
@ -1770,10 +1770,10 @@ static int parse_type_audio_ps2_old(ubi_sb_header* sb, off_t offset, STREAMFILE*
sb->is_streamed = read_32bit(offset + sb->cfg.audio_streamed_flag, sf) & sb->cfg.audio_streamed_and; sb->is_streamed = read_32bit(offset + sb->cfg.audio_streamed_flag, sf) & sb->cfg.audio_streamed_and;
sb->loop_flag = read_32bit(offset + sb->cfg.audio_loop_flag, sf) & sb->cfg.audio_loop_and; sb->loop_flag = read_32bit(offset + sb->cfg.audio_loop_flag, sf) & sb->cfg.audio_loop_and;
sb->is_localized = read_32bit(offset + sb->cfg.audio_loc_flag, sf) & sb->cfg.audio_loc_and; sb->is_localized = read_32bit(offset + sb->cfg.audio_loc_flag, sf) & sb->cfg.audio_loc_and;
sb->is_stereo = read_32bit(offset + sb->cfg.audio_stereo_flag, sf) & sb->cfg.audio_stereo_and; is_stereo = read_32bit(offset + sb->cfg.audio_stereo_flag, sf) & sb->cfg.audio_stereo_and;
sb->num_samples = 0; /* calculate from size */ sb->num_samples = 0; /* calculate from size */
sb->channels = sb->is_stereo ? 2 : 1; sb->channels = is_stereo ? 2 : 1;
sb->stream_size *= sb->channels; sb->stream_size *= sb->channels;
sb->subblock_id = 0; sb->subblock_id = 0;
@ -1793,6 +1793,7 @@ fail:
static int parse_type_layer_ps2_old(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) { static int parse_type_layer_ps2_old(ubi_sb_header* sb, off_t offset, STREAMFILE* sf) {
int32_t(*read_32bit)(off_t, STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE; int32_t(*read_32bit)(off_t, STREAMFILE*) = sb->big_endian ? read_32bitBE : read_32bitLE;
uint32_t pitch;
/* much simpler than later iteration */ /* much simpler than later iteration */
sb->layer_count = read_32bit(offset + sb->cfg.layer_layer_count, sf); sb->layer_count = read_32bit(offset + sb->cfg.layer_layer_count, sf);
@ -1809,7 +1810,8 @@ static int parse_type_layer_ps2_old(ubi_sb_header* sb, off_t offset, STREAMFILE*
goto fail; goto fail;
} }
sb->sample_rate = ubi_ps2_pitch_to_freq(read_32bit(offset + sb->cfg.layer_pitch, sf)); pitch = read_32bit(offset + sb->cfg.layer_pitch, sf);
sb->sample_rate = ubi_ps2_pitch_to_freq(pitch);
sb->is_localized = read_32bit(offset + sb->cfg.layer_loc_flag, sf) & sb->cfg.layer_loc_and; sb->is_localized = read_32bit(offset + sb->cfg.layer_loc_flag, sf) & sb->cfg.layer_loc_and;
sb->num_samples = 0; /* calculate from size */ sb->num_samples = 0; /* calculate from size */
@ -1957,8 +1959,7 @@ static int parse_type_sequence(ubi_sb_header* sb, off_t offset, STREAMFILE* sf)
if (!sb->sequence_multibank) { if (!sb->sequence_multibank) {
sb->sequence_multibank = is_other_bank(sb, sf, bank_number); sb->sequence_multibank = is_other_bank(sb, sf, bank_number);
} }
} } else {
else {
entry_number = entry_number & 0x3FFFFFFF; entry_number = entry_number & 0x3FFFFFFF;
if (entry_number > sb->section2_num) { if (entry_number > sb->section2_num) {
VGM_LOG("UBI SB: chain with wrong entry %i vs %i at %x\n", entry_number, sb->section2_num, (uint32_t)sb->extra_offset); VGM_LOG("UBI SB: chain with wrong entry %i vs %i at %x\n", entry_number, sb->section2_num, (uint32_t)sb->extra_offset);
@ -2954,7 +2955,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
if (sb->is_bnm || sb->is_dat || sb->is_ps2_bnm) { if (sb->is_bnm || sb->is_dat || sb->is_ps2_bnm) {
//sb->allowed_types[0x0a] = 1; /* only needed inside sequences */ //sb->allowed_types[0x0a] = 1; /* only needed inside sequences */
sb->allowed_types[0x0b] = 1; sb->allowed_types[0x0b] = 1;
sb->allowed_types[0x09] = 1;
} }
#if 0 #if 0
@ -2984,7 +2984,6 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
sb->version = 0x00000000; sb->version = 0x00000000;
} }
/* Tonic Touble beta has garbage instead of version */ /* Tonic Touble beta has garbage instead of version */
if (sb->is_bnm && sb->version > 0x00000000 && sb->platform == UBI_PC) { if (sb->is_bnm && sb->version > 0x00000000 && sb->platform == UBI_PC) {
if (check_project_file(sf, "ED_MAIN.LCB", 0)) { if (check_project_file(sf, "ED_MAIN.LCB", 0)) {
@ -3084,7 +3083,7 @@ static int config_sb_version(ubi_sb_header* sb, STREAMFILE* sf) {
/* Rayman 2: Revolution (2000)(PS2)-bnm */ /* Rayman 2: Revolution (2000)(PS2)-bnm */
/* Disney's Dinosaur (2000)(PS2)-bnm */ /* Disney's Dinosaur (2000)(PS2)-bnm */
/* Hype: The Time Quest (2001)(PS2)-bnm */ /* Hype: The Time Quest (2001)(PS2)-bnm */
if (sb->version == 0x32787370 && sb->platform == UBI_PS2 && sb->is_ps2_bnm) { if (sb->version == 0x32787370 && sb->platform == UBI_PS2) {
sb->version = 0x00000000; /* for convenience */ sb->version = 0x00000000; /* for convenience */
config_sb_entry(sb, 0x1c, 0x44); config_sb_entry(sb, 0x1c, 0x44);

View File

@ -141,6 +141,7 @@ typedef enum {
coding_REF_IMA, /* Reflections IMA ADPCM */ coding_REF_IMA, /* Reflections IMA ADPCM */
coding_AWC_IMA, /* Rockstar AWC IMA ADPCM */ coding_AWC_IMA, /* Rockstar AWC IMA ADPCM */
coding_UBI_IMA, /* Ubisoft IMA ADPCM */ coding_UBI_IMA, /* Ubisoft IMA ADPCM */
coding_UBI_SCE_IMA, /* Ubisoft SCE IMA ADPCM */
coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */ coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */
coding_MTF_IMA, /* Capcom MT Framework IMA ADPCM */ coding_MTF_IMA, /* Capcom MT Framework IMA ADPCM */
coding_CD_IMA, /* Crystal Dynamics IMA ADPCM */ coding_CD_IMA, /* Crystal Dynamics IMA ADPCM */