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;