From 2c9b8e614b639e6192a33d3a6e490b93e0221c17 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sun, 1 May 2022 18:10:22 +0200 Subject: [PATCH] Add .audio_data [Lego SW: The Skywalker Saga (PC)] --- src/coding/ima_decoder.c | 16 +++--- src/decode.c | 11 +++- src/formats.c | 4 ++ src/layout/blocked.c | 3 + src/layout/blocked_tt_ad.c | 31 +++++++++++ src/layout/layout.h | 1 + src/libvgmstream.vcxproj | 2 + src/libvgmstream.vcxproj.filters | 6 ++ src/meta/meta.h | 2 + src/meta/tt_ad.c | 94 ++++++++++++++++++++++++++++++++ src/render.c | 1 + src/vgmstream.c | 8 ++- src/vgmstream.h | 3 + 13 files changed, 170 insertions(+), 12 deletions(-) create mode 100644 src/layout/blocked_tt_ad.c create mode 100644 src/meta/tt_ad.c diff --git a/src/coding/ima_decoder.c b/src/coding/ima_decoder.c index a6ec41ac..68e6d3f6 100644 --- a/src/coding/ima_decoder.c +++ b/src/coding/ima_decoder.c @@ -563,21 +563,23 @@ void decode_blitz_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channels /* IMA with custom frame sizes, header and nibble layout. Outputs an odd number of samples per frame, * so to simplify calcs this decodes full frames, thus hist doesn't need to be mantained. * Officially defined in "Microsoft Multimedia Standards Update" doc (RIFFNEW.pdf). */ -void decode_ms_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { +void decode_ms_ima(VGMSTREAM* vgmstream, VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { int i, samples_read = 0, samples_done = 0, max_samples; int32_t hist1;// = stream->adpcm_history1_32; int step_index;// = stream->adpcm_step_index; + int frame_channels = vgmstream->codec_config ? 1 : vgmstream->channels; /* mono or mch modes */ + int frame_channel = vgmstream->codec_config ? 0 : channel; /* internal interleave (configurable size), mixed channels */ - int block_samples = ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1; + int block_samples = ((vgmstream->frame_size - 0x04*frame_channels) * 2 / frame_channels) + 1; first_sample = first_sample % block_samples; /* normal header (hist+step+reserved), per channel */ { //if (first_sample == 0) { - off_t header_offset = stream->offset + 0x04*channel; + off_t header_offset = stream->offset + 0x04*frame_channel; - hist1 = read_16bitLE(header_offset+0x00,stream->streamfile); - step_index = read_8bit(header_offset+0x02,stream->streamfile); /* 0x03: reserved */ + hist1 = read_s16le(header_offset+0x00,stream->streamfile); + step_index = read_u8(header_offset+0x02,stream->streamfile); /* 0x03: reserved */ if (step_index < 0) step_index = 0; if (step_index > 88) step_index = 88; @@ -595,7 +597,7 @@ void decode_ms_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * /* decode nibbles (layout: alternates 4 bytes/4*2 nibbles per channel) */ for (i = 0; i < max_samples; i++) { - off_t byte_offset = stream->offset + 0x04*vgmstream->channels + 0x04*channel + 0x04*vgmstream->channels*(i/8) + (i%8)/2; + off_t byte_offset = stream->offset + 0x04*frame_channels + 0x04*frame_channel + 0x04*frame_channels*(i/8) + (i%8)/2; int nibble_shift = (i&1?4:0); /* low nibble first */ std_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); /* original expand */ @@ -609,7 +611,7 @@ void decode_ms_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample_t * /* internal interleave: increment offset on complete frame */ if (first_sample + samples_done == block_samples) { - stream->offset += vgmstream->interleave_block_size; + stream->offset += vgmstream->frame_size; } //stream->adpcm_history1_32 = hist1; diff --git a/src/decode.c b/src/decode.c index 1549e78e..0e82f468 100644 --- a/src/decode.c +++ b/src/decode.c @@ -428,7 +428,9 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) { return 64; case coding_MS_IMA: case coding_REF_IMA: - return ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1; + return ((vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels) + 1;/* +1 from header sample */ + case coding_MS_IMA_mono: + return ((vgmstream->frame_size - 0x04) * 2) + 1; /* +1 from header sample */ case coding_RAD_IMA: return (vgmstream->interleave_block_size - 0x04*vgmstream->channels) * 2 / vgmstream->channels; case coding_NDS_IMA: @@ -628,12 +630,14 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) { case coding_OKI4S: case coding_MTF_IMA: return 0x01; - case coding_MS_IMA: case coding_RAD_IMA: case coding_NDS_IMA: case coding_DAT4_IMA: case coding_REF_IMA: return vgmstream->interleave_block_size; + case coding_MS_IMA: + case coding_MS_IMA_mono: + return vgmstream->frame_size; case coding_AWC_IMA: return 0x800; case coding_RAD_IMA_mono: @@ -931,6 +935,9 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_ } break; case coding_MS_IMA: + case coding_MS_IMA_mono: + //TODO: improve + vgmstream->codec_config = (vgmstream->coding_type == coding_MS_IMA_mono) || vgmstream->channels == 1; /* mono mode */ for (ch = 0; ch < vgmstream->channels; ch++) { decode_ms_ima(vgmstream,&vgmstream->ch[ch], buffer+ch, vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); diff --git a/src/formats.c b/src/formats.c index f96d69c2..d08c0749 100644 --- a/src/formats.c +++ b/src/formats.c @@ -82,6 +82,7 @@ static const char* extension_list[] = { "atx", "aud", "audio", //txth/reserved [Grimm Echoes (Android)] + "audio_data", "aus", "awa", //txth/reserved [Missing Parts Side A (PS2)] "awb", @@ -781,6 +782,7 @@ static const coding_info coding_info_list[] = { {coding_MTF_IMA, "MT Framework 4-bit IMA ADPCM"}, {coding_MS_IMA, "Microsoft 4-bit IMA ADPCM"}, + {coding_MS_IMA_mono, "Microsoft 4-bit IMA ADPCM (mono/interleave)"}, {coding_XBOX_IMA, "XBOX 4-bit IMA ADPCM"}, {coding_XBOX_IMA_mch, "XBOX 4-bit IMA ADPCM (multichannel)"}, {coding_XBOX_IMA_int, "XBOX 4-bit IMA ADPCM (mono/interleave)"}, @@ -928,6 +930,7 @@ static const layout_info layout_info_list[] = { {layout_blocked_vs_square, "blocked (Square VS)"}, {layout_blocked_vid1, "blocked (VID1)"}, {layout_blocked_ubi_sce, "blocked (Ubi SCE)"}, + {layout_blocked_tt_ad, "blocked (TT AD)"}, }; static const meta_info meta_info_list[] = { @@ -1394,6 +1397,7 @@ static const meta_info meta_info_list[] = { {meta_S3V, "Konami S3V header"}, {meta_ESF, "Eurocom ESF header"}, {meta_ADM3, "Crankcase ADM3 header"}, + {meta_TT_AD, "Traveller's Tales AUDIO_DATA header"}, }; void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { diff --git a/src/layout/blocked.c b/src/layout/blocked.c index ef574958..dff0e496 100644 --- a/src/layout/blocked.c +++ b/src/layout/blocked.c @@ -213,6 +213,9 @@ void block_update(off_t block_offset, VGMSTREAM* vgmstream) { case layout_blocked_ubi_sce: block_update_ubi_sce(block_offset,vgmstream); break; + case layout_blocked_tt_ad: + block_update_tt_ad(block_offset,vgmstream); + break; default: /* not a blocked layout */ break; } diff --git a/src/layout/blocked_tt_ad.c b/src/layout/blocked_tt_ad.c new file mode 100644 index 00000000..f5f1be8f --- /dev/null +++ b/src/layout/blocked_tt_ad.c @@ -0,0 +1,31 @@ +#include "layout.h" + +/* Traveller's Tales blocks (.AUDIO_DATA) */ +void block_update_tt_ad(off_t block_offset, VGMSTREAM* vgmstream) { + STREAMFILE* sf = vgmstream->ch[0].streamfile; + uint32_t header_id, block_size, header_size; + int i; + + header_size = 0x00; + block_size = vgmstream->frame_size; + + //TODO could be optimized? + /* first chunk and last frame has an extra header: + * 0x00: id + * 0x04: 0 in FRST, left samples in LAST, others not seen (found in exe) */ + header_id = read_u32be(block_offset, sf); + if (header_id == get_id32be("FRST") || header_id == get_id32be("LAST") || + header_id == get_id32be("LSRT") || header_id == get_id32be("LEND")) { + header_size = 0x08; + } + VGM_ASSERT(header_id == get_id32be("LSRT") || header_id == get_id32be("LEND"), "TT-AD: loop found\n"); + + vgmstream->current_block_offset = block_offset; + vgmstream->current_block_size = block_size /* * vgmstream->channels*/; + vgmstream->next_block_offset = block_offset + block_size * vgmstream->channels + header_size; + + /* MS-IMA = same offset per channel */ + for (i = 0; i < vgmstream->channels; i++) { + vgmstream->ch[i].offset = vgmstream->current_block_offset + header_size + block_size * i; + } +} diff --git a/src/layout/layout.h b/src/layout/layout.h index 79dc63b5..7accda7e 100644 --- a/src/layout/layout.h +++ b/src/layout/layout.h @@ -49,6 +49,7 @@ void block_update_xa_aiff(off_t block_offset, VGMSTREAM* vgmstream); void block_update_vs_square(off_t block_offset, VGMSTREAM* vgmstream); void block_update_vid1(off_t block_offset, VGMSTREAM* vgmstream); void block_update_ubi_sce(off_t block_offset, VGMSTREAM* vgmstream); +void block_update_tt_ad(off_t block_offset, VGMSTREAM* vgmstream); /* other layouts */ void render_vgmstream_interleave(sample_t* buffer, int32_t sample_count, VGMSTREAM* vgmstream); diff --git a/src/libvgmstream.vcxproj b/src/libvgmstream.vcxproj index 9fe1a518..880b3312 100644 --- a/src/libvgmstream.vcxproj +++ b/src/libvgmstream.vcxproj @@ -564,6 +564,7 @@ + @@ -722,6 +723,7 @@ + diff --git a/src/libvgmstream.vcxproj.filters b/src/libvgmstream.vcxproj.filters index f3c95f61..a9123765 100644 --- a/src/libvgmstream.vcxproj.filters +++ b/src/libvgmstream.vcxproj.filters @@ -1159,6 +1159,9 @@ meta\Source Files + + meta\Source Files + meta\Source Files @@ -1606,6 +1609,9 @@ layout\Source Files + + layout\Source Files + layout\Source Files diff --git a/src/meta/meta.h b/src/meta/meta.h index 17402a22..4aff0a00 100644 --- a/src/meta/meta.h +++ b/src/meta/meta.h @@ -986,4 +986,6 @@ VGMSTREAM* init_vgmstream_esf(STREAMFILE* sf); VGMSTREAM* init_vgmstream_adm3(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_tt_ad(STREAMFILE* sf); + #endif /*_META_H*/ diff --git a/src/meta/tt_ad.c b/src/meta/tt_ad.c new file mode 100644 index 00000000..5bfffa50 --- /dev/null +++ b/src/meta/tt_ad.c @@ -0,0 +1,94 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* .AUDIO_DATA - Traveller's Tales "NTT" engine audio format [Lego Star Wars: The Skywalker Saga (PC/Switch)] */ +VGMSTREAM* init_vgmstream_tt_ad(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + uint32_t offset, stream_offset, stream_size; + int loop_flag, channels, sample_rate, codec, frame_size = 0; + int32_t num_samples; + + + /* checks */ + if (!is_id32be(0x00,sf, "FMT ")) + goto fail; + + /* actual extension */ + if (!check_extensions(sf, "audio_data")) + goto fail; + + offset = 0x08; + /* 0x00: null */ + codec = read_u16le(offset + 0x02,sf); + sample_rate = read_s32le(offset + 0x04,sf); + num_samples = read_s32le(offset + 0x08,sf); + channels = read_u8(offset + 0x0c,sf); + /* 0x0d: bps (16=IMA, 32=Ogg) */ + /* 0x10: + Ogg = some size? + IMA = frame size + flag? */ + if (codec == 0x0a) + frame_size = read_u16le(offset + 0x10,sf); + + + loop_flag = 0; /* music just repeats? */ + + offset += read_u32le(0x04, sf); + + /* Ogg seek table*/ + if (is_id32be(offset, sf, "SEEK")) { + offset += 0x08 + read_u32le(offset + 0x04, sf); + } + + /* found with some IMA */ + if (is_id32be(offset, sf, "RMS ")) { + offset += 0x08 + read_u32le(offset + 0x04, sf); + } + + if (!is_id32be(offset, sf, "DATA")) + goto fail; + + stream_offset = offset + 0x08; + stream_size = read_u32le(offset + 0x04, sf); + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_TT_AD; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + + switch(codec) { +#ifdef VGM_USE_VORBIS + case 0x01: { + vgmstream->codec_data = init_ogg_vorbis(sf, stream_offset, stream_size, NULL); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_OGG_VORBIS; + vgmstream->layout_type = layout_none; + + break; + } +#endif + + case 0x0a: + vgmstream->coding_type = coding_MS_IMA_mono; + vgmstream->layout_type = layout_blocked_tt_ad; + vgmstream->frame_size = frame_size; + vgmstream->interleave_block_size = frame_size; + break; + + default: + vgm_logi("FMT: unsupported codec 0x%x\n", codec); + goto fail; + } + + if (!vgmstream_open_stream(vgmstream, sf, stream_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/src/render.c b/src/render.c index 5e5243a2..6233ec84 100644 --- a/src/render.c +++ b/src/render.c @@ -304,6 +304,7 @@ int render_layout(sample_t* buf, int32_t sample_count, VGMSTREAM* vgmstream) { case layout_blocked_vs_square: case layout_blocked_vid1: case layout_blocked_ubi_sce: + case layout_blocked_tt_ad: render_vgmstream_blocked(buf, sample_count, vgmstream); break; case layout_segmented: diff --git a/src/vgmstream.c b/src/vgmstream.c index 9969f904..f3ad67ff 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -524,6 +524,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_s3v, init_vgmstream_esf, init_vgmstream_adm3, + init_vgmstream_tt_ad, /* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */ init_vgmstream_mpeg, @@ -1161,9 +1162,10 @@ int vgmstream_open_stream_bf(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t start_o goto fail; } - if ((vgmstream->coding_type == coding_MSADPCM || - vgmstream->coding_type == coding_MSADPCM_ck || - vgmstream->coding_type == coding_MSADPCM_int) && + if ((vgmstream->coding_type == coding_MSADPCM || vgmstream->coding_type == coding_MSADPCM_ck || + vgmstream->coding_type == coding_MSADPCM_int || + vgmstream->coding_type == coding_MS_IMA || vgmstream->coding_type == coding_MS_IMA_mono + ) && vgmstream->frame_size == 0) { vgmstream->frame_size = vgmstream->interleave_block_size; } diff --git a/src/vgmstream.h b/src/vgmstream.h index 45100628..86e680de 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -114,6 +114,7 @@ typedef enum { coding_BLITZ_IMA, /* Blitz Games 4-bit IMA ADPCM */ coding_MS_IMA, /* Microsoft IMA ADPCM */ + coding_MS_IMA_mono, /* Microsoft IMA ADPCM (mono/interleave) */ coding_XBOX_IMA, /* XBOX IMA ADPCM */ coding_XBOX_IMA_mch, /* XBOX IMA ADPCM (multichannel) */ coding_XBOX_IMA_int, /* XBOX IMA ADPCM (mono/interleave) */ @@ -277,6 +278,7 @@ typedef enum { layout_blocked_vs_square, layout_blocked_vid1, layout_blocked_ubi_sce, + layout_blocked_tt_ad, /* otherwise odd */ layout_segmented, /* song divided in segments (song sections) */ @@ -758,6 +760,7 @@ typedef enum { meta_S3V, meta_ESF, meta_ADM3, + meta_TT_AD, } meta_t;