mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-28 08:20:54 +01:00
commit
ee26f6ca53
@ -205,9 +205,10 @@ are used in few games.
|
||||
- SDX2 2:1 Squareroot-Delta-Exact compression DPCM
|
||||
- CBD2 2:1 Cuberoot-Delta-Exact compression DPCM
|
||||
- InterPlay ACM
|
||||
- Visual Art's NWA
|
||||
- VisualArt's NWA
|
||||
- CRI HCA
|
||||
- Electronic Arts MicroTalk a.k.a. UTK or UMT
|
||||
- FMOD FADPCM 4-bit ADPCM
|
||||
- Xiph Vorbis (Ogg, FSB5, Wwise, OGL, Silicon Knights)
|
||||
- MPEG MP1/2/3 (standard, AHX, XVAG, FSB, AWC, P3D, etc)
|
||||
- ITU-T G.722.1 (Polycom Siren 7)
|
||||
|
@ -817,23 +817,65 @@ void acm_reset(ACMStream *acm)
|
||||
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;
|
||||
|
||||
while (samples_read < samples_to_do) {
|
||||
int32_t bytes_read_just_now;
|
||||
bytes_read_just_now =
|
||||
acm_read(acm,(char*)(
|
||||
outbuf+samples_read*channelspacing),
|
||||
(samples_to_do-samples_read)*sizeof(sample)*
|
||||
channelspacing,0,2,1);
|
||||
int32_t bytes_read_just_now = acm_read(
|
||||
acm,
|
||||
(char*)(outbuf+samples_read*channelspacing),
|
||||
(samples_to_do-samples_read)*sizeof(sample)*channelspacing,
|
||||
0,2,1);
|
||||
|
||||
if (bytes_read_just_now > 0) {
|
||||
samples_read +=
|
||||
bytes_read_just_now/sizeof(sample)/channelspacing;
|
||||
samples_read += bytes_read_just_now/sizeof(sample)/channelspacing;
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 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 apple_ima4_bytes_to_samples(size_t bytes, int channels);
|
||||
|
||||
/* ngc_dsp_decoder */
|
||||
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);
|
||||
|
||||
/* 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 */
|
||||
void decode_nwa(NWAData *nwa, sample *outbuf, int32_t samples_to_do);
|
||||
|
@ -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) */
|
||||
}
|
||||
|
||||
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) {
|
||||
int version, big_endian, header_samples;
|
||||
int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL;
|
||||
|
@ -197,6 +197,7 @@ static const char* extension_list[] = {
|
||||
"mpdsp",
|
||||
"mpds",
|
||||
"msa",
|
||||
"msd",
|
||||
"msf",
|
||||
"mss",
|
||||
"msvp",
|
||||
@ -509,12 +510,7 @@ static const coding_info coding_info_list[] = {
|
||||
{coding_CBD2, "Cuberoot-delta-exact (CBD2) 8-bit DPCM"},
|
||||
{coding_CBD2_int, "Cuberoot-delta-exact (CBD2) 8-bit DPCM with 1 byte interleave"},
|
||||
{coding_ACM, "InterPlay ACM"},
|
||||
{coding_NWA0, "NWA DPCM Level 0"},
|
||||
{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_NWA, "VisualArt's NWA DPCM"},
|
||||
|
||||
{coding_EA_MT, "Electronic Arts MicroTalk"},
|
||||
|
||||
@ -581,8 +577,6 @@ static const layout_info layout_info_list[] = {
|
||||
{layout_blocked_rws, "blocked (RWS)"},
|
||||
{layout_blocked_hwas, "blocked (HWAS)"},
|
||||
{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_segmented, "segmented"},
|
||||
{layout_scd_int, "SCD multistream interleave"},
|
||||
@ -669,9 +663,9 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_PS2_SVS, "Square SVS header"},
|
||||
{meta_RIFF_WAVE, "RIFF WAVE header"},
|
||||
{meta_RIFF_WAVE_POS, "RIFF WAVE header and .pos for looping"},
|
||||
{meta_NWA, "Visual Art's NWA header"},
|
||||
{meta_NWA_NWAINFOINI, "Visual Art's NWA header and NWAINFO.INI for looping"},
|
||||
{meta_NWA_GAMEEXEINI, "Visual Art's NWA header and Gameexe.ini for looping"},
|
||||
{meta_NWA, "VisualArt's NWA header"},
|
||||
{meta_NWA_NWAINFOINI, "VisualArt's NWA header (NWAINFO.INI looping)"},
|
||||
{meta_NWA_GAMEEXEINI, "VisualArt's NWA header (Gameexe.ini looping)"},
|
||||
{meta_XSS, "Dino Crisis 3 XSS File"},
|
||||
{meta_HGC1, "Knights of the Temple 2 hgC1 Header"},
|
||||
{meta_AUS, "Capcom AUS Header"},
|
||||
@ -701,7 +695,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_BG00, "Falcom BG00 Header"},
|
||||
{meta_PS2_RSTM, "Rockstar Games RSTM 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_DXH, "Tokobot Plus DXH 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_SCK_DSP, "The Scorpion King SCK Header"},
|
||||
{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_SAB, "Team17 SAB 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_OGG_L2SD, "Ogg Vorbis (L2SD)"},
|
||||
{meta_WAF, "KID WAF header"},
|
||||
{meta_WAVE, "WayForward .WAVE header"},
|
||||
{meta_WAVE_segmented, "WayForward .WAVE header (segmented)"},
|
||||
{meta_WAVE, "EngineBlack .WAVE header"},
|
||||
{meta_WAVE_segmented, "EngineBlack .WAVE header (segmented)"},
|
||||
|
||||
#ifdef VGM_USE_MP4V2
|
||||
{meta_MP4, "AAC header"},
|
||||
|
@ -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_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_segmented(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -1758,10 +1758,6 @@
|
||||
RelativePath=".\layout\blocked_ivaud.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\layout\mus_acm_layout.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\layout\mxch_blocked.c"
|
||||
>
|
||||
|
@ -488,7 +488,6 @@
|
||||
<ClCompile Include="layout\ims_block.c" />
|
||||
<ClCompile Include="layout\interleave.c" />
|
||||
<ClCompile Include="layout\blocked_ivaud.c" />
|
||||
<ClCompile Include="layout\mus_acm_layout.c" />
|
||||
<ClCompile Include="layout\mxch_blocked.c" />
|
||||
<ClCompile Include="layout\nolayout.c" />
|
||||
<ClCompile Include="layout\blocked_adm.c" />
|
||||
|
@ -1042,9 +1042,6 @@
|
||||
<ClCompile Include="layout\blocked_ivaud.c">
|
||||
<Filter>layout\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="layout\mus_acm_layout.c">
|
||||
<Filter>layout\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="layout\mxch_blocked.c">
|
||||
<Filter>layout\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -1,66 +1,49 @@
|
||||
#include "../vgmstream.h"
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../coding/acm_decoder.h"
|
||||
|
||||
/* InterPlay ACM */
|
||||
/* The real work is done by libacm */
|
||||
/* ACM - InterPlay infinity engine games [Planescape: Torment (PC), Baldur's Gate (PC)] */
|
||||
VGMSTREAM * init_vgmstream_acm(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
ACMStream *acm_stream = NULL;
|
||||
mus_acm_codec_data *data;
|
||||
int loop_flag = 0, channel_count, sample_rate, num_samples;
|
||||
acm_codec_data *data = NULL;
|
||||
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
int loop_flag = 0;
|
||||
int channel_count;
|
||||
|
||||
/* 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;
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "acm"))
|
||||
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) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
channel_count = acm_stream->info.channels;
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = acm_stream->info.rate;
|
||||
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;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples;
|
||||
|
||||
data->file_count = 1;
|
||||
data->current_file = 0;
|
||||
data->files[0] = acm_stream;
|
||||
/*data->end_file = -1;*/
|
||||
vgmstream->meta_type = meta_ACM;
|
||||
vgmstream->coding_type = coding_ACM;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
vgmstream->codec_data = data;
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
free_acm(data);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1,170 +1,160 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* Apple Core Audio Format */
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* Apple Core Audio Format File - from iOS games [Vectros (iOS), Ridge Racer Accelerated (iOS)] */
|
||||
VGMSTREAM * init_vgmstream_apple_caff(STREAMFILE *streamFile) {
|
||||
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;
|
||||
off_t data_size = 0;
|
||||
off_t sample_count = 0;
|
||||
off_t interleave = 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;
|
||||
int found_desc = 0 /*, found_pakt = 0*/, found_data = 0;
|
||||
uint32_t codec = 0 /*, codec_flags = 0*/;
|
||||
uint32_t bytes_per_packet = 0, samples_per_packet = 0, channels_per_packet = 0, bits_per_sample = 0;
|
||||
int valid_samples = 0 /*, priming_samples = 0, unused_samples = 0*/;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("caf",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check "caff" id */
|
||||
if (read_32bitBE(0,streamFile)!=0x63616666) goto fail;
|
||||
/* check version, flags */
|
||||
if (read_32bitBE(4,streamFile)!=0x00010000) goto fail;
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "caf"))
|
||||
goto fail;
|
||||
|
||||
file_length = (off_t)get_streamfile_size(streamFile);
|
||||
if (read_32bitBE(0x00,streamFile) != 0x63616666) /* "caff" */
|
||||
goto fail;
|
||||
if (read_32bitBE(0x04,streamFile) != 0x00010000) /* version/flags */
|
||||
goto fail;
|
||||
|
||||
while (chunk_offset < file_length)
|
||||
{
|
||||
/* high half of size (expect 0s) */
|
||||
if (read_32bitBE(chunk_offset+4,streamFile) != 0) goto fail;
|
||||
file_size = get_streamfile_size(streamFile);
|
||||
chunk_offset = 0x08;
|
||||
|
||||
/* handle chunk type */
|
||||
switch (read_32bitBE(chunk_offset,streamFile))
|
||||
{
|
||||
case 0x64657363: /* desc */
|
||||
while (chunk_offset < file_size) {
|
||||
uint32_t chunk_type = read_32bitBE(chunk_offset+0x00,streamFile);
|
||||
uint32_t chunk_size = (uint32_t)read_64bitBE(chunk_offset+0x04,streamFile);
|
||||
chunk_offset += 0x0c;
|
||||
|
||||
switch (chunk_type) {
|
||||
|
||||
case 0x64657363: /* "desc" */
|
||||
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;
|
||||
uint32_t codec_4cc = read_32bitBE(chunk_offset+0x14, streamFile);
|
||||
/* only supporting ima4 for now */
|
||||
if (codec_4cc != 0x696d6134) goto fail;
|
||||
uint64_t sample_long = (uint64_t)read_64bitBE(chunk_offset+0x00, streamFile);
|
||||
double* sample_double; /* double sample rate, double the fun */
|
||||
|
||||
/* format flags */
|
||||
if (read_32bitBE(chunk_offset+0x18, streamFile) != 0) goto fail;
|
||||
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;
|
||||
sample_double = (double*)&sample_long;
|
||||
sample_rate = (int)(*sample_double);
|
||||
}
|
||||
|
||||
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;
|
||||
case 0x70616b74: /* pakt */
|
||||
found_pakt = 1;
|
||||
/* 64-bit packet table size, 0 for constant bitrate */
|
||||
if (
|
||||
read_32bitBE(chunk_offset+0x0c,streamFile) != 0 ||
|
||||
read_32bitBE(chunk_offset+0x10,streamFile) != 0) goto fail;
|
||||
/* high half of valid frames count */
|
||||
if (read_32bitBE(chunk_offset+0x14,streamFile) != 0) goto fail;
|
||||
/* 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);
|
||||
|
||||
case 0x70616b74: /* "pakt" */
|
||||
//found_pakt = 1;
|
||||
|
||||
//packets_table_size = (uint32_t)read_64bitBE(chunk_offset+0x00,streamFile); /* 0 for constant bitrate */
|
||||
valid_samples = (uint32_t)read_64bitBE(chunk_offset+0x08,streamFile);
|
||||
//priming_samples = read_32bitBE(chunk_offset+0x10,streamFile); /* encoder delay samples */
|
||||
//unused_samples = read_32bitBE(chunk_offset+0x14,streamFile); /* footer samples */
|
||||
break;
|
||||
case 0x66726565: /* free */
|
||||
/* padding, ignore */
|
||||
break;
|
||||
case 0x64617461: /* data */
|
||||
if (read_32bitBE(chunk_offset+12,streamFile) != 1) goto fail;
|
||||
|
||||
case 0x64617461: /* "data" */
|
||||
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;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* 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 (start_offset == 0 || data_size == 0 || sample_count == 0 ||
|
||||
sample_rate == -1 || channel_count == 0) goto fail;
|
||||
if (!found_desc || !found_data)
|
||||
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;
|
||||
|
||||
vgmstream->channels = channel_count;
|
||||
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;
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<channel_count;i++)
|
||||
{
|
||||
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||
switch(codec) {
|
||||
case 0x6C70636D: /* "lpcm" */
|
||||
vgmstream->num_samples = valid_samples;
|
||||
if (!vgmstream->num_samples)
|
||||
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bits_per_sample);
|
||||
|
||||
vgmstream->ch[i].offset =
|
||||
vgmstream->ch[i].channel_start_offset =
|
||||
start_offset + interleave * i;
|
||||
}
|
||||
//todo check codec_flags for BE/LE, signed/etc
|
||||
if (bits_per_sample == 8) {
|
||||
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;
|
||||
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -181,8 +181,9 @@ VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE *streamFile) {
|
||||
|
||||
|
||||
/* check extension, case insensitive */
|
||||
/* .bin: Final Fantasy Dimensions (iOS), Final Fantasy V (iOS) */
|
||||
if (!check_extensions(streamFile,"mp4,m4a,m4v,lmp4,bin"))
|
||||
/* .bin: Final Fantasy Dimensions (iOS), Final Fantasy V (iOS)
|
||||
* .msd: UNO (iOS) */
|
||||
if (!check_extensions(streamFile,"mp4,m4a,m4v,lmp4,bin,msd"))
|
||||
goto fail;
|
||||
|
||||
filesize = streamFile->get_size(streamFile);
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include "../vgmstream.h"
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
#include "../streamfile.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../coding/acm_decoder.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -13,11 +12,107 @@
|
||||
#define DIRSEP '/'
|
||||
#endif
|
||||
|
||||
#define NAME_LENGTH PATH_LIMIT
|
||||
|
||||
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
|
||||
|
||||
static int exists(char *filename, STREAMFILE *streamfile) {
|
||||
STREAMFILE * temp =
|
||||
streamfile->open(streamfile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
STREAMFILE * temp = streamfile->open(streamfile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!temp) return 0;
|
||||
|
||||
close_streamfile(temp);
|
||||
@ -91,57 +186,40 @@ fail:
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* MUS playlist for InterPlay ACM */
|
||||
VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
ACMStream *acm_stream = NULL;
|
||||
mus_acm_codec_data *data = NULL;
|
||||
static char** parse_mus(STREAMFILE *streamFile, int *out_file_count, int *out_loop_flag, int *out_loop_start_index, int *out_loop_end_index) {
|
||||
char** names = NULL;
|
||||
|
||||
char filename[NAME_LENGTH];
|
||||
char line_buffer[NAME_LENGTH];
|
||||
char * end_ptr;
|
||||
char name_base[NAME_LENGTH];
|
||||
char (*names)[NAME_LENGTH] = NULL;
|
||||
char dir_name[NAME_LENGTH];
|
||||
char subdir_name[NAME_LENGTH];
|
||||
|
||||
int i;
|
||||
int loop_flag = 0;
|
||||
int channel_count;
|
||||
int file_count;
|
||||
size_t line_bytes;
|
||||
int whole_line_read = 0;
|
||||
off_t mus_offset = 0;
|
||||
|
||||
int loop_end_index = -1;
|
||||
int loop_start_index = -1;
|
||||
int32_t loop_start_samples = -1;
|
||||
int32_t loop_end_samples = -1;
|
||||
int i;
|
||||
int loop_flag = 0, loop_start_index = -1, loop_end_index = -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 */
|
||||
line_bytes = get_streamfile_text_line(sizeof(line_buffer),line_buffer,
|
||||
mus_offset, streamFile, &whole_line_read);
|
||||
line_bytes = get_streamfile_text_line(sizeof(line_buffer),line_buffer, mus_offset, streamFile, &whole_line_read);
|
||||
if (!whole_line_read) goto fail;
|
||||
mus_offset += line_bytes;
|
||||
memcpy(name_base,line_buffer,sizeof(name_base));
|
||||
|
||||
/* uppercase name_base */
|
||||
{
|
||||
int i;
|
||||
for (i=0;name_base[i];i++) name_base[i]=toupper(name_base[i]);
|
||||
int j;
|
||||
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 */
|
||||
line_bytes = get_streamfile_text_line(sizeof(line_buffer),line_buffer,
|
||||
mus_offset, streamFile, &whole_line_read);
|
||||
line_bytes = get_streamfile_text_line(sizeof(line_buffer),line_buffer, mus_offset, streamFile, &whole_line_read);
|
||||
if (!whole_line_read) goto fail;
|
||||
if (line_buffer[0] == '\0') goto fail;
|
||||
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) */
|
||||
if (*end_ptr != '\0') goto fail;
|
||||
|
||||
/*printf("entries: %d\n",file_count);*/
|
||||
|
||||
names = calloc(file_count,sizeof(names[0]));
|
||||
/* set names */
|
||||
names = calloc(file_count,sizeof(char*)); /* array of strings (size NAME_LENGTH) */
|
||||
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';
|
||||
streamFile->get_name(streamFile,filename,sizeof(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;
|
||||
last_slash = strrchr(dir_name,DIRSEP);
|
||||
char * last_slash = strrchr(dir_name,DIRSEP);
|
||||
if (last_slash != NULL) {
|
||||
/* trim off the file name */
|
||||
last_slash[1]='\0';
|
||||
last_slash[1]='\0'; /* trim off the file name */
|
||||
} else {
|
||||
/* no dir name? annihilate! */
|
||||
dir_name[0] = '\0';
|
||||
dir_name[0] = '\0'; /* no dir name? annihilate! */
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,7 +260,8 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
|
||||
char loop_name_temp[NAME_LENGTH];
|
||||
char loop_name_base[NAME_LENGTH];
|
||||
char loop_name[NAME_LENGTH];
|
||||
for (i=0;i<file_count;i++)
|
||||
|
||||
for (i = 0; i < file_count; i++)
|
||||
{
|
||||
int fields_matched;
|
||||
line_bytes =
|
||||
@ -192,7 +273,6 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
|
||||
fields_matched = sscanf(line_buffer,"%s %s %s",name,
|
||||
loop_name_base_temp,loop_name_temp);
|
||||
|
||||
|
||||
if (fields_matched < 1) goto fail;
|
||||
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 */
|
||||
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 */
|
||||
names[i][0] = '\0';
|
||||
concatn(sizeof(names[0]),names[i],dir_name);
|
||||
concatn(sizeof(names[0]),names[i],name);
|
||||
concatn(sizeof(names[0]),names[i],".ACM");
|
||||
concatn(NAME_LENGTH,names[i],dir_name);
|
||||
concatn(NAME_LENGTH,names[i],name);
|
||||
concatn(NAME_LENGTH,names[i],".ACM");
|
||||
|
||||
if (!exists(names[i],streamFile)) {
|
||||
|
||||
@ -242,16 +323,14 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
names[i][0] = '\0';
|
||||
concatn(sizeof(names[0]),names[i],dir_name);
|
||||
concatn(sizeof(names[0]),names[i],subdir_name);
|
||||
concatn(sizeof(names[0]),names[i],name_base);
|
||||
concatn(sizeof(names[0]),names[i],name);
|
||||
concatn(sizeof(names[0]),names[i],".ACM");
|
||||
concatn(NAME_LENGTH,names[i],dir_name);
|
||||
concatn(NAME_LENGTH,names[i],subdir_name);
|
||||
concatn(NAME_LENGTH,names[i],name_base);
|
||||
concatn(NAME_LENGTH,names[i],name);
|
||||
concatn(NAME_LENGTH,names[i],".ACM");
|
||||
|
||||
if (!exists(names[i],streamFile)) goto fail;
|
||||
}
|
||||
|
||||
/*printf("%2d %s\n",i,names[i]);*/
|
||||
}
|
||||
|
||||
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);
|
||||
concatn(sizeof(target_name),target_name,".ACM");
|
||||
/*printf("looking for loop %s\n",target_name);*/
|
||||
|
||||
for (i=0;i<file_count;i++) {
|
||||
if (!strcmp(target_name,names[i]))
|
||||
{
|
||||
if (!strcmp(target_name,names[i])) {
|
||||
loop_start_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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++;*/
|
||||
loop_end_index++;
|
||||
loop_flag = 1;
|
||||
@ -283,78 +359,25 @@ VGMSTREAM * init_vgmstream_mus_acm(STREAMFILE *streamFile) {
|
||||
}
|
||||
}
|
||||
|
||||
/* set up the struct to track the files */
|
||||
data = calloc(1,sizeof(mus_acm_codec_data));
|
||||
if (!data) goto fail;
|
||||
*out_loop_start_index = loop_start_index;
|
||||
*out_loop_end_index = loop_end_index;
|
||||
*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... */
|
||||
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 */
|
||||
return names;
|
||||
fail:
|
||||
if (data) {
|
||||
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);
|
||||
clean_mus(names, file_count);
|
||||
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);
|
||||
}
|
||||
|
@ -88,7 +88,8 @@ VGMSTREAM * init_vgmstream_musx_v005(STREAMFILE *streamFile) {
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(streamFile, "musx"))
|
||||
/* .sfx: Batman Begins, .musx: header id */
|
||||
if (!check_extensions(streamFile, "musx,sfx"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,streamFile) != 0x4D555358) /* "MUSX" */
|
||||
goto fail;
|
||||
@ -158,27 +159,27 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
/* MUSX (Version 006) */
|
||||
/* MUSX (Version 006) [Batman Begins (GC)] */
|
||||
VGMSTREAM * init_vgmstream_musx_v006(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
int loop_flag;
|
||||
int channel_count;
|
||||
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" */
|
||||
goto fail;
|
||||
if (read_32bitBE(0x08,streamFile) != 0x06000000) /* "0x06000000" */
|
||||
if (read_32bitBE(0x08,streamFile) != 0x06000000)
|
||||
goto fail;
|
||||
|
||||
loop_flag = (read_32bitLE(0x840,streamFile)!=0xFFFFFFFF);
|
||||
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 */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
@ -200,6 +201,8 @@ VGMSTREAM * init_vgmstream_musx_v006(STREAMFILE *streamFile) {
|
||||
break;
|
||||
case 0x47435F5F: /* GC__ */
|
||||
start_offset = read_32bitBE(0x28,streamFile);
|
||||
if (start_offset == 0 || start_offset == 0xABABABAB) goto fail; /* some are empty */
|
||||
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = 32000;
|
||||
vgmstream->coding_type = coding_DAT4_IMA;
|
||||
@ -216,22 +219,9 @@ VGMSTREAM * init_vgmstream_musx_v006(STREAMFILE *streamFile) {
|
||||
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;
|
||||
|
||||
fail:
|
||||
|
@ -280,22 +280,12 @@ VGMSTREAM * init_vgmstream_nwa(STREAMFILE *streamFile) {
|
||||
switch (comp_level)
|
||||
{
|
||||
case 0:
|
||||
vgmstream->coding_type = coding_NWA0;
|
||||
break;
|
||||
case 1:
|
||||
vgmstream->coding_type = coding_NWA1;
|
||||
break;
|
||||
case 2:
|
||||
vgmstream->coding_type = coding_NWA2;
|
||||
break;
|
||||
case 3:
|
||||
vgmstream->coding_type = coding_NWA3;
|
||||
break;
|
||||
case 4:
|
||||
vgmstream->coding_type = coding_NWA4;
|
||||
break;
|
||||
case 5:
|
||||
vgmstream->coding_type = coding_NWA5;
|
||||
vgmstream->coding_type = coding_NWA;
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
|
@ -193,8 +193,11 @@ VGMSTREAM * init_vgmstream_ogg_vorbis(STREAMFILE *streamFile) {
|
||||
|
||||
|
||||
/* check extension */
|
||||
/* .ogg: standard/psychic, .logg: renamed for plugins, .adx: KID [Remember11 (PC)], .rof: The Rhythm of Fighters (Mobile) */
|
||||
if (check_extensions(streamFile,"ogg,logg,adx,rof")) {
|
||||
/* .ogg: standard/psychic, .logg: renamed for plugins,
|
||||
* .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;
|
||||
} else if (check_extensions(streamFile,"um3")) {
|
||||
is_um3 = 1;
|
||||
|
101
src/vgmstream.c
101
src/vgmstream.c
@ -571,24 +571,10 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
|
||||
#endif
|
||||
|
||||
if (vgmstream->coding_type==coding_ACM) {
|
||||
mus_acm_codec_data *data = vgmstream->codec_data;
|
||||
int i;
|
||||
if (data) {
|
||||
data->current_file = 0;
|
||||
for (i=0;i<data->file_count;i++) {
|
||||
acm_reset(data->files[i]);
|
||||
}
|
||||
}
|
||||
reset_acm(vgmstream);
|
||||
}
|
||||
|
||||
if (
|
||||
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
|
||||
) {
|
||||
if (vgmstream->coding_type == coding_NWA) {
|
||||
nwa_codec_data *data = vgmstream->codec_data;
|
||||
if (data)
|
||||
reset_nwa(data->nwa);
|
||||
@ -764,39 +750,14 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
|
||||
#endif
|
||||
|
||||
if (vgmstream->coding_type==coding_ACM) {
|
||||
mus_acm_codec_data *data = (mus_acm_codec_data *) vgmstream->codec_data;
|
||||
|
||||
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;
|
||||
}
|
||||
free_acm(vgmstream->codec_data);
|
||||
vgmstream->codec_data = NULL;
|
||||
}
|
||||
|
||||
if (
|
||||
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
|
||||
) {
|
||||
if (vgmstream->coding_type == coding_NWA) {
|
||||
nwa_codec_data *data = (nwa_codec_data *) vgmstream->codec_data;
|
||||
close_nwa(data->nwa);
|
||||
free(data);
|
||||
|
||||
vgmstream->codec_data = NULL;
|
||||
}
|
||||
|
||||
@ -967,10 +928,6 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
|
||||
case layout_blocked_xvag_subsong:
|
||||
render_vgmstream_blocked(buffer,sample_count,vgmstream);
|
||||
break;
|
||||
case layout_acm:
|
||||
case layout_mus_acm:
|
||||
render_vgmstream_mus_acm(buffer,sample_count,vgmstream);
|
||||
break;
|
||||
case layout_aix:
|
||||
render_vgmstream_aix(buffer,sample_count,vgmstream);
|
||||
break;
|
||||
@ -1034,12 +991,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
|
||||
case coding_SDX2_int:
|
||||
case coding_CBD2:
|
||||
case coding_ACM:
|
||||
case coding_NWA0:
|
||||
case coding_NWA1:
|
||||
case coding_NWA2:
|
||||
case coding_NWA3:
|
||||
case coding_NWA4:
|
||||
case coding_NWA5:
|
||||
case coding_NWA:
|
||||
case coding_SASSC:
|
||||
return 1;
|
||||
|
||||
@ -1194,12 +1146,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
|
||||
case coding_SDX2:
|
||||
case coding_SDX2_int:
|
||||
case coding_CBD2:
|
||||
case coding_NWA0:
|
||||
case coding_NWA1:
|
||||
case coding_NWA2:
|
||||
case coding_NWA3:
|
||||
case coding_NWA4:
|
||||
case coding_NWA5:
|
||||
case coding_NWA:
|
||||
case coding_SASSC:
|
||||
return 0x01;
|
||||
|
||||
@ -1820,14 +1767,11 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
|
||||
break;
|
||||
#endif
|
||||
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;
|
||||
case coding_NWA0:
|
||||
case coding_NWA1:
|
||||
case coding_NWA2:
|
||||
case coding_NWA3:
|
||||
case coding_NWA4:
|
||||
case coding_NWA5:
|
||||
case coding_NWA:
|
||||
decode_nwa(((nwa_codec_data*)vgmstream->codec_data)->nwa,
|
||||
buffer+samples_written*vgmstream->channels,
|
||||
samples_to_do
|
||||
@ -2044,13 +1988,7 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (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)
|
||||
{
|
||||
if (vgmstream->coding_type == coding_NWA) {
|
||||
nwa_codec_data *data = vgmstream->codec_data;
|
||||
if (data)
|
||||
seek_nwa(data->nwa, vgmstream->loop_sample);
|
||||
@ -2439,12 +2377,25 @@ static int get_vgmstream_average_bitrate_channel_count(VGMSTREAM * vgmstream)
|
||||
/* average bitrate helper */
|
||||
static STREAMFILE * get_vgmstream_average_bitrate_channel_streamfile(VGMSTREAM * vgmstream, int channel)
|
||||
{
|
||||
//AAX, AIX, ACM?
|
||||
//AAX, AIX?
|
||||
|
||||
if (vgmstream->layout_type==layout_scd_int) {
|
||||
scd_int_codec_data *data = (scd_int_codec_data *) vgmstream->codec_data;
|
||||
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
|
||||
if (vgmstream->coding_type==coding_OGG_VORBIS) {
|
||||
ogg_vorbis_codec_data *data = (ogg_vorbis_codec_data *) vgmstream->codec_data;
|
||||
|
@ -160,12 +160,7 @@ typedef enum {
|
||||
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_ACM, /* InterPlay ACM */
|
||||
coding_NWA0, /* Visual Art's NWA (compressed at various levels) */
|
||||
coding_NWA1,
|
||||
coding_NWA2,
|
||||
coding_NWA3,
|
||||
coding_NWA4,
|
||||
coding_NWA5,
|
||||
coding_NWA, /* VisualArt's NWA */
|
||||
|
||||
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)] */
|
||||
|
||||
/* 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_segmented, /* song divided in segments, each a complete VGMSTREAM */
|
||||
layout_scd_int, /* deinterleave done by the SCDINTSTREAMFILE */
|
||||
@ -1043,20 +1036,10 @@ typedef struct {
|
||||
} atrac9_codec_data;
|
||||
#endif
|
||||
|
||||
/* with one file this is also used for just ACM */
|
||||
/* libacm interface */
|
||||
typedef struct {
|
||||
int file_count;
|
||||
int current_file;
|
||||
/* 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;
|
||||
ACMStream *file;
|
||||
} acm_codec_data;
|
||||
|
||||
#define AIX_BUFFER_SIZE 0x1000
|
||||
/* AIXery */
|
||||
|
Loading…
Reference in New Issue
Block a user