Add AIFC IMA4 [Alida (PC)]

This commit is contained in:
bnnm 2018-06-02 16:13:37 +02:00
parent 48c11e81ca
commit 88250d3d26
3 changed files with 74 additions and 86 deletions

View File

@ -120,8 +120,9 @@ handling.
A few extensions that vgmstream supports clash with common ones. Since players A few extensions that vgmstream supports clash with common ones. Since players
like foobar or Winamp don't react well to that, they may be renamed for like foobar or Winamp don't react well to that, they may be renamed for
vgmstream (mainly to get looping in some cases). vgmstream (mainly to get looping in some cases).
- .ac3 to .lac3
- .aac to .laac - .aac to .laac
- .ac3 to .lac3
- .aif to .aiffl or .aifcl
- .asf to .sng (EA formats) - .asf to .sng (EA formats)
- .mp4 to .lmp4 - .mp4 to .lmp4
- .ogg to .logg - .ogg to .logg

View File

@ -28,9 +28,11 @@ static const char* extension_list[] = {
"afc", "afc",
"agsc", "agsc",
"ahx", "ahx",
"aifc", //"aif", //common
"aifcl", "aifc", //common?
"aifcl", //fake extension, for AIF???
//"aiff", //common //"aiff", //common
"aiffl", //fake extension, for AIF???
"aix", "aix",
"akb", "akb",
"al2", "al2",
@ -687,8 +689,8 @@ static const meta_info meta_info_list[] = {
{meta_SADL, "Procyon Studio SADL header"}, {meta_SADL, "Procyon Studio SADL header"},
{meta_PS2_BMDX, "Beatmania .bmdx header"}, {meta_PS2_BMDX, "Beatmania .bmdx header"},
{meta_DSP_WSI, "Alone in the Dark .WSI header"}, {meta_DSP_WSI, "Alone in the Dark .WSI header"},
{meta_AIFC, "Audio Interchange File Format AIFF-C"}, {meta_AIFC, "Apple AIFF-C (Audio Interchange File Format) header"},
{meta_AIFF, "Audio Interchange File Format"}, {meta_AIFF, "Apple AIFF (Audio Interchange File Format) header"},
{meta_STR_SNDS, ".str SNDS SHDR chunk"}, {meta_STR_SNDS, ".str SNDS SHDR chunk"},
{meta_WS_AUD, "Westwood Studios .aud header"}, {meta_WS_AUD, "Westwood Studios .aud header"},
{meta_WS_AUD_old, "Westwood Studios .aud (old) header"}, {meta_WS_AUD_old, "Westwood Studios .aud (old) header"},
@ -798,7 +800,7 @@ static const meta_info meta_info_list[] = {
{meta_YDSP, "Yuke's DSP (YDSP) Header"}, {meta_YDSP, "Yuke's DSP (YDSP) Header"},
{meta_MSVP, "MSVP Header"}, {meta_MSVP, "MSVP Header"},
{meta_NGC_SSM, "SSM DSP Header"}, {meta_NGC_SSM, "SSM DSP Header"},
{meta_PS2_JOE, "Disney/Pixar JOE Header"}, {meta_PS2_JOE, "Asobo Studio .JOE header"},
{meta_VGS, "Guitar Hero VGS Header"}, {meta_VGS, "Guitar Hero VGS Header"},
{meta_DC_DCSW_DCS, "Evil Twin DCS file with helper"}, {meta_DC_DCSW_DCS, "Evil Twin DCS file with helper"},
{meta_WII_SMP, "SMP DSP Header"}, {meta_WII_SMP, "SMP DSP Header"},

View File

@ -1,11 +1,6 @@
#include "meta.h" #include "meta.h"
#include "../layout/layout.h" #include "../layout/layout.h"
#include "../util.h"
/* Audio Interchange File Format AIFF-C */
/* also plain AIFF, for good measure */
/* Included primarily for 3DO */
/* for reading integers inexplicably packed into 80 bit floats */ /* for reading integers inexplicably packed into 80 bit floats */
static uint32_t read80bitSANE(off_t offset, STREAMFILE *streamFile) { static uint32_t read80bitSANE(off_t offset, STREAMFILE *streamFile) {
@ -53,9 +48,10 @@ static uint32_t find_marker(STREAMFILE *streamFile, off_t MarkerChunkOffset,
return -1; return -1;
} }
/* Audio Interchange File Format AIFF/AIFF-C - from Mac/3DO games */
VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
off_t file_size = -1; off_t file_size = -1;
int channel_count = 0; int channel_count = 0;
@ -82,22 +78,23 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
int InstrumentChunkFound =0; int InstrumentChunkFound =0;
off_t InstrumentChunkOffset = -1; off_t InstrumentChunkOffset = -1;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename)); /* checks */
if (!strcasecmp("aifc",filename_extension(filename)) || /* .aif: common (AIFF or AIFC), .aiff: common AIFF, .aifc: common AIFC
!strcasecmp("afc",filename_extension(filename)) || * .cbd2: M2 games, .bgm: Super Street Fighter II Turbo (3DO), aifcl/aiffl: for plugins? */
!strcasecmp("aifcl",filename_extension(filename)) || if (check_extensions(streamFile, "aif")) {
!strcasecmp("cbd2",filename_extension(filename)))
{
AIFCext = 1; AIFCext = 1;
}
else if (!strcasecmp("aiff",filename_extension(filename)) ||
!strcasecmp("aif",filename_extension(filename)) ||
!strcasecmp("aiffl",filename_extension(filename)))
{
AIFFext = 1; AIFFext = 1;
} }
else goto fail; else if (check_extensions(streamFile, "aifc,aifcl,afc,cbd2,bgm")) {
AIFCext = 1;
}
else if (check_extensions(streamFile, "aiff,aiffl")) {
AIFFext = 1;
}
else {
goto fail;
}
/* check header */ /* check header */
if ((uint32_t)read_32bitBE(0,streamFile)==0x464F524D && /* "FORM" */ if ((uint32_t)read_32bitBE(0,streamFile)==0x464F524D && /* "FORM" */
@ -115,13 +112,16 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
AIFF = 1; AIFF = 1;
} }
else goto fail; else goto fail;
} else goto fail; }
else {
goto fail;
}
file_size = get_streamfile_size(streamFile); file_size = get_streamfile_size(streamFile);
/* read through chunks to verify format and find metadata */ /* read through chunks to verify format and find metadata */
{ {
off_t current_chunk = 0xc; /* start with first chunk within FORM */ off_t current_chunk = 0x0c; /* start with first chunk within FORM */
while (current_chunk < file_size) { while (current_chunk < file_size) {
uint32_t chunk_type = read_32bitBE(current_chunk,streamFile); uint32_t chunk_type = read_32bitBE(current_chunk,streamFile);
@ -134,54 +134,55 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
if (current_chunk+8+chunk_size > file_size) goto fail; if (current_chunk+8+chunk_size > file_size) goto fail;
switch(chunk_type) { switch(chunk_type) {
case 0x46564552: /* FVER */ case 0x46564552: /* "FVER" (version info) */
/* only one per file */
if (FormatVersionChunkFound) goto fail; if (FormatVersionChunkFound) goto fail;
/* plain AIFF shouldn't have */ if (AIFF) goto fail; /* plain AIFF shouldn't have */
if (AIFF) goto fail;
FormatVersionChunkFound = 1; FormatVersionChunkFound = 1;
/* specific size */ /* specific size */
if (chunk_size != 4) goto fail; if (chunk_size != 4) goto fail;
/* Version 1 of AIFF-C spec timestamp */ /* Version 1 of AIFF-C spec timestamp */
if ((uint32_t)read_32bitBE(current_chunk+8,streamFile) != if ((uint32_t)read_32bitBE(current_chunk+8,streamFile) != 0xA2805140) goto fail;
0xA2805140) goto fail;
break; break;
case 0x434F4D4D: /* COMM */
/* only one per file */ case 0x434F4D4D: /* "COMM" (main header) */
if (CommonChunkFound) goto fail; if (CommonChunkFound) goto fail;
CommonChunkFound = 1; CommonChunkFound = 1;
channel_count = read_16bitBE(current_chunk+8,streamFile); channel_count = read_16bitBE(current_chunk+8,streamFile);
if (channel_count <= 0) goto fail; if (channel_count <= 0) goto fail;
sample_count = (uint32_t)read_32bitBE(current_chunk+0xa,streamFile); sample_count = (uint32_t)read_32bitBE(current_chunk+0x0a,streamFile); /* number of blocks, actually */
sample_size = read_16bitBE(current_chunk+0x0e,streamFile);
sample_size = read_16bitBE(current_chunk+0xe,streamFile);
sample_rate = read80bitSANE(current_chunk+0x10,streamFile); sample_rate = read80bitSANE(current_chunk+0x10,streamFile);
if (AIFC) { if (AIFC) {
switch (read_32bitBE(current_chunk+0x1a,streamFile)) { switch (read_32bitBE(current_chunk+0x1a,streamFile)) {
case 0x53445832: /* SDX2 */ case 0x53445832: /* "SDX2" [3DO games: Super Street Fighter II Turbo (3DO), etc] */
coding_type = coding_SDX2; coding_type = coding_SDX2;
interleave = 1; interleave = 0x01;
break; break;
case 0x43424432: /* CBD2 */ case 0x43424432: /* "CBD2" [M2 (arcade 3DO) games: IMSA Racing (M2), etc] */
coding_type = coding_CBD2; coding_type = coding_CBD2;
interleave = 1; interleave = 0x01;
break; break;
case 0x41445034: /* ADP4 */ case 0x41445034: /* "ADP4" */
coding_type = coding_DVI_IMA_int; coding_type = coding_DVI_IMA_int;
/* don't know how stereo DVI is laid out */ if (channel_count != 1) break; /* don't know how stereo DVI is laid out */
if (channel_count != 1) break; break;
case 0x696D6134: /* "ima4" [Alida (PC) Lunar SSS (iOS)] */
coding_type = coding_APPLE_IMA4;
interleave = 0x22;
sample_count = sample_count * ((interleave-0x2)*2);
break; break;
default: default:
/* we should probably support uncompressed here */ VGM_LOG("AIFC: unknown codec\n");
goto fail; goto fail;
} }
} else if (AIFF) { /* string size and human-readable AIFF-C codec follows */
}
else if (AIFF) {
switch (sample_size) { switch (sample_size) {
case 8: case 8:
coding_type = coding_PCM8; coding_type = coding_PCM8;
@ -191,39 +192,40 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
coding_type = coding_PCM16BE; coding_type = coding_PCM16BE;
interleave = 2; interleave = 2;
break; break;
/* 32 is a possibility, but we don't see it and I
* don't have a reader for it yet */
default: default:
VGM_LOG("AIFF: unknown codec\n");
goto fail; goto fail;
} }
} }
/* we don't check the human-readable portion of AIFF-C*/
break; break;
case 0x53534E44: /* SSND */
/* at most one per file */ case 0x53534E44: /* "SSND" (main data) */
if (SoundDataChunkFound) goto fail; if (SoundDataChunkFound) goto fail;
SoundDataChunkFound = 1; SoundDataChunkFound = 1;
start_offset = current_chunk + 16 + read_32bitBE(current_chunk+8,streamFile); start_offset = current_chunk + 16 + read_32bitBE(current_chunk+8,streamFile);
break; break;
case 0x4D41524B: /* MARK */
case 0x4D41524B: /* "MARK" (loops) */
if (MarkerChunkFound) goto fail; if (MarkerChunkFound) goto fail;
MarkerChunkFound = 1; MarkerChunkFound = 1;
MarkerChunkOffset = current_chunk; MarkerChunkOffset = current_chunk;
break; break;
case 0x494E5354: /* INST */
case 0x494E5354: /* "INST" (loops) */
if (InstrumentChunkFound) goto fail; if (InstrumentChunkFound) goto fail;
InstrumentChunkFound = 1; InstrumentChunkFound = 1;
InstrumentChunkOffset = current_chunk; InstrumentChunkOffset = current_chunk;
break; break;
default: default:
/* spec says we can skip unrecognized chunks */ /* spec says we can skip unrecognized chunks */
break; break;
} }
current_chunk += 8+chunk_size; current_chunk += 0x08+chunk_size;
} }
} }
@ -235,6 +237,7 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
goto fail; goto fail;
} }
/* read loop points */ /* read loop points */
if (InstrumentChunkFound && MarkerChunkFound) { if (InstrumentChunkFound && MarkerChunkFound) {
int start_marker; int start_marker;
@ -262,49 +265,31 @@ VGMSTREAM * init_vgmstream_aifc(STREAMFILE *streamFile) {
} }
} }
/* build the VGMSTREAM */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
/* fill in the vital statistics */
vgmstream->num_samples = sample_count;
vgmstream->sample_rate = sample_rate; vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = sample_count;
vgmstream->coding_type = coding_type;
if (channel_count > 1)
vgmstream->layout_type = layout_interleave;
else
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = interleave;
vgmstream->loop_start_sample = loop_start; vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end; vgmstream->loop_end_sample = loop_end;
vgmstream->coding_type = coding_type;
vgmstream->layout_type = (channel_count > 1) ? layout_interleave : layout_none;
vgmstream->interleave_block_size = interleave;
if (AIFC) if (AIFC)
vgmstream->meta_type = meta_AIFC; vgmstream->meta_type = meta_AIFC;
else if (AIFF) else if (AIFF)
vgmstream->meta_type = meta_AIFF; vgmstream->meta_type = meta_AIFF;
/* open the file, set up each channel */
{
int i;
vgmstream->ch[0].streamfile = streamFile->open(streamFile,filename,
STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!vgmstream->ch[0].streamfile) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = vgmstream->ch[0].streamfile;
vgmstream->ch[i].offset = vgmstream->ch[i].channel_start_offset =
start_offset+i*interleave;
}
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream; return vgmstream;
/* clean up anything we may have opened */
fail: fail:
if (vgmstream) close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }