Merge pull request #1131 from bnnm/adm3-ad

adm3 ad
This commit is contained in:
bnnm 2022-05-01 20:45:08 +02:00 committed by GitHub
commit e51f5dcd66
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 535 additions and 151 deletions

View File

@ -92,6 +92,9 @@ as explained below, but often will use default values. Accepted codec strings:
# * Variation with modified encoding
# - PCM8_U_int PCM 8-bit unsigned (interleave block)
# * Variation with modified encoding
# - PCM_FLOAT_LE PCM 32-bit float little endian
# * For few rare games [Ikinari Maou (Switch)]
# * Interleave is multiple of 0x4 (default)
# - IMA IMA ADPCM (mono/stereo)
# * For some PC games, and rarely consoles
# * Special interleave is multiple of 0x1, often +0x80

View File

@ -491,6 +491,7 @@ void free_mpeg(mpeg_codec_data* data);
int mpeg_get_sample_rate(mpeg_codec_data* data);
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data);
uint32_t mpeg_get_tag_size(STREAMFILE* sf, uint32_t offset, uint32_t header);
int mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info);
#endif

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

@ -340,6 +340,37 @@ int mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info) {
return mpeg_get_frame_info_h(header, info);
}
uint32_t mpeg_get_tag_size(STREAMFILE* sf, uint32_t offset, uint32_t header) {
if (!header)
header = read_u32be(offset+0x00, sf);
/* skip ID3v2 */
if ((header & 0xFFFFFF00) == get_id32be("ID3\0")) {
size_t frame_size = 0;
uint8_t flags = read_u8(offset+0x05, sf);
/* this is how it's officially read :/ */
frame_size += read_u8(offset+0x06, sf) << 21;
frame_size += read_u8(offset+0x07, sf) << 14;
frame_size += read_u8(offset+0x08, sf) << 7;
frame_size += read_u8(offset+0x09, sf) << 0;
frame_size += 0x0a;
if (flags & 0x10) /* footer? */
frame_size += 0x0a;
return frame_size;
}
/* skip ID3v1 */
if ((header & 0xFFFFFF00) == get_id32be("TAG\0")) {
;VGM_LOG("MPEG: ID3v1 at %x\n", offset);
return 0x80;
}
return 0;
}
size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes) {
off_t offset = start_offset;
off_t max_offset = start_offset + bytes;
@ -355,32 +386,13 @@ size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes) {
/* MPEG may use VBR so must read all frames */
while (offset < max_offset) {
uint32_t header = read_u32be(offset+0x00, sf);
/* skip ID3v2 */
if ((header & 0xFFFFFF00) == 0x49443300) { /* "ID3\0" */
size_t frame_size = 0;
uint8_t flags = read_u8(offset+0x05, sf);
/* this is how it's officially read :/ */
frame_size += read_u8(offset+0x06, sf) << 21;
frame_size += read_u8(offset+0x07, sf) << 14;
frame_size += read_u8(offset+0x08, sf) << 7;
frame_size += read_u8(offset+0x09, sf) << 0;
frame_size += 0x0a;
if (flags & 0x10) /* footer? */
frame_size += 0x0a;
offset += frame_size;
size_t tag_size = mpeg_get_tag_size(sf, offset, header);
if (tag_size) {
offset += tag_size;
continue;
}
/* skip ID3v1 */
if ((header & 0xFFFFFF00) == 0x54414700) { /* "TAG\0" */
;VGM_LOG("MPEG: ID3v1 at %lx\n", offset);
offset += 0x80;
continue;
}
/* regular frame */
/* regular frame (assumed) */
if (!mpeg_get_frame_info_h(header, &info)) {
VGM_LOG("MPEG: unknown frame at %lx\n", offset);
break;
@ -407,9 +419,13 @@ size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes) {
}
/* other flags indicate seek table and stuff */
;VGM_LOG("MPEG: found Xing header\n");
/* vendor specific */
if (info.frame_size > xing_offset + 0x78 + 0x24 &&
read_u32be(offset + xing_offset + 0x78, sf) == 0x4C414D45) { /* "LAME" */
if (info.frame_size > xing_offset + 0x78 + 0x24) {
uint32_t sub_id = read_u32be(offset + xing_offset + 0x78, sf);
if (sub_id == get_id32be("LAME") || /* LAME */
sub_id == get_id32be("Lavc")) { /* FFmpeg */
if (info.layer == 3) {
uint32_t delays = read_u32be(offset + xing_offset + 0x8C, sf);
encoder_delay = ((delays >> 12) & 0xFFF);
@ -422,13 +438,12 @@ size_t mpeg_get_samples(STREAMFILE* sf, off_t start_offset, size_t bytes) {
else {
encoder_delay = 240 + 1;
}
}
/* replay gain and stuff */
}
/* there is also "iTunes" vendor with no apparent extra info, iTunes delays are in "iTunSMPB" ID3 tag */
;VGM_LOG("MPEG: found Xing header\n");
break; /* we got samples */
}
}

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",
@ -522,6 +523,7 @@ static const char* extension_list[] = {
"swag",
"swav",
"swd",
"switch", //txth/reserved (.m4a-x.switch) [Ikinari Maou (Switch)]
"switch_audio",
"sx",
"sxd",
@ -780,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)"},
@ -927,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[] = {
@ -1392,6 +1396,8 @@ static const meta_info meta_info_list[] = {
{meta_SSPF, "Konami SSPF header"},
{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

@ -358,7 +358,13 @@ static int get_vgmstream_file_bitrate_main(VGMSTREAM* vgmstream, bitrate_info_t*
* segments use only a few samples from a full file (like Wwise transitions), bitrates
* become a bit high since its hard to detect only part of the file is needed. */
if (vgmstream->layout_type == layout_segmented) {
if (vgmstream->stream_size != 0) {
/* format may report full size for custom layouts that otherwise get odd values */
bitrate += get_vgmstream_file_bitrate_from_size(vgmstream->stream_size, vgmstream->sample_rate, vgmstream->num_samples);
if (p_uniques)
(*p_uniques)++;
}
else if (vgmstream->layout_type == layout_segmented) {
int uniques = 0;
segmented_layout_data *data = (segmented_layout_data *) vgmstream->layout_data;
for (i = 0; i < data->segment_count; i++) {
@ -400,20 +406,20 @@ static int get_vgmstream_file_bitrate_main(VGMSTREAM* vgmstream, bitrate_info_t*
}
if (is_unique) {
size_t stream_size;
size_t file_bitrate;
if (br->count >= br->count_max) goto fail;
if (vgmstream->stream_size) {
/* stream_size applies to both channels but should add once and detect repeats (for current subsong) */
stream_size = get_vgmstream_file_bitrate_from_size(vgmstream->stream_size, vgmstream->sample_rate, vgmstream->num_samples);
file_bitrate = get_vgmstream_file_bitrate_from_size(vgmstream->stream_size, vgmstream->sample_rate, vgmstream->num_samples);
}
else {
stream_size = get_vgmstream_file_bitrate_from_streamfile(sf_cur, vgmstream->sample_rate, vgmstream->num_samples);
file_bitrate = get_vgmstream_file_bitrate_from_streamfile(sf_cur, vgmstream->sample_rate, vgmstream->num_samples);
}
/* possible in cases like using silence codec */
if (!stream_size)
if (!file_bitrate)
break;
br->hash[br->count] = hash_cur;
@ -423,7 +429,7 @@ static int get_vgmstream_file_bitrate_main(VGMSTREAM* vgmstream, bitrate_info_t*
if (p_uniques)
(*p_uniques)++;
bitrate += stream_size;
bitrate += file_bitrate;
break;
}

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

@ -110,7 +110,6 @@
<ClInclude Include="meta\bar_streamfile.h" />
<ClInclude Include="meta\bgw_streamfile.h" />
<ClInclude Include="meta\bnsf_keys.h" />
<ClInclude Include="meta\cri_utf.h" />
<ClInclude Include="meta\deblock_streamfile.h" />
<ClInclude Include="meta\ea_eaac_streamfile.h" />
<ClInclude Include="meta\ea_eaac_opus_streamfile.h" />
@ -172,6 +171,7 @@
<ClInclude Include="coding\tac_decoder_lib_ops.h" />
<ClInclude Include="layout\layout.h" />
<ClInclude Include="util\chunks.h" />
<ClInclude Include="util\cri_utf.h" />
<ClInclude Include="util\endianness.h" />
<ClInclude Include="util\log.h" />
<ClInclude Include="util\m2_psb.h" />
@ -310,6 +310,7 @@
<ClCompile Include="meta\acb.c" />
<ClCompile Include="meta\acm.c" />
<ClCompile Include="meta\acx.c" />
<ClCompile Include="meta\adm3.c" />
<ClCompile Include="meta\adp_konami.c" />
<ClCompile Include="meta\adpcm_capcom.c" />
<ClCompile Include="meta\ads_midway.c" />
@ -339,7 +340,6 @@
<ClCompile Include="meta\ck.c" />
<ClCompile Include="meta\compresswave.c" />
<ClCompile Include="meta\cpk.c" />
<ClCompile Include="meta\cri_utf.c" />
<ClCompile Include="meta\csb.c" />
<ClCompile Include="meta\csmp.c" />
<ClCompile Include="meta\cstr.c" />
@ -563,6 +563,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" />
@ -721,6 +722,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" />
@ -728,6 +730,7 @@
<ClCompile Include="layout\blocked_xa_aiff.c" />
<ClCompile Include="layout\blocked_xvas.c" />
<ClCompile Include="util\chunks.c" />
<ClCompile Include="util\cri_utf.c" />
<ClCompile Include="util\log.c" />
<ClCompile Include="util\m2_psb.c" />
<ClCompile Include="util\text_reader.c" />

View File

@ -95,9 +95,6 @@
<ClInclude Include="meta\bnsf_keys.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\cri_utf.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\deblock_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
@ -314,6 +311,9 @@
<ClInclude Include="util\chunks.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="util\cri_utf.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="util\endianness.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -415,6 +415,9 @@
<ClCompile Include="meta\acx.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\adm3.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\adp_konami.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -487,9 +490,6 @@
<ClCompile Include="meta\cpk.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\cri_utf.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\csb.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -1156,6 +1156,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>
@ -1603,6 +1606,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>
@ -1966,6 +1972,9 @@
<ClCompile Include="util\chunks.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="util\cri_utf.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="util\log.c">
<Filter>Source Files</Filter>
</ClCompile>

View File

@ -1,31 +1,32 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../coding/coding.h"
#include "cri_utf.h"
#include "../util/cri_utf.h"
#define MAX_SEGMENTS 2 /* usually segment0=intro, segment1=loop/main */
/* AAX - segmented ADX [Bayonetta (PS3), Pandora's Tower (Wii), Catherine (X360), Binary Domain (PS3)] */
VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
int loop_flag = 0, channel_count = 0;
VGMSTREAM* init_vgmstream_aax(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
int loop_flag = 0, channels = 0;
int32_t sample_count, loop_start_sample = 0, loop_end_sample = 0;
segmented_layout_data *data = NULL;
segmented_layout_data* data = NULL;
int segment_count, loop_segment = 0, is_hca;
off_t segment_offset[MAX_SEGMENTS];
size_t segment_size[MAX_SEGMENTS];
int i;
utf_context *utf = NULL;
utf_context* utf = NULL;
/* checks */
if (!is_id32be(0x00,sf, "@UTF"))
goto fail;
/* .aax: often with extension (with either HCA or AAX tables)
* (extensionless): sometimes without [PES 2013 (PC)] */
if (!check_extensions(streamFile, "aax,"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x40555446) /* "@UTF" */
if (!check_extensions(sf, "aax,"))
goto fail;
/* .aax contains a simple UTF table, each row being a segment pointing to a CRI audio format */
@ -35,7 +36,7 @@ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) {
uint32_t table_offset = 0x00;
utf = utf_open(streamFile, table_offset, &rows, &name);
utf = utf_open(sf, table_offset, &rows, &name);
if (!utf) goto fail;
if (strcmp(name, "AAX") == 0)
@ -74,7 +75,7 @@ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) {
/* open each segment subfile */
for (i = 0; i < segment_count; i++) {
STREAMFILE* temp_sf = setup_subfile_streamfile(streamFile, segment_offset[i],segment_size[i], (is_hca ? "hca" : "adx"));
STREAMFILE* temp_sf = setup_subfile_streamfile(sf, segment_offset[i],segment_size[i], (is_hca ? "hca" : "adx"));
if (!temp_sf) goto fail;
data->segments[i] = is_hca ?
@ -105,11 +106,11 @@ VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) {
}
}
channel_count = data->output_channels;
channels = data->output_channels;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
vgmstream = allocate_vgmstream(channels,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = data->segments[0]->sample_rate;
@ -135,21 +136,22 @@ fail:
/* CRI's UTF wrapper around DSP [Sonic Colors sfx (Wii), NiGHTS: Journey of Dreams sfx (Wii)] */
VGMSTREAM * init_vgmstream_utf_dsp(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
VGMSTREAM* init_vgmstream_utf_dsp(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
off_t start_offset;
uint8_t loop_flag = 0, channel_count;
uint8_t loop_flag = 0, channels;
uint32_t sample_rate, num_samples, loop_start, loop_end, interleave;
uint32_t data_offset, data_size, header_offset, header_size;
utf_context *utf = NULL;
utf_context* utf = NULL;
/* checks */
if (!is_id32be(0x00,sf, "@UTF"))
goto fail;
/* .aax: assumed
* (extensionless): extracted names inside csb/cpk often don't have extensions */
if (!check_extensions(streamFile, "aax,"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x40555446) /* "@UTF" */
if (!check_extensions(sf, "aax,"))
goto fail;
/* .aax contains a simple UTF table with one row and various columns being header info */
@ -159,7 +161,7 @@ VGMSTREAM * init_vgmstream_utf_dsp(STREAMFILE *streamFile) {
uint32_t table_offset = 0x00;
utf = utf_open(streamFile, table_offset, &rows, &name);
utf = utf_open(sf, table_offset, &rows, &name);
if (!utf) goto fail;
if (strcmp(name, "ADPCM_WII") != 0)
@ -172,7 +174,7 @@ VGMSTREAM * init_vgmstream_utf_dsp(STREAMFILE *streamFile) {
goto fail;
if (!utf_query_u32(utf, 0, "nsmpl", &num_samples))
goto fail;
if (!utf_query_u8(utf, 0, "nch", &channel_count))
if (!utf_query_u8(utf, 0, "nch", &channels))
goto fail;
if (!utf_query_u8(utf, 0, "lpflg", &loop_flag)) /* full loops */
goto fail;
@ -182,21 +184,21 @@ VGMSTREAM * init_vgmstream_utf_dsp(STREAMFILE *streamFile) {
if (!utf_query_data(utf, 0, "header", &header_offset, &header_size))
goto fail;
if (channel_count < 1 || channel_count > 2)
if (channels < 1 || channels > 2)
goto fail;
if (header_size != channel_count * 0x60)
if (header_size != channels * 0x60)
goto fail;
start_offset = data_offset;
interleave = (data_size+7) / 8 * 8 / channel_count;
interleave = (data_size+7) / 8 * 8 / channels;
loop_start = read_32bitBE(header_offset + 0x10, streamFile);
loop_end = read_32bitBE(header_offset + 0x14, streamFile);
loop_start = read_32bitBE(header_offset + 0x10, sf);
loop_end = read_32bitBE(header_offset + 0x14, sf);
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
vgmstream = allocate_vgmstream(channels, loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = sample_rate;
@ -209,9 +211,9 @@ VGMSTREAM * init_vgmstream_utf_dsp(STREAMFILE *streamFile) {
vgmstream->interleave_block_size = interleave;
vgmstream->meta_type = meta_UTF_DSP;
dsp_read_coefs_be(vgmstream, streamFile, header_offset+0x1c, 0x60);
dsp_read_coefs_be(vgmstream, sf, header_offset+0x1c, 0x60);
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;

View File

@ -1,6 +1,6 @@
#include "meta.h"
#include "../coding/coding.h"
#include "cri_utf.h"
#include "../util/cri_utf.h"
/* ACB (Atom Cue sheet Binary) - CRI container of memory audio, often together with a .awb wave bank */

152
src/meta/adm3.c Normal file
View File

@ -0,0 +1,152 @@
#include "meta.h"
#include "../coding/coding.h"
typedef struct {
int total_subsongs;
int target_subsong;
uint32_t stream_offset;
uint32_t stream_size;
int loop_flag;
int sample_rate;
int channels;
int32_t num_samples;
} adm3_header_t;
static int parse_adm3(adm3_header_t* adm3, STREAMFILE* sf);
/* ADM3 - Crankcase Audio REV plugin file [Cyberpunk 2077 (PC), MotoGP 21 (PC)] */
VGMSTREAM* init_vgmstream_adm3(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
adm3_header_t adm3 = {0};
/* checks */
if (!is_id32be(0x00,sf, "ADM3"))
goto fail;
if (!check_extensions(sf, "wem"))
goto fail;
adm3.target_subsong = sf->stream_index;
if (adm3.target_subsong == 0) adm3.target_subsong = 1;
/* ADM3 are files used with the Wwise Crankaudio plugin, that simulate engine noises with
* base internal samples and some internal RPM config (probably). Actual file seems to
* define some combo of samples, this only plays those separate samples.
* Decoder is basically Apple's IMA (internally just "ADPCMDecoder") but transforms to float
* each sample during decode by multiplying by 0.000030518509 */
if (!parse_adm3(&adm3, sf))
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(adm3.channels, adm3.loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_ADM3;
vgmstream->sample_rate = adm3.sample_rate;
vgmstream->num_samples = adm3.num_samples; /* slightly lower than bytes-to-samples */
vgmstream->num_streams = adm3.total_subsongs;
vgmstream->stream_size = adm3.stream_size;
vgmstream->coding_type = coding_APPLE_IMA4;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x22;
if (!vgmstream_open_stream(vgmstream, sf, adm3.stream_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
static int parse_type(adm3_header_t* adm3, STREAMFILE* sf, uint32_t offset) {
if (is_id32be(offset, sf, "RMP1")) {
offset = read_u32le(offset + 0x1c, sf);
if (!parse_type(adm3, sf, offset))
goto fail;
/* 0x24: offset to GRN1 */
}
else if (is_id32be(offset, sf, "SMB1")) {
uint32_t table_count = read_u32le(offset + 0x10, sf);
uint32_t table_offset = read_u32le(offset + 0x18, sf);
int i;
for (i = 0; i < table_count; i++) {
uint32_t smp2_unk = read_u32le(table_offset + i * 0x08 + 0x00, sf);
uint32_t smp2_offset = read_u32le(table_offset + i * 0x08 + 0x04, sf);
if (smp2_unk != 1)
goto fail;
if (!parse_type(adm3, sf, smp2_offset)) /* SMP2 */
goto fail;
}
}
else if (is_id32be(offset, sf, "SMP2")) {
adm3->total_subsongs++;
if (adm3->target_subsong == adm3->total_subsongs) {
/* 0x04 always 0 */
/* 0x08 always 0x00040000 */
adm3->channels = read_u32le(offset + 0x0c, sf);
/* 0x10 float pitch? */
/* 0x14 int pitch? */
/* 0x18 0x0001? */
/* 0x1a 0x0030? (header size?) */
adm3->sample_rate = read_s32le(offset + 0x1c, sf);
adm3->num_samples = read_s32le(offset + 0x20, sf);
adm3->stream_size = read_u32le(offset + 0x24, sf);
/* 0x28 1? */
adm3->stream_offset = read_u32le(offset + 0x2c, sf);
}
}
else {
VGM_LOG("ADM3: unknown at %x\n", offset);
goto fail;
}
return 1;
fail:
return 0;
}
static int parse_adm3(adm3_header_t* adm3, STREAMFILE* sf) {
uint32_t offset;
/* 0x04: null */
/* 0x08: version? */
/* 0x0c: header size */
/* 0x10: data start */
/* rest unknown, looks mostly the same between files */
/* higher ramp, N samples from low to high */
offset = read_u32le(0x0FC, sf);
if (!parse_type(adm3, sf, offset)) goto fail; /* RMP1 */
if (read_u32le(0x100, sf) != 1) goto fail;
/* lower ramp, also N samples */
offset = read_u32le(0x104, sf);
if (!parse_type(adm3, sf, offset)) goto fail; /* RMP1 */
if (read_u32le(0x108, sf) != 1) goto fail;
/* idle engine */
offset = read_u32le(0x10c, sf);
if (!parse_type(adm3, sf, offset)) goto fail; /* SMP2 */
if (read_u32le(0x110, sf) != 1) goto fail;
if (adm3->target_subsong < 0 || adm3->target_subsong > adm3->total_subsongs || adm3->total_subsongs < 1)
goto fail;
return 1;
fail:
return 0;
}

View File

@ -1,6 +1,6 @@
#include "meta.h"
#include "../coding/coding.h"
#include "cri_utf.h"
#include "../util/cri_utf.h"
typedef enum { HCA, CWAV, ADX } cpk_type_t;
@ -26,10 +26,11 @@ VGMSTREAM* init_vgmstream_cpk_memory(STREAMFILE* sf, STREAMFILE* sf_acb) {
/* checks */
if (!check_extensions(sf, "awb"))
goto fail;
if (!is_id32be(0x00,sf, "CPK "))
goto fail;
if (!check_extensions(sf, "awb"))
goto fail;
if (!is_id32be(0x10,sf, "@UTF"))
goto fail;
/* 04: 0xFF? */

View File

@ -1,6 +1,6 @@
#include "meta.h"
#include "../coding/coding.h"
#include "cri_utf.h"
#include "../util/cri_utf.h"
/* CSB (Cue Sheet Binary?) - CRI container of memory audio, often together with a .cpk wave bank */
@ -9,8 +9,8 @@ VGMSTREAM* init_vgmstream_csb(STREAMFILE* sf) {
STREAMFILE* temp_sf = NULL;
off_t subfile_offset;
size_t subfile_size;
utf_context *utf = NULL;
utf_context *utf_sdl = NULL;
utf_context* utf = NULL;
utf_context* utf_sdl = NULL;
int total_subsongs, target_subsong = sf->stream_index;
uint8_t fmt = 0;
const char* stream_name = NULL;

View File

@ -984,4 +984,8 @@ VGMSTREAM* init_vgmstream_s3v(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_esf(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_adm3(STREAMFILE* sf);
VGMSTREAM* init_vgmstream_tt_ad(STREAMFILE* sf);
#endif /*_META_H*/

View File

@ -2,16 +2,33 @@
#include "../coding/coding.h"
/* MPEG - standard MP1/2/3 audio MP3 */
/* MPEG - standard MP1/2/3 audio */
VGMSTREAM* init_vgmstream_mpeg(STREAMFILE* sf) {
#ifdef VGM_USE_MPEG
VGMSTREAM* vgmstream = NULL;
uint32_t start_offset;
int loop_flag = 0;
mpeg_frame_info info = {0};
uint32_t header_id;
/* checks */
if (!mpeg_get_frame_info(sf, 0x00, &info))
header_id = read_u32be(0x00, sf);
if ((header_id & 0xFFF00000) != 0xFFF00000 &&
(header_id & 0xFFFFFF00) != get_id32be("ID3\0") &&
(header_id & 0xFFFFFF00) != get_id32be("TAG\0"))
goto fail;
//TODO: may try init_mpeg as-is, already skips tags
start_offset = 0x00;
while (start_offset < get_streamfile_size(sf)) {
uint32_t tag_size = mpeg_get_tag_size(sf, start_offset, 0);
if (tag_size == 0)
break;
start_offset += tag_size;
}
if (!mpeg_get_frame_info(sf, start_offset, &info))
goto fail;
/* .mp3/mp2: standard (is .mp1 ever used in games?)

View File

@ -654,8 +654,8 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) {
read_u32be(start_offset+0x3c, sf) == 0xFFFFFFFF)
goto fail;
///* MSADPCM .ckd are parsed elsewhere, though they are valid so no big deal if parsed here (just that loops should be ignored) */
if (!fmt.is_at9 && check_extensions(sf, "ckd"))
/* MSADPCM .ckd are parsed elsewhere, though they are valid so no big deal if parsed here (just that loops should be ignored) */
if (fmt.codec == 0x0002 && check_extensions(sf, "ckd"))
goto fail;
/* ignore Gitaroo Man Live! (PSP) multi-RIFF (to allow chunked TXTH) */

View File

@ -21,7 +21,7 @@ VGMSTREAM* init_vgmstream_sspf(STREAMFILE* sf) {
goto fail;
/* extra check to ignore .spc, that are a RAM pack of .ssp with a ~0x800 table at the end */
file_size = read_u32be(0x08, sf) + 0x08; /* without padding */
file_size = read_u32be(0x08, sf); /* without padding */
pad_size = 0;
if (file_size % 0x800) /* add padding */
pad_size = 0x800 - (file_size % 0x800);

View File

@ -2,24 +2,36 @@
#include "../coding/coding.h"
/* Tiger Game.com ADPCM file */
VGMSTREAM * init_vgmstream_tgc(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
VGMSTREAM* init_vgmstream_tgc(STREAMFILE* sf) {
VGMSTREAM* vgmstream = NULL;
uint16_t size;
off_t start_offset;
/* checks */
if (!check_extensions(streamFile, "4"))
if (read_u8(0x00, sf) != 0)
goto fail;
if (!check_extensions(sf, "4"))
goto fail;
size = read_u16be(0x01, sf);
if (size != get_streamfile_size(sf))
goto fail;
start_offset = 0x03;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(1, 0);
if (!vgmstream) goto fail;
vgmstream->sample_rate = 8000;
vgmstream->num_samples = ((uint16_t)read_16bitBE(1, streamFile) - 3) * 2;
vgmstream->num_samples = (size - 0x03) * 2;
vgmstream->meta_type = meta_TGC;
vgmstream->layout_type = layout_none;
vgmstream->coding_type = coding_TGC;
if (!vgmstream_open_stream(vgmstream, streamFile, 3))
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
goto fail;
return vgmstream;

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

@ -46,6 +46,7 @@ typedef enum {
XA,
XA_EA,
CP_YM,
PCM_FLOAT_LE,
UNKNOWN = 99,
} txth_codec_t;
@ -220,6 +221,7 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
case PCM16BE: interleave = 0x02; break;
case PCM8: interleave = 0x01; break;
case PCM8_U: interleave = 0x01; break;
case PCM_FLOAT_LE: interleave = 0x04; break;
default:
break;
}
@ -235,6 +237,7 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
case PCM16BE: coding = coding_PCM16BE; break;
case PCM16LE: coding = coding_PCM16LE; break;
case PCM8: coding = coding_PCM8; break;
case PCM_FLOAT_LE: coding = coding_PCMFLOAT; break;
case SDX2: coding = coding_SDX2; break;
case DVI_IMA: coding = coding_DVI_IMA; break;
#ifdef VGM_USE_MPEG
@ -305,6 +308,7 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) {
case coding_PCM16BE:
case coding_PCM8:
case coding_PCM8_U:
case coding_PCMFLOAT:
case coding_PCM4:
case coding_PCM4_U:
case coding_SDX2:
@ -963,6 +967,7 @@ static txth_codec_t parse_codec(txth_header* txth, const char* val) {
else if (is_string(val,"XA")) return XA;
else if (is_string(val,"XA_EA")) return XA_EA;
else if (is_string(val,"CP_YM")) return CP_YM;
else if (is_string(val,"PCM_FLOAT_LE")) return PCM_FLOAT_LE;
/* special handling */
else if (is_string(val,"name_value")) return txth->name_values[0];
else if (is_string(val,"name_value1")) return txth->name_values[0];
@ -2039,6 +2044,8 @@ static int get_bytes_to_samples(txth_header* txth, uint32_t bytes) {
case PCM8_U_int:
case PCM8_U:
return pcm_bytes_to_samples(bytes, txth->channels, 8);
case PCM_FLOAT_LE:
return pcm_bytes_to_samples(bytes, txth->channels, 32);
case PCM4:
case PCM4_U:
case TGC:

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

@ -1,5 +1,5 @@
#include "cri_utf.h"
#include "../util/log.h"
#include "log.h"
#define UTF_MAX_SCHEMA_SIZE 0x8000 /* arbitrary max */
#define COLUMN_BITMASK_FLAG 0xf0

View File

@ -470,7 +470,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_csb,
init_vgmstream_fwse,
init_vgmstream_fda,
init_vgmstream_tgc,
init_vgmstream_kwb,
init_vgmstream_lrmd,
init_vgmstream_bkhd,
@ -523,6 +522,8 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_opus_rsnd,
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,
@ -536,6 +537,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
init_vgmstream_seb,
init_vgmstream_ps2_pnb,
init_vgmstream_sli_ogg,
init_vgmstream_tgc,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
@ -1160,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) */
@ -757,6 +759,8 @@ typedef enum {
meta_SSPF,
meta_S3V,
meta_ESF,
meta_ADM3,
meta_TT_AD,
} meta_t;