Merge pull request #204 from bnnm/acm-caff

ACM, CAFF
This commit is contained in:
Christopher Snowhill 2018-03-17 14:06:14 -07:00 committed by GitHub
commit ee26f6ca53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 429 additions and 528 deletions

View File

@ -205,9 +205,10 @@ are used in few games.
- SDX2 2:1 Squareroot-Delta-Exact compression DPCM - SDX2 2:1 Squareroot-Delta-Exact compression DPCM
- CBD2 2:1 Cuberoot-Delta-Exact compression DPCM - CBD2 2:1 Cuberoot-Delta-Exact compression DPCM
- InterPlay ACM - InterPlay ACM
- Visual Art's NWA - VisualArt's NWA
- CRI HCA - CRI HCA
- Electronic Arts MicroTalk a.k.a. UTK or UMT - Electronic Arts MicroTalk a.k.a. UTK or UMT
- FMOD FADPCM 4-bit ADPCM
- Xiph Vorbis (Ogg, FSB5, Wwise, OGL, Silicon Knights) - Xiph Vorbis (Ogg, FSB5, Wwise, OGL, Silicon Knights)
- MPEG MP1/2/3 (standard, AHX, XVAG, FSB, AWC, P3D, etc) - MPEG MP1/2/3 (standard, AHX, XVAG, FSB, AWC, P3D, etc)
- ITU-T G.722.1 (Polycom Siren 7) - ITU-T G.722.1 (Polycom Siren 7)

View File

@ -817,23 +817,65 @@ void acm_reset(ACMStream *acm)
memset(acm->wrapbuf, 0, acm->wrapbuf_len * sizeof(int)); memset(acm->wrapbuf, 0, acm->wrapbuf_len * sizeof(int));
} }
/* interface to vgmstream */
void decode_acm(ACMStream * acm, sample * outbuf, /***********************************************
int32_t samples_to_do, int channelspacing) { * interface to vgmstream
***********************************************/
acm_codec_data *init_acm(STREAMFILE *streamFile) {
acm_codec_data* data = NULL;
ACMStream *acm_stream = NULL;
char filename[PATH_LIMIT];
data = calloc(1,sizeof(acm_codec_data));
if (!data) goto fail;
streamFile->get_name(streamFile,filename,sizeof(filename));
if (acm_open_decoder(&acm_stream,streamFile,filename) != ACM_OK)
goto fail;
data->file = acm_stream;
return data;
fail:
free_acm(data);
return NULL;
}
void decode_acm(acm_codec_data *data, sample * outbuf, int32_t samples_to_do, int channelspacing) {
ACMStream * acm = data->file;
int32_t samples_read = 0; int32_t samples_read = 0;
while (samples_read < samples_to_do) { while (samples_read < samples_to_do) {
int32_t bytes_read_just_now; int32_t bytes_read_just_now = acm_read(
bytes_read_just_now = acm,
acm_read(acm,(char*)( (char*)(outbuf+samples_read*channelspacing),
outbuf+samples_read*channelspacing), (samples_to_do-samples_read)*sizeof(sample)*channelspacing,
(samples_to_do-samples_read)*sizeof(sample)* 0,2,1);
channelspacing,0,2,1);
if (bytes_read_just_now > 0) { if (bytes_read_just_now > 0) {
samples_read += samples_read += bytes_read_just_now/sizeof(sample)/channelspacing;
bytes_read_just_now/sizeof(sample)/channelspacing;
} else { } else {
return; return;
} }
} }
} }
void reset_acm(VGMSTREAM *vgmstream) {
acm_codec_data *data = vgmstream->codec_data;
if (data && data->file) {
acm_reset(data->file);
}
}
void free_acm(acm_codec_data *data) {
if (data) {
if (data->file) {
acm_close(data->file);
}
free(data);
}
}

View File

@ -36,6 +36,7 @@ size_t ima_bytes_to_samples(size_t bytes, int channels);
size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels); size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels);
size_t xbox_ima_bytes_to_samples(size_t bytes, int channels); size_t xbox_ima_bytes_to_samples(size_t bytes, int channels);
size_t ubi_ima_bytes_to_samples(size_t bytes, int channels, STREAMFILE *streamFile, off_t offset); size_t ubi_ima_bytes_to_samples(size_t bytes, int channels, STREAMFILE *streamFile, off_t offset);
size_t apple_ima4_bytes_to_samples(size_t bytes, int channels);
/* ngc_dsp_decoder */ /* ngc_dsp_decoder */
void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_ngc_dsp(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
@ -102,7 +103,10 @@ void decode_cbd2_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
void decode_ws(VGMSTREAM * vgmstream, int channel, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); void decode_ws(VGMSTREAM * vgmstream, int channel, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
/* acm_decoder */ /* acm_decoder */
void decode_acm(ACMStream * acm, sample * outbuf, int32_t samples_to_do, int channelspacing); acm_codec_data *init_acm();
void decode_acm(acm_codec_data *data, sample * outbuf, int32_t samples_to_do, int channelspacing);
void reset_acm(VGMSTREAM *vgmstream);
void free_acm(acm_codec_data *data);
/* nwa_decoder */ /* nwa_decoder */
void decode_nwa(NWAData *nwa, sample *outbuf, int32_t samples_to_do); void decode_nwa(NWAData *nwa, sample *outbuf, int32_t samples_to_do);

View File

@ -867,6 +867,12 @@ size_t xbox_ima_bytes_to_samples(size_t bytes, int channels) {
+ ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); /* unlikely (encoder aligns) */ + ((bytes % block_align) ? ((bytes % block_align) - 4 * channels) * 2 / channels : 0); /* unlikely (encoder aligns) */
} }
size_t apple_ima4_bytes_to_samples(size_t bytes, int channels) {
int block_align = 0x22 * channels;
return (bytes / block_align) * (block_align - 0x02*channels) * 2 / channels
+ ((bytes % block_align) ? ((bytes % block_align) - 0x02*channels) * 2 / channels : 0);
}
size_t ubi_ima_bytes_to_samples(size_t bytes, int channels, STREAMFILE *streamFile, off_t offset) { size_t ubi_ima_bytes_to_samples(size_t bytes, int channels, STREAMFILE *streamFile, off_t offset) {
int version, big_endian, header_samples; int version, big_endian, header_samples;
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;

View File

@ -197,6 +197,7 @@ static const char* extension_list[] = {
"mpdsp", "mpdsp",
"mpds", "mpds",
"msa", "msa",
"msd",
"msf", "msf",
"mss", "mss",
"msvp", "msvp",
@ -509,12 +510,7 @@ static const coding_info coding_info_list[] = {
{coding_CBD2, "Cuberoot-delta-exact (CBD2) 8-bit DPCM"}, {coding_CBD2, "Cuberoot-delta-exact (CBD2) 8-bit DPCM"},
{coding_CBD2_int, "Cuberoot-delta-exact (CBD2) 8-bit DPCM with 1 byte interleave"}, {coding_CBD2_int, "Cuberoot-delta-exact (CBD2) 8-bit DPCM with 1 byte interleave"},
{coding_ACM, "InterPlay ACM"}, {coding_ACM, "InterPlay ACM"},
{coding_NWA0, "NWA DPCM Level 0"}, {coding_NWA, "VisualArt's NWA DPCM"},
{coding_NWA1, "NWA DPCM Level 1"},
{coding_NWA2, "NWA DPCM Level 2"},
{coding_NWA3, "NWA DPCM Level 3"},
{coding_NWA4, "NWA DPCM Level 4"},
{coding_NWA5, "NWA DPCM Level 5"},
{coding_EA_MT, "Electronic Arts MicroTalk"}, {coding_EA_MT, "Electronic Arts MicroTalk"},
@ -581,8 +577,6 @@ static const layout_info layout_info_list[] = {
{layout_blocked_rws, "blocked (RWS)"}, {layout_blocked_rws, "blocked (RWS)"},
{layout_blocked_hwas, "blocked (HWAS)"}, {layout_blocked_hwas, "blocked (HWAS)"},
{layout_tra_blocked, "TRA blocked"}, {layout_tra_blocked, "TRA blocked"},
{layout_acm, "ACM blocked"},
{layout_mus_acm, "multiple ACM files, ACM blocked"},
{layout_aix, "AIX interleave, internally 18-byte interleaved"}, {layout_aix, "AIX interleave, internally 18-byte interleaved"},
{layout_segmented, "segmented"}, {layout_segmented, "segmented"},
{layout_scd_int, "SCD multistream interleave"}, {layout_scd_int, "SCD multistream interleave"},
@ -669,9 +663,9 @@ static const meta_info meta_info_list[] = {
{meta_PS2_SVS, "Square SVS header"}, {meta_PS2_SVS, "Square SVS header"},
{meta_RIFF_WAVE, "RIFF WAVE header"}, {meta_RIFF_WAVE, "RIFF WAVE header"},
{meta_RIFF_WAVE_POS, "RIFF WAVE header and .pos for looping"}, {meta_RIFF_WAVE_POS, "RIFF WAVE header and .pos for looping"},
{meta_NWA, "Visual Art's NWA header"}, {meta_NWA, "VisualArt's NWA header"},
{meta_NWA_NWAINFOINI, "Visual Art's NWA header and NWAINFO.INI for looping"}, {meta_NWA_NWAINFOINI, "VisualArt's NWA header (NWAINFO.INI looping)"},
{meta_NWA_GAMEEXEINI, "Visual Art's NWA header and Gameexe.ini for looping"}, {meta_NWA_GAMEEXEINI, "VisualArt's NWA header (Gameexe.ini looping)"},
{meta_XSS, "Dino Crisis 3 XSS File"}, {meta_XSS, "Dino Crisis 3 XSS File"},
{meta_HGC1, "Knights of the Temple 2 hgC1 Header"}, {meta_HGC1, "Knights of the Temple 2 hgC1 Header"},
{meta_AUS, "Capcom AUS Header"}, {meta_AUS, "Capcom AUS Header"},
@ -701,7 +695,7 @@ static const meta_info meta_info_list[] = {
{meta_BG00, "Falcom BG00 Header"}, {meta_BG00, "Falcom BG00 Header"},
{meta_PS2_RSTM, "Rockstar Games RSTM Header"}, {meta_PS2_RSTM, "Rockstar Games RSTM Header"},
{meta_ACM, "InterPlay ACM Header"}, {meta_ACM, "InterPlay ACM Header"},
{meta_MUS_ACM, "MUS playlist and multiple InterPlay ACM Headered files"}, {meta_MUS_ACM, "InterPlay MUS ACM header"},
{meta_PS2_KCES, "Konami KCES Header"}, {meta_PS2_KCES, "Konami KCES Header"},
{meta_PS2_DXH, "Tokobot Plus DXH Header"}, {meta_PS2_DXH, "Tokobot Plus DXH Header"},
{meta_PS2_PSH, "Dawn of Mana - Seiken Densetsu 4 PSH Header"}, {meta_PS2_PSH, "Dawn of Mana - Seiken Densetsu 4 PSH Header"},
@ -813,7 +807,7 @@ static const meta_info meta_info_list[] = {
{meta_NGC_GCUB, "GCub Header"}, {meta_NGC_GCUB, "GCub Header"},
{meta_NGC_SCK_DSP, "The Scorpion King SCK Header"}, {meta_NGC_SCK_DSP, "The Scorpion King SCK Header"},
{meta_NGC_SWD, "PSF + Standard DSP Headers"}, {meta_NGC_SWD, "PSF + Standard DSP Headers"},
{meta_CAFF, "Apple Core Audio Format Header"}, {meta_CAFF, "Apple Core Audio Format File header"},
{meta_PC_MXST, "Lego Island MxSt Header"}, {meta_PC_MXST, "Lego Island MxSt Header"},
{meta_SAB, "Team17 SAB header"}, {meta_SAB, "Team17 SAB header"},
{meta_MAXIS_XA, "Maxis XAI/XAJ Header"}, {meta_MAXIS_XA, "Maxis XAI/XAJ Header"},
@ -968,8 +962,8 @@ static const meta_info meta_info_list[] = {
{meta_SQEX_MAB, "Square-Enix MAB header"}, {meta_SQEX_MAB, "Square-Enix MAB header"},
{meta_OGG_L2SD, "Ogg Vorbis (L2SD)"}, {meta_OGG_L2SD, "Ogg Vorbis (L2SD)"},
{meta_WAF, "KID WAF header"}, {meta_WAF, "KID WAF header"},
{meta_WAVE, "WayForward .WAVE header"}, {meta_WAVE, "EngineBlack .WAVE header"},
{meta_WAVE_segmented, "WayForward .WAVE header (segmented)"}, {meta_WAVE_segmented, "EngineBlack .WAVE header (segmented)"},
#ifdef VGM_USE_MP4V2 #ifdef VGM_USE_MP4V2
{meta_MP4, "AAC header"}, {meta_MP4, "AAC header"},

View File

@ -68,8 +68,6 @@ void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREA
void render_vgmstream_nolayout(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); void render_vgmstream_nolayout(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream);
void render_vgmstream_mus_acm(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream);
void render_vgmstream_aix(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); void render_vgmstream_aix(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream);
void render_vgmstream_segmented(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream); void render_vgmstream_segmented(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream);

View File

@ -1,50 +0,0 @@
#include "layout.h"
#include "../vgmstream.h"
#include "../coding/acm_decoder.h"
#include "../coding/coding.h"
void render_vgmstream_mus_acm(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream) {
int samples_written=0;
mus_acm_codec_data *data = vgmstream->codec_data;
while (samples_written<sample_count) {
ACMStream *acm = data->files[data->current_file];
int samples_to_do;
int samples_this_block = acm->total_values / acm->info.channels;
if (vgmstream->loop_flag && vgmstream_do_loop(vgmstream)) {
data->current_file = data->loop_start_file;
acm_reset(data->files[data->current_file]);
vgmstream->samples_into_block = 0;
continue;
}
samples_to_do = vgmstream_samples_to_do(samples_this_block, 1, vgmstream);
/*printf("samples_to_do=%d,samples_this_block=%d,samples_written=%d,sample_count=%d\n",samples_to_do,samples_this_block,samples_written,sample_count);*/
if (samples_written+samples_to_do > sample_count)
samples_to_do=sample_count-samples_written;
if (samples_to_do == 0)
{
data->current_file++;
/*printf("next %d, %d samples\n",data->current_file,data->files[data->current_file]->total_values/data->files[data->current_file]->info.channels);*/
/* force loop back to first file in case we're still playing for some
* reason, prevent out of bounds stuff */
if (data->current_file >= data->file_count) data->current_file = 0;
acm_reset(data->files[data->current_file]);
vgmstream->samples_into_block = 0;
continue;
}
/*printf("decode %d samples file %d\n",samples_to_do,data->current_file);*/
decode_acm(acm,
buffer+samples_written*vgmstream->channels,
samples_to_do, vgmstream->channels);
samples_written += samples_to_do;
vgmstream->current_sample += samples_to_do;
vgmstream->samples_into_block+=samples_to_do;
}
}

View File

@ -1758,10 +1758,6 @@
RelativePath=".\layout\blocked_ivaud.c" RelativePath=".\layout\blocked_ivaud.c"
> >
</File> </File>
<File
RelativePath=".\layout\mus_acm_layout.c"
>
</File>
<File <File
RelativePath=".\layout\mxch_blocked.c" RelativePath=".\layout\mxch_blocked.c"
> >

View File

@ -488,7 +488,6 @@
<ClCompile Include="layout\ims_block.c" /> <ClCompile Include="layout\ims_block.c" />
<ClCompile Include="layout\interleave.c" /> <ClCompile Include="layout\interleave.c" />
<ClCompile Include="layout\blocked_ivaud.c" /> <ClCompile Include="layout\blocked_ivaud.c" />
<ClCompile Include="layout\mus_acm_layout.c" />
<ClCompile Include="layout\mxch_blocked.c" /> <ClCompile Include="layout\mxch_blocked.c" />
<ClCompile Include="layout\nolayout.c" /> <ClCompile Include="layout\nolayout.c" />
<ClCompile Include="layout\blocked_adm.c" /> <ClCompile Include="layout\blocked_adm.c" />

View File

@ -1042,9 +1042,6 @@
<ClCompile Include="layout\blocked_ivaud.c"> <ClCompile Include="layout\blocked_ivaud.c">
<Filter>layout\Source Files</Filter> <Filter>layout\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="layout\mus_acm_layout.c">
<Filter>layout\Source Files</Filter>
</ClCompile>
<ClCompile Include="layout\mxch_blocked.c"> <ClCompile Include="layout\mxch_blocked.c">
<Filter>layout\Source Files</Filter> <Filter>layout\Source Files</Filter>
</ClCompile> </ClCompile>

View File

@ -1,66 +1,49 @@
#include "../vgmstream.h"
#include "meta.h" #include "meta.h"
#include "../util.h" #include "../coding/coding.h"
#include "../coding/acm_decoder.h" #include "../coding/acm_decoder.h"
/* InterPlay ACM */ /* ACM - InterPlay infinity engine games [Planescape: Torment (PC), Baldur's Gate (PC)] */
/* The real work is done by libacm */
VGMSTREAM * init_vgmstream_acm(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_acm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
ACMStream *acm_stream = NULL; int loop_flag = 0, channel_count, sample_rate, num_samples;
mus_acm_codec_data *data; acm_codec_data *data = NULL;
char filename[PATH_LIMIT];
int loop_flag = 0; /* checks */
int channel_count; if (!check_extensions(streamFile, "acm"))
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("acm",filename_extension(filename))) goto fail;
/* check header */
if (read_32bitBE(0x0,streamFile) != 0x97280301)
goto fail;
data = calloc(1,sizeof(mus_acm_codec_data));
if (!data) goto fail;
data->files = calloc(1,sizeof(ACMStream *));
if (!data->files) {
free(data); data = NULL;
goto fail; goto fail;
if (read_32bitBE(0x0,streamFile) != 0x97280301) /* header id */
goto fail;
/* init decoder */
{
data = init_acm(streamFile);
if (!data) goto fail;
channel_count = data->file->info.channels;
sample_rate = data->file->info.rate;
num_samples = data->file->total_values / data->file->info.channels;
} }
/* gonna do this a little backwards, open and parse the file
before creating the vgmstream */
if (acm_open_decoder(&acm_stream,streamFile,filename) != ACM_OK) { /* build the VGMSTREAM */
goto fail;
}
channel_count = acm_stream->info.channels;
vgmstream = allocate_vgmstream(channel_count,loop_flag); vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->channels = channel_count; vgmstream->sample_rate = sample_rate;
vgmstream->sample_rate = acm_stream->info.rate; vgmstream->num_samples = num_samples;
vgmstream->coding_type = coding_ACM;
vgmstream->num_samples = acm_stream->total_values / acm_stream->info.channels;
vgmstream->layout_type = layout_acm;
vgmstream->meta_type = meta_ACM;
data->file_count = 1; vgmstream->meta_type = meta_ACM;
data->current_file = 0; vgmstream->coding_type = coding_ACM;
data->files[0] = acm_stream; vgmstream->layout_type = layout_none;
/*data->end_file = -1;*/
vgmstream->codec_data = data; vgmstream->codec_data = data;
return vgmstream; return vgmstream;
/* clean up anything we may have opened */
fail: fail:
if (vgmstream) close_vgmstream(vgmstream); free_acm(data);
close_vgmstream(vgmstream);
return NULL; return NULL;
} }

View File

@ -1,170 +1,160 @@
#include "meta.h" #include "meta.h"
#include "../util.h" #include "../coding/coding.h"
/* Apple Core Audio Format */
/* Apple Core Audio Format File - from iOS games [Vectros (iOS), Ridge Racer Accelerated (iOS)] */
VGMSTREAM * init_vgmstream_apple_caff(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_apple_caff(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT]; off_t start_offset = 0, chunk_offset;
size_t file_size, data_size = 0;
int loop_flag, channel_count = 0, sample_rate = 0;
off_t start_offset = 0; int found_desc = 0 /*, found_pakt = 0*/, found_data = 0;
off_t data_size = 0; uint32_t codec = 0 /*, codec_flags = 0*/;
off_t sample_count = 0; uint32_t bytes_per_packet = 0, samples_per_packet = 0, channels_per_packet = 0, bits_per_sample = 0;
off_t interleave = 0; int valid_samples = 0 /*, priming_samples = 0, unused_samples = 0*/;
int sample_rate = -1, unused_frames = 0;
int channel_count = 0;
off_t file_length;
off_t chunk_offset = 8;
int found_desc = 0, found_pakt = 0, found_data = 0;
/* check extension, case insensitive */ /* checks */
streamFile->get_name(streamFile,filename,sizeof(filename)); if (!check_extensions(streamFile, "caf"))
if (strcasecmp("caf",filename_extension(filename))) goto fail; goto fail;
/* check "caff" id */ if (read_32bitBE(0x00,streamFile) != 0x63616666) /* "caff" */
if (read_32bitBE(0,streamFile)!=0x63616666) goto fail; goto fail;
/* check version, flags */ if (read_32bitBE(0x04,streamFile) != 0x00010000) /* version/flags */
if (read_32bitBE(4,streamFile)!=0x00010000) goto fail; goto fail;
file_length = (off_t)get_streamfile_size(streamFile); file_size = get_streamfile_size(streamFile);
chunk_offset = 0x08;
while (chunk_offset < file_length) while (chunk_offset < file_size) {
{ uint32_t chunk_type = read_32bitBE(chunk_offset+0x00,streamFile);
/* high half of size (expect 0s) */ uint32_t chunk_size = (uint32_t)read_64bitBE(chunk_offset+0x04,streamFile);
if (read_32bitBE(chunk_offset+4,streamFile) != 0) goto fail; chunk_offset += 0x0c;
/* handle chunk type */ switch (chunk_type) {
switch (read_32bitBE(chunk_offset,streamFile))
{ case 0x64657363: /* "desc" */
case 0x64657363: /* desc */
found_desc = 1; found_desc = 1;
{
/* rather than put a convoluted conversion here for
portability, just look it up */
uint32_t sratefloat = read_32bitBE(chunk_offset+0x0c, streamFile);
if (read_32bitBE(chunk_offset+0x10, streamFile) != 0) goto fail;
switch (sratefloat)
{
case 0x40D19400:
sample_rate = 18000;
break;
case 0x40D58880:
sample_rate = 22050;
break;
case 0x40DF4000:
sample_rate = 32000;
break;
case 0x40E58880:
sample_rate = 44100;
break;
case 0x40E77000:
sample_rate = 48000;
break;
default:
goto fail;
}
}
{ {
uint32_t bytes_per_packet, frames_per_packet, channels_per_frame, bits_per_channel; uint64_t sample_long = (uint64_t)read_64bitBE(chunk_offset+0x00, streamFile);
uint32_t codec_4cc = read_32bitBE(chunk_offset+0x14, streamFile); double* sample_double; /* double sample rate, double the fun */
/* only supporting ima4 for now */
if (codec_4cc != 0x696d6134) goto fail;
/* format flags */ sample_double = (double*)&sample_long;
if (read_32bitBE(chunk_offset+0x18, streamFile) != 0) goto fail; sample_rate = (int)(*sample_double);
bytes_per_packet = read_32bitBE(chunk_offset+0x1c, streamFile);
frames_per_packet = read_32bitBE(chunk_offset+0x20, streamFile);
channels_per_frame = read_32bitBE(chunk_offset+0x24, streamFile);
bits_per_channel = read_32bitBE(chunk_offset+0x28, streamFile);
interleave = bytes_per_packet / channels_per_frame;
channel_count = channels_per_frame;
if (channels_per_frame != 1 && channels_per_frame != 2)
goto fail;
/* ima4-specific */
if (frames_per_packet != 64) goto fail;
if ((frames_per_packet / 2 + 2) * channels_per_frame !=
bytes_per_packet) goto fail;
if (bits_per_channel != 0) goto fail;
} }
codec = read_32bitBE(chunk_offset+0x08, streamFile);
//codec_flags = read_32bitBE(chunk_offset+0x0c, streamFile);
bytes_per_packet = read_32bitBE(chunk_offset+0x10, streamFile);
samples_per_packet = read_32bitBE(chunk_offset+0x14, streamFile);
channels_per_packet = read_32bitBE(chunk_offset+0x18, streamFile);
bits_per_sample = read_32bitBE(chunk_offset+0x1C, streamFile);
break; break;
case 0x70616b74: /* pakt */
found_pakt = 1; case 0x70616b74: /* "pakt" */
/* 64-bit packet table size, 0 for constant bitrate */ //found_pakt = 1;
if (
read_32bitBE(chunk_offset+0x0c,streamFile) != 0 || //packets_table_size = (uint32_t)read_64bitBE(chunk_offset+0x00,streamFile); /* 0 for constant bitrate */
read_32bitBE(chunk_offset+0x10,streamFile) != 0) goto fail; valid_samples = (uint32_t)read_64bitBE(chunk_offset+0x08,streamFile);
/* high half of valid frames count */ //priming_samples = read_32bitBE(chunk_offset+0x10,streamFile); /* encoder delay samples */
if (read_32bitBE(chunk_offset+0x14,streamFile) != 0) goto fail; //unused_samples = read_32bitBE(chunk_offset+0x14,streamFile); /* footer samples */
/* frame count */
sample_count = read_32bitBE(chunk_offset+0x18,streamFile);
/* priming frames */
if (read_32bitBE(chunk_offset+0x1c,streamFile) != 0) goto fail;
/* remainder (unused) frames */
unused_frames = read_32bitBE(chunk_offset+0x20,streamFile);
break; break;
case 0x66726565: /* free */
/* padding, ignore */ case 0x64617461: /* "data" */
break;
case 0x64617461: /* data */
if (read_32bitBE(chunk_offset+12,streamFile) != 1) goto fail;
found_data = 1; found_data = 1;
start_offset = chunk_offset + 16;
data_size = read_32bitBE(chunk_offset+8,streamFile) - 4; /* 0x00: version? 0x00/0x01 */
start_offset = chunk_offset + 0x04;
data_size = chunk_size - 0x4;
break;
default: /* "free" "kuki" "info" "chan" etc: ignore */
break; break;
default:
goto fail;
} }
/* done with chunk */ /* done with chunk */
chunk_offset += 12 + read_32bitBE(chunk_offset+8,streamFile); chunk_offset += chunk_size;
} }
if (!found_pakt || !found_desc || !found_data) goto fail; if (!found_desc || !found_data)
if (start_offset == 0 || data_size == 0 || sample_count == 0 || goto fail;
sample_rate == -1 || channel_count == 0) goto fail; if (start_offset == 0 || data_size == 0)
goto fail;
/* ima4-specific */
/* check for full packets */
if (data_size % (interleave*channel_count) != 0) goto fail;
if ((sample_count+unused_frames)%((interleave-2)*2) != 0) goto fail;
/* check that all packets are accounted for */
if (data_size/interleave/channel_count !=
(sample_count+unused_frames)/((interleave-2)*2)) goto fail;
vgmstream = allocate_vgmstream(channel_count,0); loop_flag = 0;
channel_count = channels_per_packet;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail; if (!vgmstream) goto fail;
vgmstream->channels = channel_count;
vgmstream->sample_rate = sample_rate; vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = sample_count;
/* ima4-specific */
vgmstream->coding_type = coding_APPLE_IMA4;
if (channel_count == 2)
vgmstream->layout_type = layout_interleave;
else
vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = interleave;
vgmstream->meta_type = meta_CAFF; vgmstream->meta_type = meta_CAFF;
/* open the file for reading by each channel */ switch(codec) {
{ case 0x6C70636D: /* "lpcm" */
int i; vgmstream->num_samples = valid_samples;
for (i=0;i<channel_count;i++) if (!vgmstream->num_samples)
{ vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bits_per_sample);
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!vgmstream->ch[i].streamfile) goto fail;
vgmstream->ch[i].offset = //todo check codec_flags for BE/LE, signed/etc
vgmstream->ch[i].channel_start_offset = if (bits_per_sample == 8) {
start_offset + interleave * i; vgmstream->coding_type = coding_PCM8;
} }
else {
goto fail;
}
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = bytes_per_packet / channels_per_packet;
break;
case 0x696D6134: /* "ima4" */
vgmstream->num_samples = valid_samples;
if (!vgmstream->num_samples) /* rare [Endless Fables 2 (iOS) */
vgmstream->num_samples = apple_ima4_bytes_to_samples(data_size, channel_count);
vgmstream->coding_type = coding_APPLE_IMA4;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = bytes_per_packet / channels_per_packet;
/* ima4 defaults */
//if (channels_per_packet != 1 && channels_per_packet != 2)
// goto fail;
if (samples_per_packet != 64)
goto fail;
if ((samples_per_packet / 2 + 2) * channels_per_packet != bytes_per_packet)
goto fail;
if (bits_per_sample != 0 && bits_per_sample != 4) /* 4 is rare too [Endless Fables 2 (iOS) */
goto fail;
/* check for full packets and that all packets are accounted for */
//if (found_pakt) {
// if (data_size % (vgmstream->interleave_block_size*channel_count) != 0)
// goto fail;
// if ((valid_samples+unused_samples)%((vgmstream->interleave_block_size-2)*2) != 0)
// goto fail;
// if (data_size/vgmstream->interleave_block_size/channel_count !=
// (valid_samples+unused_samples)/((vgmstream->interleave_block_size-2)*2))
// goto fail;
//}
break;
default: /* "aac " "alac" etc: probably parsed by FFMpeg... */
VGM_LOG("CAFF: unknown codec %x\n", codec);
goto fail;
} }
goto fail;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream; return vgmstream;
fail: fail:
if (vgmstream) close_vgmstream(vgmstream); close_vgmstream(vgmstream);
return NULL; return NULL;
} }

View File

@ -181,8 +181,9 @@ VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE *streamFile) {
/* check extension, case insensitive */ /* check extension, case insensitive */
/* .bin: Final Fantasy Dimensions (iOS), Final Fantasy V (iOS) */ /* .bin: Final Fantasy Dimensions (iOS), Final Fantasy V (iOS)
if (!check_extensions(streamFile,"mp4,m4a,m4v,lmp4,bin")) * .msd: UNO (iOS) */
if (!check_extensions(streamFile,"mp4,m4a,m4v,lmp4,bin,msd"))
goto fail; goto fail;
filesize = streamFile->get_size(streamFile); filesize = streamFile->get_size(streamFile);

View File

@ -1,7 +1,6 @@
#include "../vgmstream.h"
#include "meta.h" #include "meta.h"
#include "../util.h" #include "../layout/layout.h"
#include "../streamfile.h" #include "../coding/coding.h"
#include "../coding/acm_decoder.h" #include "../coding/acm_decoder.h"
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -13,11 +12,107 @@
#define DIRSEP '/' #define DIRSEP '/'
#endif #endif
static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_loop_flag, int *out_loop_start_index, int *out_loop_end_index);
static void clean_mus(char** mus_filenames, int file_count);
/* .MUS - playlist for InterPlay games [Planescape: Torment (PC), Baldur's Gate Enhanced Edition (PC)] */
VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
segmented_layout_data *data = NULL;
int channel_count, loop_flag = 0, loop_start_index = -1, loop_end_index = -1;
int32_t num_samples = 0, loop_start_samples = 0, loop_end_samples = 0;
char** mus_filenames = NULL;
int i, segment_count = 0;
/* checks */
if (!check_extensions(streamFile, "mus"))
goto fail;
/* get file paths from the .MUS text file */
mus_filenames = parse_mus(streamFile, &segment_count, &loop_flag, &loop_start_index, &loop_end_index);
if (!mus_filenames) goto fail;
/* init layout */
data = init_layout_segmented(segment_count);
if (!data) goto fail;
/* open each segment subfile */
for (i = 0; i < segment_count; i++) {
STREAMFILE* temp_streamFile = streamFile->open(streamFile, mus_filenames[i], STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!temp_streamFile) goto fail;
/* find .ACM type */
switch(read_32bitBE(0x00,temp_streamFile)) {
case 0x97280301: /* ACM header id [Planescape: Torment (PC)] */
data->segments[i] = init_vgmstream_acm(temp_streamFile);
break;
case 0x4F676753: /* "OggS" [Planescape: Torment Enhanced Edition (PC)] */
data->segments[i] = init_vgmstream_ogg_vorbis(temp_streamFile);
break;
default:
data->segments[i] = NULL;
break;
}
close_streamfile(temp_streamFile);
if (!data->segments[i]) goto fail;
if (i==loop_start_index)
loop_start_samples = num_samples;
if (i==loop_end_index)
loop_end_samples = num_samples;
num_samples += data->segments[i]->num_samples;
}
if (i==loop_end_index)
loop_end_samples = num_samples;
/* setup segmented VGMSTREAMs */
if (!setup_layout_segmented(data))
goto fail;
channel_count = data->segments[0]->channels;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = data->segments[0]->sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start_samples;
vgmstream->loop_end_sample = loop_end_samples;
vgmstream->meta_type = meta_MUS_ACM;
vgmstream->coding_type = data->segments[0]->coding_type;
vgmstream->layout_type = layout_segmented;
vgmstream->layout_data = data;
data->loop_segment = loop_start_index;
clean_mus(mus_filenames, segment_count);
return vgmstream;
fail:
clean_mus(mus_filenames, segment_count);
free_layout_segmented(data);
close_vgmstream(vgmstream);
return NULL;
}
/* .mus text file parsing */
#define NAME_LENGTH PATH_LIMIT #define NAME_LENGTH PATH_LIMIT
static int exists(char *filename, STREAMFILE *streamfile) { static int exists(char *filename, STREAMFILE *streamfile) {
STREAMFILE * temp = STREAMFILE * temp = streamfile->open(streamfile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
streamfile->open(streamfile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!temp) return 0; if (!temp) return 0;
close_streamfile(temp); close_streamfile(temp);
@ -91,57 +186,40 @@ fail:
return 1; return 1;
} }
/* MUS playlist for InterPlay ACM */ static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_loop_flag, int *out_loop_start_index, int *out_loop_end_index) {
VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) { char** names = NULL;
VGMSTREAM * vgmstream = NULL;
ACMStream *acm_stream = NULL;
mus_acm_codec_data *data = NULL;
char filename[NAME_LENGTH]; char filename[NAME_LENGTH];
char line_buffer[NAME_LENGTH]; char line_buffer[NAME_LENGTH];
char * end_ptr; char * end_ptr;
char name_base[NAME_LENGTH]; char name_base[NAME_LENGTH];
char (*names)[NAME_LENGTH] = NULL;
char dir_name[NAME_LENGTH]; char dir_name[NAME_LENGTH];
char subdir_name[NAME_LENGTH]; char subdir_name[NAME_LENGTH];
int i;
int loop_flag = 0;
int channel_count;
int file_count; int file_count;
size_t line_bytes; size_t line_bytes;
int whole_line_read = 0; int whole_line_read = 0;
off_t mus_offset = 0; off_t mus_offset = 0;
int loop_end_index = -1; int i;
int loop_start_index = -1; int loop_flag = 0, loop_start_index = -1, loop_end_index = -1;
int32_t loop_start_samples = -1;
int32_t loop_end_samples = -1;
int32_t total_samples = 0;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("mus",filename_extension(filename))) goto fail;
/* read file name base */ /* read file name base */
line_bytes = get_streamfile_text_line(sizeof(line_buffer),line_buffer, line_bytes = get_streamfile_text_line(sizeof(line_buffer),line_buffer, mus_offset, streamFile, &whole_line_read);
mus_offset, streamFile, &whole_line_read);
if (!whole_line_read) goto fail; if (!whole_line_read) goto fail;
mus_offset += line_bytes; mus_offset += line_bytes;
memcpy(name_base,line_buffer,sizeof(name_base)); memcpy(name_base,line_buffer,sizeof(name_base));
/* uppercase name_base */ /* uppercase name_base */
{ {
int i; int j;
for (i=0;name_base[i];i++) name_base[i]=toupper(name_base[i]); for (j=0;name_base[j];j++)
name_base[j] = toupper(name_base[j]);
} }
/*printf("name base: %s\n",name_base);*/
/* read track entry count */ /* read track entry count */
line_bytes = get_streamfile_text_line(sizeof(line_buffer),line_buffer, line_bytes = get_streamfile_text_line(sizeof(line_buffer),line_buffer, mus_offset, streamFile, &whole_line_read);
mus_offset, streamFile, &whole_line_read);
if (!whole_line_read) goto fail; if (!whole_line_read) goto fail;
if (line_buffer[0] == '\0') goto fail; if (line_buffer[0] == '\0') goto fail;
mus_offset += line_bytes; mus_offset += line_bytes;
@ -149,24 +227,26 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
/* didn't parse whole line as an integer (optional opening whitespace) */ /* didn't parse whole line as an integer (optional opening whitespace) */
if (*end_ptr != '\0') goto fail; if (*end_ptr != '\0') goto fail;
/*printf("entries: %d\n",file_count);*/ /* set names */
names = calloc(file_count,sizeof(char*)); /* array of strings (size NAME_LENGTH) */
names = calloc(file_count,sizeof(names[0]));
if (!names) goto fail; if (!names) goto fail;
for (i = 0; i < file_count; i++) {
names[i] = calloc(1,sizeof(char)*NAME_LENGTH);
if (!names[i]) goto fail;
}
dir_name[0]='\0'; dir_name[0]='\0';
streamFile->get_name(streamFile,filename,sizeof(filename));
concatn(sizeof(dir_name),dir_name,filename); concatn(sizeof(dir_name),dir_name,filename);
/* find directory name for the directory contianing the MUS */
{ {
/* find directory name for the directory contianing the MUS */ char * last_slash = strrchr(dir_name,DIRSEP);
char * last_slash;
last_slash = strrchr(dir_name,DIRSEP);
if (last_slash != NULL) { if (last_slash != NULL) {
/* trim off the file name */ last_slash[1]='\0'; /* trim off the file name */
last_slash[1]='\0';
} else { } else {
/* no dir name? annihilate! */ dir_name[0] = '\0'; /* no dir name? annihilate! */
dir_name[0] = '\0';
} }
} }
@ -180,7 +260,8 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
char loop_name_temp[NAME_LENGTH]; char loop_name_temp[NAME_LENGTH];
char loop_name_base[NAME_LENGTH]; char loop_name_base[NAME_LENGTH];
char loop_name[NAME_LENGTH]; char loop_name[NAME_LENGTH];
for (i=0;i<file_count;i++)
for (i = 0; i < file_count; i++)
{ {
int fields_matched; int fields_matched;
line_bytes = line_bytes =
@ -192,7 +273,6 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
fields_matched = sscanf(line_buffer,"%s %s %s",name, fields_matched = sscanf(line_buffer,"%s %s %s",name,
loop_name_base_temp,loop_name_temp); loop_name_base_temp,loop_name_temp);
if (fields_matched < 1) goto fail; if (fields_matched < 1) goto fail;
if (fields_matched == 3 && loop_name_base_temp[0] != '@' && loop_name_temp[0] != '@') if (fields_matched == 3 && loop_name_base_temp[0] != '@' && loop_name_temp[0] != '@')
{ {
@ -222,14 +302,15 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
{ {
/* uppercase */ /* uppercase */
int j; int j;
for (j=0;j<strlen(name);j++) name[j]=toupper(name[j]); for (j=0;j<strlen(name);j++)
name[j]=toupper(name[j]);
} }
/* try looking in the common directory */ /* try looking in the common directory */
names[i][0] = '\0'; names[i][0] = '\0';
concatn(sizeof(names[0]),names[i],dir_name); concatn(NAME_LENGTH,names[i],dir_name);
concatn(sizeof(names[0]),names[i],name); concatn(NAME_LENGTH,names[i],name);
concatn(sizeof(names[0]),names[i],".ACM"); concatn(NAME_LENGTH,names[i],".ACM");
if (!exists(names[i],streamFile)) { if (!exists(names[i],streamFile)) {
@ -242,16 +323,14 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
} }
names[i][0] = '\0'; names[i][0] = '\0';
concatn(sizeof(names[0]),names[i],dir_name); concatn(NAME_LENGTH,names[i],dir_name);
concatn(sizeof(names[0]),names[i],subdir_name); concatn(NAME_LENGTH,names[i],subdir_name);
concatn(sizeof(names[0]),names[i],name_base); concatn(NAME_LENGTH,names[i],name_base);
concatn(sizeof(names[0]),names[i],name); concatn(NAME_LENGTH,names[i],name);
concatn(sizeof(names[0]),names[i],".ACM"); concatn(NAME_LENGTH,names[i],".ACM");
if (!exists(names[i],streamFile)) goto fail; if (!exists(names[i],streamFile)) goto fail;
} }
/*printf("%2d %s\n",i,names[i]);*/
} }
if (loop_end_index != -1) { if (loop_end_index != -1) {
@ -263,18 +342,15 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
concatn(sizeof(target_name),target_name,loop_name_base); concatn(sizeof(target_name),target_name,loop_name_base);
concatn(sizeof(target_name),target_name,loop_name); concatn(sizeof(target_name),target_name,loop_name);
concatn(sizeof(target_name),target_name,".ACM"); concatn(sizeof(target_name),target_name,".ACM");
/*printf("looking for loop %s\n",target_name);*/
for (i=0;i<file_count;i++) { for (i=0;i<file_count;i++) {
if (!strcmp(target_name,names[i])) if (!strcmp(target_name,names[i])) {
{
loop_start_index = i; loop_start_index = i;
break; break;
} }
} }
if (loop_start_index != -1) { if (loop_start_index != -1) {
/*printf("loop from %d to %d\n",loop_end_index,loop_start_index);*/
/*if (loop_start_index < file_count-1) loop_start_index++;*/ /*if (loop_start_index < file_count-1) loop_start_index++;*/
loop_end_index++; loop_end_index++;
loop_flag = 1; loop_flag = 1;
@ -283,78 +359,25 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
} }
} }
/* set up the struct to track the files */ *out_loop_start_index = loop_start_index;
data = calloc(1,sizeof(mus_acm_codec_data)); *out_loop_end_index = loop_end_index;
if (!data) goto fail; *out_loop_flag = loop_flag;
*out_file_count = file_count;
data->files = calloc(file_count,sizeof(ACMStream *));
if (!data->files) {
free(data); data = NULL;
goto fail;
}
/* open each file... */ return names;
for (i=0;i<file_count;i++) {
/* gonna do this a little backwards, open and parse the file
before creating the vgmstream */
if (acm_open_decoder(&acm_stream,streamFile,names[i]) != ACM_OK) {
goto fail;
}
data->files[i]=acm_stream;
if (i==loop_start_index) loop_start_samples = total_samples;
if (i==loop_end_index) loop_end_samples = total_samples;
total_samples += acm_stream->total_values / acm_stream->info.channels;
if (i>0) {
if (acm_stream->info.channels != data->files[0]->info.channels ||
acm_stream->info.rate != data->files[0]->info.rate) goto fail;
}
}
if (i==loop_end_index) loop_end_samples = total_samples;
channel_count = data->files[0]->info.channels;
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->channels = channel_count;
vgmstream->sample_rate = data->files[0]->info.rate;
vgmstream->coding_type = coding_ACM;
vgmstream->num_samples = total_samples;
vgmstream->loop_start_sample = loop_start_samples;
vgmstream->loop_end_sample = loop_end_samples;
vgmstream->layout_type = layout_mus_acm;
vgmstream->meta_type = meta_MUS_ACM;
data->file_count = file_count;
data->current_file = 0;
data->loop_start_file = loop_start_index;
data->loop_end_file = loop_end_index;
/*data->end_file = -1;*/
vgmstream->codec_data = data;
free(names);
return vgmstream;
/* clean up anything we may have opened */
fail: fail:
if (data) { clean_mus(names, file_count);
int i;
for (i=0;i<data->file_count;i++) {
if (data->files[i]) {
acm_close(data->files[i]);
data->files[i] = NULL;
}
}
}
if (names) free(names);
if (vgmstream) close_vgmstream(vgmstream);
return NULL; return NULL;
} }
static void clean_mus(char** mus_filenames, int file_count) {
int i;
if (!mus_filenames) return;
for (i = 0; i < file_count; i++) {
free(mus_filenames[i]);
}
free(mus_filenames);
}

View File

@ -88,7 +88,8 @@ VGMSTREAM * init_vgmstream_musx_v005(STREAMFILE *streamFile) {
/* checks */ /* checks */
if (!check_extensions(streamFile, "musx")) /* .sfx: Batman Begins, .musx: header id */
if (!check_extensions(streamFile, "musx,sfx"))
goto fail; goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */ if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */
goto fail; goto fail;
@ -158,27 +159,27 @@ fail:
} }
/* MUSX (Version 006) */ /* MUSX (Version 006) [Batman Begins (GC)] */
VGMSTREAM * init_vgmstream_musx_v006(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_musx_v006(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
int loop_flag;
int channel_count;
off_t start_offset; off_t start_offset;
int loop_flag, channel_count;
/* check extension, case insensitive */
streamFile->get_name(streamFile,filename,sizeof(filename));
if (strcasecmp("musx",filename_extension(filename))) goto fail;
/* check header */ /* checks */
/* .sfx: Batman Begins, .musx: header id */
if (!check_extensions(streamFile, "sfx,musx"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */ if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */
goto fail; goto fail;
if (read_32bitBE(0x08,streamFile) != 0x06000000) /* "0x06000000" */ if (read_32bitBE(0x08,streamFile) != 0x06000000)
goto fail; goto fail;
loop_flag = (read_32bitLE(0x840,streamFile)!=0xFFFFFFFF); loop_flag = (read_32bitLE(0x840,streamFile)!=0xFFFFFFFF);
channel_count = 2; channel_count = 2;
//todo some files (ex. Batman Begins) have a subsong table at 0x800, not sure what signals it (flags at 0x04/0x14?)
/* 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;
@ -200,6 +201,8 @@ VGMSTREAM * init_vgmstream_musx_v006(STREAMFILE *streamFile) {
break; break;
case 0x47435F5F: /* GC__ */ case 0x47435F5F: /* GC__ */
start_offset = read_32bitBE(0x28,streamFile); start_offset = read_32bitBE(0x28,streamFile);
if (start_offset == 0 || start_offset == 0xABABABAB) goto fail; /* some are empty */
vgmstream->channels = channel_count; vgmstream->channels = channel_count;
vgmstream->sample_rate = 32000; vgmstream->sample_rate = 32000;
vgmstream->coding_type = coding_DAT4_IMA; vgmstream->coding_type = coding_DAT4_IMA;
@ -216,22 +219,9 @@ VGMSTREAM * init_vgmstream_musx_v006(STREAMFILE *streamFile) {
goto fail; goto fail;
} }
/* open the file for reading */
{
int i;
STREAMFILE * file;
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!file) goto fail;
for (i=0;i<channel_count;i++) {
vgmstream->ch[i].streamfile = file;
vgmstream->ch[i].channel_start_offset=
vgmstream->ch[i].offset=start_offset+
vgmstream->interleave_block_size*i;
}
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream; return vgmstream;
fail: fail:

View File

@ -280,22 +280,12 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) {
switch (comp_level) switch (comp_level)
{ {
case 0: case 0:
vgmstream->coding_type = coding_NWA0;
break;
case 1: case 1:
vgmstream->coding_type = coding_NWA1;
break;
case 2: case 2:
vgmstream->coding_type = coding_NWA2;
break;
case 3: case 3:
vgmstream->coding_type = coding_NWA3;
break;
case 4: case 4:
vgmstream->coding_type = coding_NWA4;
break;
case 5: case 5:
vgmstream->coding_type = coding_NWA5; vgmstream->coding_type = coding_NWA;
break; break;
default: default:
goto fail; goto fail;

View File

@ -193,8 +193,11 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
/* check extension */ /* check extension */
/* .ogg: standard/psychic, .logg: renamed for plugins, .adx: KID [Remember11 (PC)], .rof: The Rhythm of Fighters (Mobile) */ /* .ogg: standard/psychic, .logg: renamed for plugins,
if (check_extensions(streamFile,"ogg,logg,adx,rof")) { * .adx: KID [Remember11 (PC)],
* .rof: The Rhythm of Fighters (Mobile)
* .acm: Planescape Torment Enhanced Edition (PC) */
if (check_extensions(streamFile,"ogg,logg,adx,rof,acm")) {
is_ogg = 1; is_ogg = 1;
} else if (check_extensions(streamFile,"um3")) { } else if (check_extensions(streamFile,"um3")) {
is_um3 = 1; is_um3 = 1;

View File

@ -571,24 +571,10 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
#endif #endif
if (vgmstream->coding_type==coding_ACM) { if (vgmstream->coding_type==coding_ACM) {
mus_acm_codec_data *data = vgmstream->codec_data; reset_acm(vgmstream);
int i;
if (data) {
data->current_file = 0;
for (i=0;i<data->file_count;i++) {
acm_reset(data->files[i]);
}
}
} }
if ( if (vgmstream->coding_type == coding_NWA) {
vgmstream->coding_type == coding_NWA0 ||
vgmstream->coding_type == coding_NWA1 ||
vgmstream->coding_type == coding_NWA2 ||
vgmstream->coding_type == coding_NWA3 ||
vgmstream->coding_type == coding_NWA4 ||
vgmstream->coding_type == coding_NWA5
) {
nwa_codec_data *data = vgmstream->codec_data; nwa_codec_data *data = vgmstream->codec_data;
if (data) if (data)
reset_nwa(data->nwa); reset_nwa(data->nwa);
@ -764,39 +750,14 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
#endif #endif
if (vgmstream->coding_type==coding_ACM) { if (vgmstream->coding_type==coding_ACM) {
mus_acm_codec_data *data = (mus_acm_codec_data *) vgmstream->codec_data; free_acm(vgmstream->codec_data);
vgmstream->codec_data = NULL;
if (data) {
if (data->files) {
int i;
for (i=0; i<data->file_count; i++) {
/* shouldn't be duplicates */
if (data->files[i]) {
acm_close(data->files[i]);
data->files[i] = NULL;
}
}
free(data->files);
data->files = NULL;
}
free(vgmstream->codec_data);
vgmstream->codec_data = NULL;
}
} }
if ( if (vgmstream->coding_type == coding_NWA) {
vgmstream->coding_type == coding_NWA0 ||
vgmstream->coding_type == coding_NWA1 ||
vgmstream->coding_type == coding_NWA2 ||
vgmstream->coding_type == coding_NWA3 ||
vgmstream->coding_type == coding_NWA4 ||
vgmstream->coding_type == coding_NWA5
) {
nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data; nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data;
close_nwa(data->nwa); close_nwa(data->nwa);
free(data); free(data);
vgmstream->codec_data = NULL; vgmstream->codec_data = NULL;
} }
@ -967,10 +928,6 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
case layout_blocked_xvag_subsong: case layout_blocked_xvag_subsong:
render_vgmstream_blocked(buffer,sample_count,vgmstream); render_vgmstream_blocked(buffer,sample_count,vgmstream);
break; break;
case layout_acm:
case layout_mus_acm:
render_vgmstream_mus_acm(buffer,sample_count,vgmstream);
break;
case layout_aix: case layout_aix:
render_vgmstream_aix(buffer,sample_count,vgmstream); render_vgmstream_aix(buffer,sample_count,vgmstream);
break; break;
@ -1034,12 +991,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_SDX2_int: case coding_SDX2_int:
case coding_CBD2: case coding_CBD2:
case coding_ACM: case coding_ACM:
case coding_NWA0: case coding_NWA:
case coding_NWA1:
case coding_NWA2:
case coding_NWA3:
case coding_NWA4:
case coding_NWA5:
case coding_SASSC: case coding_SASSC:
return 1; return 1;
@ -1194,12 +1146,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
case coding_SDX2: case coding_SDX2:
case coding_SDX2_int: case coding_SDX2_int:
case coding_CBD2: case coding_CBD2:
case coding_NWA0: case coding_NWA:
case coding_NWA1:
case coding_NWA2:
case coding_NWA3:
case coding_NWA4:
case coding_NWA5:
case coding_SASSC: case coding_SASSC:
return 0x01; return 0x01;
@ -1820,14 +1767,11 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
break; break;
#endif #endif
case coding_ACM: case coding_ACM:
/* handled in its own layout, here to quiet compiler */ decode_acm(vgmstream->codec_data,
buffer+samples_written*vgmstream->channels,
samples_to_do, vgmstream->channels);
break; break;
case coding_NWA0: case coding_NWA:
case coding_NWA1:
case coding_NWA2:
case coding_NWA3:
case coding_NWA4:
case coding_NWA5:
decode_nwa(((nwa_codec_data*)vgmstream->codec_data)->nwa, decode_nwa(((nwa_codec_data*)vgmstream->codec_data)->nwa,
buffer+samples_written*vgmstream->channels, buffer+samples_written*vgmstream->channels,
samples_to_do samples_to_do
@ -2044,13 +1988,7 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
} }
#endif #endif
if (vgmstream->coding_type == coding_NWA0 || if (vgmstream->coding_type == coding_NWA) {
vgmstream->coding_type == coding_NWA1 ||
vgmstream->coding_type == coding_NWA2 ||
vgmstream->coding_type == coding_NWA3 ||
vgmstream->coding_type == coding_NWA4 ||
vgmstream->coding_type == coding_NWA5)
{
nwa_codec_data *data = vgmstream->codec_data; nwa_codec_data *data = vgmstream->codec_data;
if (data) if (data)
seek_nwa(data->nwa, vgmstream->loop_sample); seek_nwa(data->nwa, vgmstream->loop_sample);
@ -2439,12 +2377,25 @@ static int get_vgmstream_average_bitrate_channel_count(VGMSTREAM * vgmstream)
/* average bitrate helper */ /* average bitrate helper */
static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM * vgmstream, int channel) static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM * vgmstream, int channel)
{ {
//AAX, AIX, ACM? //AAX, AIX?
if (vgmstream->layout_type==layout_scd_int) { if (vgmstream->layout_type==layout_scd_int) {
scd_int_codec_data *data = (scd_int_codec_data *) vgmstream->codec_data; scd_int_codec_data *data = (scd_int_codec_data *) vgmstream->codec_data;
return data->intfiles[channel]; return data->intfiles[channel];
} }
if (vgmstream->coding_type==coding_NWA) {
nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data;
if (data && data->nwa)
return data->nwa->file;
}
if (vgmstream->coding_type==coding_ACM) {
acm_codec_data *data = (acm_codec_data *) vgmstream->codec_data;
if (data && data->file)
return data->file->streamfile;
}
#ifdef VGM_USE_VORBIS #ifdef VGM_USE_VORBIS
if (vgmstream->coding_type==coding_OGG_VORBIS) { if (vgmstream->coding_type==coding_OGG_VORBIS) {
ogg_vorbis_codec_data *data = (ogg_vorbis_codec_data *) vgmstream->codec_data; ogg_vorbis_codec_data *data = (ogg_vorbis_codec_data *) vgmstream->codec_data;

View File

@ -160,12 +160,7 @@ typedef enum {
coding_CBD2, /* CBD2 2:1 Cuberoot-Delta-Exact compression DPCM */ coding_CBD2, /* CBD2 2:1 Cuberoot-Delta-Exact compression DPCM */
coding_CBD2_int, /* CBD2 2:1 Cuberoot-Delta-Exact compression, with sample-level interleave */ coding_CBD2_int, /* CBD2 2:1 Cuberoot-Delta-Exact compression, with sample-level interleave */
coding_ACM, /* InterPlay ACM */ coding_ACM, /* InterPlay ACM */
coding_NWA0, /* Visual Art's NWA (compressed at various levels) */ coding_NWA, /* VisualArt's NWA */
coding_NWA1,
coding_NWA2,
coding_NWA3,
coding_NWA4,
coding_NWA5,
coding_EA_MT, /* Electronic Arts MicroTalk (linear-predictive speech codec) */ coding_EA_MT, /* Electronic Arts MicroTalk (linear-predictive speech codec) */
@ -255,8 +250,6 @@ typedef enum {
layout_blocked_xvag_subsong, /* XVAG subsongs [God of War III (PS4)] */ layout_blocked_xvag_subsong, /* XVAG subsongs [God of War III (PS4)] */
/* otherwise odd */ /* otherwise odd */
layout_acm, /* libacm layout */
layout_mus_acm, /* mus has multi-files to deal with */
layout_aix, /* CRI AIX's wheels within wheels */ layout_aix, /* CRI AIX's wheels within wheels */
layout_segmented, /* song divided in segments, each a complete VGMSTREAM */ layout_segmented, /* song divided in segments, each a complete VGMSTREAM */
layout_scd_int, /* deinterleave done by the SCDINTSTREAMFILE */ layout_scd_int, /* deinterleave done by the SCDINTSTREAMFILE */
@ -1043,20 +1036,10 @@ typedef struct {
} atrac9_codec_data; } atrac9_codec_data;
#endif #endif
/* with one file this is also used for just ACM */ /* libacm interface */
typedef struct { typedef struct {
int file_count; ACMStream *file;
int current_file; } acm_codec_data;
/* the index we return to upon loop completion */
int loop_start_file;
/* one after the index of the last file, typically
* will be equal to file_count */
int loop_end_file;
/* Upon exit from a loop, which file to play. */
/* -1 if there is no such file */
/*int end_file;*/
ACMStream **files;
} mus_acm_codec_data;
#define AIX_BUFFER_SIZE 0x1000 #define AIX_BUFFER_SIZE 0x1000
/* AIXery */ /* AIXery */