2008-08-02 06:40:46 +02:00
|
|
|
#include "meta.h"
|
2019-02-23 15:26:08 +01:00
|
|
|
#include "../layout/layout.h"
|
2017-12-06 12:15:27 +01:00
|
|
|
#include "aix_streamfile.h"
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
|
|
|
|
#define MAX_SEGMENTS 10 /* usually segment0=intro, segment1=loop/main, sometimes ~5 */
|
|
|
|
|
|
|
|
static VGMSTREAM *build_segmented_vgmstream(STREAMFILE *streamFile, off_t *segment_offsets, size_t *segment_sizes, int32_t *segment_samples, int segment_count, int layer_count);
|
|
|
|
|
|
|
|
/* AIX - N segments with M layers (2ch ADX) inside [SoulCalibur IV (PS3), Dragon Ball Z: Burst Limit (PS3)] */
|
|
|
|
VGMSTREAM * init_vgmstream_aix(STREAMFILE *sf) {
|
2018-09-09 20:17:41 +02:00
|
|
|
VGMSTREAM * vgmstream = NULL;
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
off_t segment_offsets[MAX_SEGMENTS] = {0};
|
|
|
|
size_t segment_sizes[MAX_SEGMENTS] = {0};
|
|
|
|
int32_t segment_samples[MAX_SEGMENTS] = {0};
|
|
|
|
int segment_rates[MAX_SEGMENTS] = {0};
|
|
|
|
|
2018-09-09 20:17:41 +02:00
|
|
|
off_t data_offset;
|
|
|
|
off_t layer_list_offset;
|
|
|
|
off_t layer_list_end;
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2018-09-09 20:17:41 +02:00
|
|
|
int segment_count, layer_count;
|
2019-02-23 15:26:08 +01:00
|
|
|
int i;
|
2008-08-02 06:40:46 +02:00
|
|
|
|
|
|
|
|
2018-09-09 20:17:41 +02:00
|
|
|
/* checks */
|
2019-02-23 15:26:08 +01:00
|
|
|
if (!check_extensions(sf, "aix"))
|
2018-09-09 20:17:41 +02:00
|
|
|
goto fail;
|
2019-02-23 15:26:08 +01:00
|
|
|
if (read_u32be(0x00,sf) != 0x41495846 || /* "AIXF" */
|
|
|
|
read_u32be(0x08,sf) != 0x01000014 || /* version? */
|
|
|
|
read_u32be(0x0c,sf) != 0x00000800)
|
2008-08-02 12:24:28 +02:00
|
|
|
goto fail;
|
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
/* AIX combine layers for multichannel and segments for looping, all very hacky.
|
|
|
|
* For some reason AIX with 1 layer and 1 segment exist (equivalent to a single ADX). */
|
|
|
|
|
2018-09-09 20:17:41 +02:00
|
|
|
/* base segment header */
|
2019-02-23 15:26:08 +01:00
|
|
|
data_offset = read_s32be(0x04,sf) + 0x08;
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
/* parse segments table */
|
|
|
|
{
|
|
|
|
const off_t segment_list_offset = 0x20;
|
|
|
|
const size_t segment_list_entry_size = 0x10;
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
segment_count = read_u16be(0x18,sf);
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
if (segment_count < 1 || segment_count > MAX_SEGMENTS) goto fail;
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
layer_list_offset = segment_list_offset + segment_count*segment_list_entry_size + 0x10;
|
|
|
|
if (layer_list_offset >= data_offset) goto fail;
|
2018-09-09 20:17:41 +02:00
|
|
|
|
|
|
|
for (i = 0; i < segment_count; i++) {
|
2019-02-23 15:26:08 +01:00
|
|
|
segment_offsets[i] = read_s32be(segment_list_offset + segment_list_entry_size*i + 0x00,sf);
|
|
|
|
segment_sizes[i] = read_u32be(segment_list_offset + segment_list_entry_size*i + 0x04,sf);
|
|
|
|
segment_samples[i] = read_s32be(segment_list_offset + segment_list_entry_size*i + 0x08,sf);
|
|
|
|
segment_rates[i] = read_s32be(segment_list_offset + segment_list_entry_size*i + 0x0c,sf);
|
|
|
|
|
|
|
|
/* segments > 0 can have 0 sample rate, seems to indicate same as first
|
|
|
|
* [Ryu ga Gotoku: Kenzan! (PS3) tenkei_sng1.aix] */
|
|
|
|
if (i > 0 && segment_rates[i] == 0)
|
|
|
|
segment_rates[i] = segment_rates[0];
|
2018-09-09 20:17:41 +02:00
|
|
|
|
|
|
|
/* all segments must have equal sample rate */
|
2019-02-23 15:26:08 +01:00
|
|
|
if (segment_rates[i] != segment_rates[0])
|
|
|
|
goto fail;
|
2011-01-17 19:29:29 +01:00
|
|
|
}
|
2008-08-02 06:40:46 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
if (segment_offsets[0] != data_offset)
|
2018-09-09 20:17:41 +02:00
|
|
|
goto fail;
|
|
|
|
}
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
/* parse layers table */
|
|
|
|
{
|
|
|
|
const size_t layer_list_entry_size = 0x08;
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
layer_count = read_u8(layer_list_offset,sf);
|
|
|
|
if (layer_count < 1) goto fail;
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
layer_list_end = layer_list_offset + 0x08 + layer_count*layer_list_entry_size;
|
|
|
|
if (layer_list_end >= data_offset) goto fail;
|
2008-08-02 06:40:46 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
for (i = 0; i < layer_count; i++) {
|
|
|
|
/* all layers must have same sample rate as segments */
|
|
|
|
if (read_s32be(layer_list_offset + 0x08 + i*layer_list_entry_size + 0x00,sf) != segment_rates[0])
|
2008-08-02 12:24:28 +02:00
|
|
|
goto fail;
|
2019-02-23 15:26:08 +01:00
|
|
|
/* 0x04: layer channels */
|
2008-08-02 12:24:28 +02:00
|
|
|
}
|
|
|
|
}
|
2008-08-02 06:40:46 +02:00
|
|
|
|
2018-09-09 20:17:41 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
/* build combo layers + segments VGMSTREAM */
|
|
|
|
vgmstream = build_segmented_vgmstream(sf, segment_offsets, segment_sizes, segment_samples, segment_count, layer_count);
|
|
|
|
if (!vgmstream) goto fail;
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
vgmstream->meta_type = meta_AIX;
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
return vgmstream;
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
fail:
|
|
|
|
close_vgmstream(vgmstream);
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
static VGMSTREAM *build_layered_vgmstream(STREAMFILE *streamFile, off_t segment_offset, size_t segment_size, int layer_count) {
|
|
|
|
VGMSTREAM *vgmstream;
|
|
|
|
layered_layout_data* data = NULL;
|
|
|
|
int i;
|
|
|
|
STREAMFILE* temp_streamFile = NULL;
|
2008-08-02 06:40:46 +02:00
|
|
|
|
2018-09-09 20:17:41 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
/* build layers */
|
|
|
|
data = init_layout_layered(layer_count);
|
|
|
|
if (!data) goto fail;
|
2018-09-09 20:17:41 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
for (i = 0; i < layer_count; i++) {
|
|
|
|
/* build the layer STREAMFILE */
|
|
|
|
temp_streamFile = setup_aix_streamfile(streamFile, segment_offset, segment_size, i, "adx");
|
|
|
|
if (!temp_streamFile) goto fail;
|
2018-09-09 20:17:41 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
/* build the sub-VGMSTREAM */
|
|
|
|
data->layers[i] = init_vgmstream_adx(temp_streamFile);
|
|
|
|
if (!data->layers[i]) goto fail;
|
2018-09-09 20:17:41 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
data->layers[i]->stream_size = segment_size;
|
2019-02-15 22:28:20 +01:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
close_streamfile(temp_streamFile);
|
|
|
|
temp_streamFile = NULL;
|
2008-08-02 06:40:46 +02:00
|
|
|
}
|
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
if (!setup_layout_layered(data))
|
|
|
|
goto fail;
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2018-09-09 20:17:41 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
/* build the layered VGMSTREAM */
|
|
|
|
vgmstream = allocate_layered_vgmstream(data);
|
2018-09-09 20:17:41 +02:00
|
|
|
if (!vgmstream) goto fail;
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
return vgmstream;
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
fail:
|
|
|
|
if (!vgmstream) free_layout_layered(data);
|
|
|
|
close_vgmstream(vgmstream);
|
|
|
|
close_streamfile(temp_streamFile);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VGMSTREAM *build_segmented_vgmstream(STREAMFILE *streamFile, off_t *segment_offsets, size_t *segment_sizes, int32_t *segment_samples, int segment_count, int layer_count) {
|
|
|
|
VGMSTREAM *vgmstream;
|
|
|
|
segmented_layout_data *data = NULL;
|
|
|
|
int i, loop_flag, loop_start_segment, loop_end_segment;
|
|
|
|
|
|
|
|
|
|
|
|
/* build segments */
|
|
|
|
data = init_layout_segmented(segment_count);
|
|
|
|
if (!data) goto fail;
|
|
|
|
|
|
|
|
for (i = 0; i < segment_count; i++) {
|
|
|
|
/* build the layered sub-VGMSTREAM */
|
|
|
|
data->segments[i] = build_layered_vgmstream(streamFile, segment_offsets[i], segment_sizes[i], layer_count);
|
|
|
|
if (!data->segments[i]) goto fail;
|
|
|
|
|
|
|
|
data->segments[i]->num_samples = segment_samples[i]; /* just in case */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!setup_layout_segmented(data))
|
|
|
|
goto fail;
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
loop_flag = (segment_count > 0);
|
|
|
|
loop_start_segment = 1;
|
|
|
|
loop_end_segment = 1; /* looks correct in some games (DBZ: Burst Limit, Metroid: Other M) */
|
2008-08-02 12:24:28 +02:00
|
|
|
|
2019-02-23 15:26:08 +01:00
|
|
|
/* build the segmented VGMSTREAM */
|
|
|
|
vgmstream = allocate_segmented_vgmstream(data, loop_flag, loop_start_segment, loop_end_segment);
|
|
|
|
if (!vgmstream) goto fail;
|
2008-08-02 06:40:46 +02:00
|
|
|
|
|
|
|
return vgmstream;
|
|
|
|
|
|
|
|
fail:
|
2019-02-23 15:26:08 +01:00
|
|
|
if (!vgmstream) free_layout_segmented(data);
|
2018-09-09 20:17:41 +02:00
|
|
|
close_vgmstream(vgmstream);
|
2008-08-02 06:40:46 +02:00
|
|
|
return NULL;
|
|
|
|
}
|