2009-01-03 12:08:44 +01:00
|
|
|
#include "meta.h"
|
2018-03-10 12:19:30 +01:00
|
|
|
#include "../layout/layout.h"
|
|
|
|
#include "../coding/coding.h"
|
2018-03-10 00:27:06 +01:00
|
|
|
#include "aax_utf.h"
|
2009-01-03 12:08:44 +01:00
|
|
|
|
2018-03-10 00:27:06 +01:00
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
#define MAX_SEGMENTS 2 /* usually segment0=intro, segment1=loop/main */
|
|
|
|
|
|
|
|
/* AAX - segmented ADX [Bayonetta (PS3), Pandora's Tower (Wii), Catherine (X360), Binary Domain (PS3)] */
|
2009-01-03 12:08:44 +01:00
|
|
|
VGMSTREAM * init_vgmstream_aax(STREAMFILE *streamFile) {
|
2017-12-06 12:15:27 +01:00
|
|
|
VGMSTREAM * vgmstream = NULL;
|
2018-03-10 17:33:48 +01:00
|
|
|
int is_hca;
|
2009-01-03 12:08:44 +01:00
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
int loop_flag = 0, channel_count = 0;
|
|
|
|
int32_t sample_count, loop_start_sample = 0, loop_end_sample = 0;
|
2018-08-26 00:59:31 +02:00
|
|
|
int segment_count;
|
2009-01-03 12:08:44 +01:00
|
|
|
|
2018-03-10 20:25:57 +01:00
|
|
|
segmented_layout_data *data = NULL;
|
2018-03-10 12:19:30 +01:00
|
|
|
int table_error = 0;
|
|
|
|
const long top_offset = 0x00;
|
|
|
|
off_t segment_offset[MAX_SEGMENTS];
|
|
|
|
size_t segment_size[MAX_SEGMENTS];
|
2017-12-06 12:15:27 +01:00
|
|
|
int i;
|
2009-01-03 12:08:44 +01:00
|
|
|
|
2009-01-03 14:16:36 +01:00
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
/* checks */
|
|
|
|
if (!check_extensions(streamFile, "aax"))
|
|
|
|
goto fail;
|
|
|
|
if (read_32bitBE(0x00,streamFile) != 0x40555446) /* "@UTF" */
|
|
|
|
goto fail;
|
2009-01-03 14:16:36 +01:00
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
|
|
|
|
/* get segment count, offsets and sizes */
|
2009-01-03 14:16:36 +01:00
|
|
|
{
|
|
|
|
struct utf_query_result result;
|
|
|
|
long aax_string_table_offset;
|
|
|
|
long aax_string_table_size;
|
2018-03-10 12:19:30 +01:00
|
|
|
long aax_data_offset;
|
|
|
|
|
|
|
|
result = query_utf_nofail(streamFile, top_offset, NULL, &table_error);
|
2009-01-03 14:16:36 +01:00
|
|
|
if (table_error) goto fail;
|
2018-03-10 12:19:30 +01:00
|
|
|
|
2009-01-03 14:16:36 +01:00
|
|
|
segment_count = result.rows;
|
2018-03-10 12:19:30 +01:00
|
|
|
if (segment_count > MAX_SEGMENTS) goto fail;
|
|
|
|
|
|
|
|
aax_string_table_offset = top_offset+0x08 + result.string_table_offset;
|
|
|
|
aax_data_offset = top_offset+0x08 + result.data_offset;
|
2009-01-03 14:16:36 +01:00
|
|
|
aax_string_table_size = aax_data_offset - aax_string_table_offset;
|
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
if (result.name_offset+0x04 > aax_string_table_size)
|
|
|
|
goto fail;
|
2018-03-10 17:33:48 +01:00
|
|
|
|
|
|
|
if (read_32bitBE(aax_string_table_offset + result.name_offset, streamFile) == 0x41415800) /* "AAX\0" */
|
|
|
|
is_hca = 0;
|
|
|
|
else if (read_32bitBE(aax_string_table_offset + result.name_offset, streamFile) == 0x48434100) /* "HCA\0" */
|
|
|
|
is_hca = 1;
|
|
|
|
else
|
2009-01-03 14:16:36 +01:00
|
|
|
goto fail;
|
2009-01-03 12:08:44 +01:00
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
/* get offsets of constituent segments */
|
|
|
|
for (i = 0; i < segment_count; i++) {
|
|
|
|
struct offset_size_pair offset_size;
|
2009-01-03 12:08:44 +01:00
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
offset_size = query_utf_data(streamFile, top_offset, i, "data", &table_error);
|
|
|
|
if (table_error) goto fail;
|
|
|
|
|
|
|
|
segment_offset[i] = aax_data_offset + offset_size.offset;
|
|
|
|
segment_size[i] = offset_size.size;
|
|
|
|
}
|
2009-01-03 12:08:44 +01:00
|
|
|
}
|
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
/* init layout */
|
2018-03-10 20:25:57 +01:00
|
|
|
data = init_layout_segmented(segment_count);
|
2018-03-10 12:19:30 +01:00
|
|
|
if (!data) goto fail;
|
|
|
|
|
|
|
|
/* open each segment subfile */
|
|
|
|
for (i = 0; i < segment_count; i++) {
|
2018-10-27 23:49:08 +02:00
|
|
|
STREAMFILE* temp_streamFile = setup_subfile_streamfile(streamFile, segment_offset[i],segment_size[i], (is_hca ? "hca" : "adx"));
|
2018-03-10 12:19:30 +01:00
|
|
|
if (!temp_streamFile) goto fail;
|
2009-01-03 12:08:44 +01:00
|
|
|
|
2018-03-10 17:33:48 +01:00
|
|
|
data->segments[i] = is_hca ?
|
|
|
|
init_vgmstream_hca(temp_streamFile) :
|
|
|
|
init_vgmstream_adx(temp_streamFile);
|
2009-01-03 12:08:44 +01:00
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
close_streamfile(temp_streamFile);
|
|
|
|
|
2018-03-11 19:43:26 +01:00
|
|
|
if (!data->segments[i]) goto fail;
|
2009-01-03 12:08:44 +01:00
|
|
|
}
|
|
|
|
|
2018-03-11 19:43:26 +01:00
|
|
|
/* setup segmented VGMSTREAMs */
|
|
|
|
if (!setup_layout_segmented(data))
|
|
|
|
goto fail;
|
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
/* get looping and samples */
|
2009-01-03 12:08:44 +01:00
|
|
|
sample_count = 0;
|
2009-01-03 14:16:36 +01:00
|
|
|
loop_flag = 0;
|
2018-03-10 12:19:30 +01:00
|
|
|
for (i = 0; i < segment_count; i++) {
|
|
|
|
int segment_loop_flag = query_utf_1byte(streamFile, top_offset, i, "lpflg", &table_error);
|
2009-01-03 14:16:36 +01:00
|
|
|
if (table_error) segment_loop_flag = 0;
|
2009-01-03 12:08:44 +01:00
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
if (!loop_flag && segment_loop_flag) {
|
2009-01-03 12:08:44 +01:00
|
|
|
loop_start_sample = sample_count;
|
2009-01-03 14:16:36 +01:00
|
|
|
}
|
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
sample_count += data->segments[i]->num_samples;
|
2009-01-03 14:16:36 +01:00
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
if (!loop_flag && segment_loop_flag) {
|
2009-01-03 12:08:44 +01:00
|
|
|
loop_end_sample = sample_count;
|
2009-01-03 14:16:36 +01:00
|
|
|
loop_flag = 1;
|
|
|
|
}
|
2009-01-03 12:08:44 +01:00
|
|
|
}
|
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
|
|
|
|
channel_count = data->segments[0]->channels;
|
|
|
|
|
|
|
|
/* build the VGMSTREAM */
|
2009-01-03 12:08:44 +01:00
|
|
|
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
2018-03-10 12:19:30 +01:00
|
|
|
if (!vgmstream) goto fail;
|
2009-01-03 12:08:44 +01:00
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
vgmstream->sample_rate = data->segments[0]->sample_rate;
|
2009-01-03 12:08:44 +01:00
|
|
|
vgmstream->num_samples = sample_count;
|
|
|
|
vgmstream->loop_start_sample = loop_start_sample;
|
|
|
|
vgmstream->loop_end_sample = loop_end_sample;
|
|
|
|
|
|
|
|
vgmstream->meta_type = meta_AAX;
|
2018-03-10 12:19:30 +01:00
|
|
|
vgmstream->coding_type = data->segments[0]->coding_type;
|
2018-03-10 20:25:57 +01:00
|
|
|
vgmstream->layout_type = layout_segmented;
|
2009-01-03 12:08:44 +01:00
|
|
|
|
2018-03-10 17:33:48 +01:00
|
|
|
vgmstream->layout_data = data;
|
2009-01-03 12:08:44 +01:00
|
|
|
|
|
|
|
return vgmstream;
|
|
|
|
|
|
|
|
fail:
|
2018-03-10 12:19:30 +01:00
|
|
|
close_vgmstream(vgmstream);
|
2018-03-10 20:25:57 +01:00
|
|
|
free_layout_segmented(data);
|
2009-01-03 12:08:44 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
2009-01-03 14:16:36 +01:00
|
|
|
|
2011-01-04 15:20:49 +01:00
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
/* CRI's UTF wrapper around DSP [Sonic Colors sfx (Wii), NiGHTS: Journey of Dreams sfx (Wii)] */
|
2011-01-04 15:20:49 +01:00
|
|
|
VGMSTREAM * init_vgmstream_utf_dsp(STREAMFILE *streamFile) {
|
2017-12-06 12:15:27 +01:00
|
|
|
VGMSTREAM * vgmstream = NULL;
|
2018-03-10 12:19:30 +01:00
|
|
|
off_t start_offset;
|
|
|
|
size_t channel_size;
|
2011-01-04 15:20:49 +01:00
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
int loop_flag = 0, channel_count, sample_rate;
|
2011-01-04 15:20:49 +01:00
|
|
|
long sample_count;
|
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
int table_error = 0;
|
|
|
|
const long top_offset = 0x00;
|
2011-01-04 15:20:49 +01:00
|
|
|
long top_data_offset, segment_count;
|
|
|
|
long body_offset, body_size;
|
|
|
|
long header_offset, header_size;
|
|
|
|
|
2011-01-06 02:23:02 +01:00
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
/* checks */
|
|
|
|
/* files don't have extension, we accept "" for CLI and .aax for plugins (they aren't exactly .aax though) */
|
|
|
|
if (!check_extensions(streamFile, "aax,"))
|
|
|
|
goto fail;
|
|
|
|
if (read_32bitBE(0x00,streamFile) != 0x40555446) /* "@UTF" */
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
/* get segment count, offsets and sizes*/
|
2011-01-04 15:20:49 +01:00
|
|
|
{
|
|
|
|
struct utf_query_result result;
|
|
|
|
long top_string_table_offset;
|
|
|
|
long top_string_table_size;
|
|
|
|
long name_offset;
|
|
|
|
|
|
|
|
result = query_utf_nofail(streamFile, top_offset, NULL, &table_error);
|
|
|
|
if (table_error) goto fail;
|
2018-03-10 12:19:30 +01:00
|
|
|
|
2011-01-04 15:20:49 +01:00
|
|
|
segment_count = result.rows;
|
2018-03-10 12:19:30 +01:00
|
|
|
if (segment_count != 1) goto fail; /* only simple stuff for now (multisegment not known) */
|
|
|
|
|
2011-01-04 15:20:49 +01:00
|
|
|
top_string_table_offset = top_offset + 8 + result.string_table_offset;
|
|
|
|
top_data_offset = top_offset + 8 + result.data_offset;
|
|
|
|
top_string_table_size = top_data_offset - top_string_table_offset;
|
|
|
|
|
|
|
|
if (result.name_offset+10 > top_string_table_size) goto fail;
|
|
|
|
|
|
|
|
name_offset = top_string_table_offset + result.name_offset;
|
2018-03-10 12:19:30 +01:00
|
|
|
if (read_32bitBE(name_offset+0x00, streamFile) != 0x41445043 || /* "ADPC" */
|
|
|
|
read_32bitBE(name_offset+0x04, streamFile) != 0x4D5F5749 || /* "M_WI" */
|
|
|
|
read_16bitBE(name_offset+0x08, streamFile) != 0x4900) /* "I\0" */
|
2011-01-04 15:20:49 +01:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
/* get sizes */
|
2011-01-04 15:20:49 +01:00
|
|
|
{
|
|
|
|
struct offset_size_pair offset_size;
|
|
|
|
|
|
|
|
offset_size = query_utf_data(streamFile, top_offset, 0, "data", &table_error);
|
|
|
|
if (table_error) goto fail;
|
|
|
|
body_offset = top_data_offset + offset_size.offset;
|
|
|
|
body_size = offset_size.size;
|
|
|
|
|
|
|
|
offset_size = query_utf_data(streamFile, top_offset, 0, "header", &table_error);
|
|
|
|
if (table_error) goto fail;
|
|
|
|
header_offset = top_data_offset + offset_size.offset;
|
|
|
|
header_size = offset_size.size;
|
|
|
|
}
|
|
|
|
|
|
|
|
channel_count = query_utf_1byte(streamFile, top_offset, 0, "nch", &table_error);
|
|
|
|
sample_count = query_utf_4byte(streamFile, top_offset, 0, "nsmpl", &table_error);
|
|
|
|
sample_rate = query_utf_4byte(streamFile, top_offset, 0, "sfreq", &table_error);
|
|
|
|
if (table_error) goto fail;
|
2011-01-05 16:49:42 +01:00
|
|
|
if (channel_count != 1 && channel_count != 2) goto fail;
|
|
|
|
if (header_size != channel_count * 0x60) goto fail;
|
2011-01-04 15:20:49 +01:00
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
start_offset = body_offset;
|
|
|
|
channel_size = (body_size+7) / 8 * 8 / channel_count;
|
|
|
|
|
|
|
|
|
|
|
|
/* build the VGMSTREAM */
|
2011-01-04 15:20:49 +01:00
|
|
|
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
2018-03-10 12:19:30 +01:00
|
|
|
if (!vgmstream) goto fail;
|
2011-01-04 15:20:49 +01:00
|
|
|
|
|
|
|
vgmstream->sample_rate = sample_rate;
|
2018-03-10 12:19:30 +01:00
|
|
|
vgmstream->num_samples = sample_count;
|
2011-01-04 15:20:49 +01:00
|
|
|
|
|
|
|
vgmstream->meta_type = meta_UTF_DSP;
|
2018-03-10 12:19:30 +01:00
|
|
|
vgmstream->coding_type = coding_NGC_DSP;
|
|
|
|
vgmstream->layout_type = layout_interleave;
|
|
|
|
vgmstream->interleave_block_size = channel_size;
|
2011-01-04 15:20:49 +01:00
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
dsp_read_coefs_be(vgmstream, streamFile, header_offset+0x1c, 0x60);
|
2011-01-04 15:20:49 +01:00
|
|
|
|
2018-03-10 12:19:30 +01:00
|
|
|
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
|
|
|
goto fail;
|
2011-01-04 15:20:49 +01:00
|
|
|
return vgmstream;
|
|
|
|
|
|
|
|
fail:
|
2018-03-10 12:19:30 +01:00
|
|
|
close_vgmstream(vgmstream);
|
2011-01-04 15:20:49 +01:00
|
|
|
return NULL;
|
|
|
|
}
|