mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-12 09:40:51 +01:00
Fix buggy Ubi HX samples
This commit is contained in:
parent
4eb40f005c
commit
f39069ee20
@ -265,13 +265,12 @@ size_t ptadpcm_bytes_to_samples(size_t bytes, int channels, size_t frame_size);
|
||||
/* ubi_adpcm_decoder */
|
||||
typedef struct ubi_adpcm_codec_data ubi_adpcm_codec_data;
|
||||
|
||||
ubi_adpcm_codec_data* init_ubi_adpcm(STREAMFILE* sf, off_t offset, int channels);
|
||||
ubi_adpcm_codec_data* init_ubi_adpcm(STREAMFILE* sf, uint32_t offset, uint32_t size, int channels);
|
||||
void decode_ubi_adpcm(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do);
|
||||
void reset_ubi_adpcm(ubi_adpcm_codec_data* data);
|
||||
void seek_ubi_adpcm(ubi_adpcm_codec_data* data, int32_t num_sample);
|
||||
void free_ubi_adpcm(ubi_adpcm_codec_data* data);
|
||||
int32_t ubi_adpcm_get_samples(ubi_adpcm_codec_data* data);
|
||||
int32_t ubi_adpcm_bytes_to_samples(ubi_adpcm_codec_data* data, uint32_t size);
|
||||
|
||||
|
||||
/* imuse_decoder */
|
||||
|
@ -71,8 +71,8 @@ struct ubi_adpcm_codec_data {
|
||||
ubi_adpcm_header_data header;
|
||||
ubi_adpcm_channel_data ch[UBI_CHANNELS_MAX];
|
||||
|
||||
off_t start_offset;
|
||||
off_t offset;
|
||||
uint32_t start_offset;
|
||||
uint32_t offset;
|
||||
int subframe_number;
|
||||
|
||||
uint8_t frame[UBI_FRAME_SIZE_MAX];
|
||||
@ -86,16 +86,17 @@ struct ubi_adpcm_codec_data {
|
||||
|
||||
/* *********************************************************************** */
|
||||
|
||||
static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data* data, off_t offset);
|
||||
static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data* data, uint32_t offset, uint32_t size);
|
||||
static void decode_frame(STREAMFILE* sf, ubi_adpcm_codec_data* data);
|
||||
static void fix_samples(ubi_adpcm_codec_data* data, uint32_t size);
|
||||
|
||||
ubi_adpcm_codec_data* init_ubi_adpcm(STREAMFILE* sf, off_t offset, int channels) {
|
||||
ubi_adpcm_codec_data* init_ubi_adpcm(STREAMFILE* sf, uint32_t offset, uint32_t size, int channels) {
|
||||
ubi_adpcm_codec_data* data = NULL;
|
||||
|
||||
data = calloc(1, sizeof(ubi_adpcm_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
if (!parse_header(sf, data, offset)) {
|
||||
if (!parse_header(sf, data, offset, size)) {
|
||||
VGM_LOG("UBI ADPCM: wrong header\n");
|
||||
goto fail;
|
||||
}
|
||||
@ -193,7 +194,7 @@ static void read_header_state(uint8_t* data, ubi_adpcm_header_data* header) {
|
||||
header->channels = get_u32le(data + 0x2c);
|
||||
}
|
||||
|
||||
static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data* data, off_t offset) {
|
||||
static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data* data, uint32_t offset, uint32_t size) {
|
||||
uint8_t buf[0x30];
|
||||
size_t bytes;
|
||||
|
||||
@ -214,6 +215,11 @@ static int parse_header(STREAMFILE* sf, ubi_adpcm_codec_data* data, off_t offset
|
||||
if (data->header.channels > UBI_CHANNELS_MAX || data->header.channels < UBI_CHANNELS_MIN)
|
||||
goto fail;
|
||||
|
||||
/* some kind of internal bug I guess, seen in a few subsongs in Rayman 3 PC demo */
|
||||
if (data->header.sample_count == 0x77E7A374 * data->header.channels) {
|
||||
fix_samples(data, size);
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
@ -548,8 +554,8 @@ static void decode_frame(STREAMFILE* sf, ubi_adpcm_codec_data* data) {
|
||||
|
||||
bytes = read_streamfile(data->frame, data->offset, frame_size, sf);
|
||||
if (bytes != frame_size) {
|
||||
VGM_LOG("UBI ADPCM: wrong bytes read %x vs %x at %lx\n", bytes, frame_size, data->offset);
|
||||
//goto fail; //?
|
||||
VGM_LOG("UBI ADPCM: wrong bytes read %x vs %x at %x\n", bytes, frame_size, data->offset);
|
||||
//goto fail; //may reach EOF earlier
|
||||
}
|
||||
|
||||
if (channels == 1) {
|
||||
@ -587,21 +593,37 @@ int32_t ubi_adpcm_get_samples(ubi_adpcm_codec_data* data) {
|
||||
return data->header.sample_count / data->header.channels;
|
||||
}
|
||||
|
||||
int32_t ubi_adpcm_bytes_to_samples(ubi_adpcm_codec_data* data, uint32_t size) {
|
||||
uint32_t frame_size;
|
||||
static void fix_samples(ubi_adpcm_codec_data* data, uint32_t size) {
|
||||
uint32_t frame_size, setup_size, subframe_size, base_frames, last_size;
|
||||
int subframes;
|
||||
int32_t samples;
|
||||
|
||||
if (!data || !data->header.channels || !data->header.subframes_per_frame)
|
||||
return 0;
|
||||
if (!data || !data->header.channels || !data->header.subframes_per_frame || !size)
|
||||
return;
|
||||
|
||||
size -= 0x30; /* ignore header */
|
||||
|
||||
setup_size = 0x34 * data->header.channels; /* setup per channel */
|
||||
subframe_size = (data->header.codes_per_subframe * data->header.bits_per_sample /*+ 8*/) / 8 + 0x01; /* padding byte */
|
||||
frame_size = setup_size + subframe_size * data->header.subframes_per_frame;
|
||||
|
||||
/* don't trust subframe count */
|
||||
base_frames = ((size - 0x01) / frame_size); /* force smaller size just in case so last frame isn't used */
|
||||
last_size = size - (base_frames * frame_size);
|
||||
subframes = base_frames * data->header.subframes_per_frame;
|
||||
|
||||
size -= 0x30; /* header */
|
||||
samples = base_frames * (data->header.codes_per_subframe * data->header.subframes_per_frame);
|
||||
|
||||
frame_size = 0x34 * data->header.channels; /* setup per channel */
|
||||
frame_size += (data->header.codes_per_subframe * data->header.bits_per_sample /*+ 8*/) * data->header.subframes_per_frame / 8;
|
||||
frame_size += data->header.subframes_per_frame * 0x01; /* padding byte */
|
||||
/* last subframe is shorter (and may contain padding after codes_per_subframe_last), and last frame may not contain all subframes */
|
||||
if (last_size > setup_size + subframe_size) {
|
||||
samples += data->header.codes_per_subframe * (data->header.subframes_per_frame - 1);
|
||||
subframes += (data->header.subframes_per_frame - 1);
|
||||
}
|
||||
|
||||
return ((size - 0x01) / frame_size) * /* force smaller size so last frame isn't used */
|
||||
data->header.codes_per_subframe * data->header.subframes_per_frame +
|
||||
data->header.codes_per_subframe_last * data->header.subframes_per_frame;
|
||||
/* for some reason several files that need fixing seem to have garbage in the 2nd half of last codes */
|
||||
samples += data->header.codes_per_subframe_last / 2;
|
||||
subframes += 1;
|
||||
|
||||
data->header.sample_count = samples; /* for all channels */
|
||||
data->header.subframe_count = subframes;
|
||||
}
|
||||
|
@ -705,18 +705,13 @@ static VGMSTREAM* init_vgmstream_ubi_hx_header(ubi_hx_header* hx, STREAMFILE* sf
|
||||
break;
|
||||
|
||||
case UBI:
|
||||
vgmstream->codec_data = init_ubi_adpcm(sb, hx->stream_offset, vgmstream->channels);
|
||||
vgmstream->codec_data = init_ubi_adpcm(sb, hx->stream_offset, hx->stream_size, vgmstream->channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_UBI_ADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->num_samples = ubi_adpcm_get_samples(vgmstream->codec_data);
|
||||
|
||||
/* some kind of internal bug I guess, seen in a few subsongs in Rayman 3 PC demo, other values are also buggy */
|
||||
if (vgmstream->num_samples == 0x77E7A374) {
|
||||
vgmstream->num_samples = ubi_adpcm_bytes_to_samples(vgmstream->codec_data, hx->stream_size);
|
||||
}
|
||||
|
||||
/* XIII has 6-bit stereo music, Rayman 3 4-bit music, both use 6-bit mono) */
|
||||
break;
|
||||
|
||||
|
@ -1018,7 +1018,7 @@ static VGMSTREAM* init_vgmstream_ubi_sb_base(ubi_sb_header* sb, STREAMFILE* sf_h
|
||||
sb->stream_size -= 0x08;
|
||||
}
|
||||
|
||||
vgmstream->codec_data = init_ubi_adpcm(sf_data, start_offset, vgmstream->channels);
|
||||
vgmstream->codec_data = init_ubi_adpcm(sf_data, start_offset, 0, vgmstream->channels);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_UBI_ADPCM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
Loading…
Reference in New Issue
Block a user