Add .audio_data [Lego SW: The Skywalker Saga (PC)]

This commit is contained in:
bnnm 2022-05-01 18:10:22 +02:00
parent 2a6dd8d782
commit 2c9b8e614b
13 changed files with 170 additions and 12 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -564,6 +564,7 @@
<ClCompile Include="meta\ta_aac.c" />
<ClCompile Include="meta\tac.c" />
<ClCompile Include="meta\thp.c" />
<ClCompile Include="meta\tt_ad.c" />
<ClCompile Include="meta\vgs.c" />
<ClCompile Include="meta\ubi_bao.c" />
<ClCompile Include="meta\ubi_ckd.c" />
@ -722,6 +723,7 @@
<ClCompile Include="layout\blocked_hwas.c" />
<ClCompile Include="layout\blocked_str_snds.c" />
<ClCompile Include="layout\blocked_thp.c" />
<ClCompile Include="layout\blocked_tt_ad.c" />
<ClCompile Include="layout\blocked_vs.c" />
<ClCompile Include="layout\blocked_ws_aud.c" />
<ClCompile Include="layout\blocked_wsi.c" />

View File

@ -1159,6 +1159,9 @@
<ClCompile Include="meta\thp.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\tt_ad.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\vgs.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -1606,6 +1609,9 @@
<ClCompile Include="layout\blocked_thp.c">
<Filter>layout\Source Files</Filter>
</ClCompile>
<ClCompile Include="layout\blocked_tt_ad.c">
<Filter>layout\Source Files</Filter>
</ClCompile>
<ClCompile Include="layout\blocked_vs.c">
<Filter>layout\Source Files</Filter>
</ClCompile>

View File

@ -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*/

94
src/meta/tt_ad.c Normal file
View File

@ -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;
}

View File

@ -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:

View File

@ -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;
}

View File

@ -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;