#include "meta.h" #include "aix_streamfile.h" /* AIX - interleaved AAX, N segments with M layers (1/2ch) inside [SoulCalibur IV (PS3), Dragon Ball Z: Burst Limit (PS3)] */ VGMSTREAM * init_vgmstream_aix(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; STREAMFILE * streamFileAIX = NULL; int loop_flag = 0, channel_count, sample_rate; int32_t sample_count, loop_start_sample = 0, loop_end_sample = 0; off_t *segment_offset = NULL; int32_t *segment_samples = NULL; aix_codec_data *data = NULL; off_t data_offset; off_t layer_list_offset; off_t layer_list_end; const off_t segment_list_offset = 0x20; const size_t segment_list_entry_size = 0x10; const size_t layer_list_entry_size = 0x08; int segment_count, layer_count; int i, j; /* checks */ if (!check_extensions(streamFile, "aix")) goto fail; if (read_32bitBE(0x00,streamFile) != 0x41495846 || /* "AIXF" */ read_32bitBE(0x08,streamFile) != 0x01000014 || /* version? */ read_32bitBE(0x0c,streamFile) != 0x00000800) goto fail; /* base segment header */ data_offset = read_32bitBE(0x04,streamFile)+0x08; segment_count = (uint16_t)read_16bitBE(0x18,streamFile); if (segment_count < 1) goto fail; layer_list_offset = segment_list_offset + segment_count*segment_list_entry_size + 0x10; if (layer_list_offset >= data_offset) goto fail; segment_samples = calloc(segment_count,sizeof(int32_t)); if (!segment_samples) goto fail; segment_offset = calloc(segment_count,sizeof(off_t)); if (!segment_offset) goto fail; /* parse segments table */ { sample_rate = read_32bitBE(layer_list_offset+0x08,streamFile); /* first layer's sample rate */ for (i = 0; i < segment_count; i++) { segment_offset[i] = read_32bitBE(segment_list_offset + segment_list_entry_size*i + 0x00,streamFile); /* 0x04: segment size */ segment_samples[i] = read_32bitBE(segment_list_offset + segment_list_entry_size*i + 0x08,streamFile); /* all segments must have equal sample rate */ if (read_32bitBE(segment_list_offset + segment_list_entry_size*i + 0x0c,streamFile) != sample_rate) { /* segments > 0 can have 0 sample rate (Ryu ga gotoku: Kenzan! tenkei_sng1.aix), seems to indicate same sample rate as first */ if (!(i > 0 && read_32bitBE(segment_list_offset + segment_list_entry_size*i + 0x0c,streamFile) == 0)) goto fail; } } if (segment_offset[0] != data_offset) goto fail; } /* base layer header */ layer_count = (uint8_t)read_8bit(layer_list_offset,streamFile); if (layer_count < 1) goto fail; layer_list_end = layer_list_offset + 0x08 + layer_count*layer_list_entry_size; if (layer_list_end >= data_offset) goto fail; /* parse layers table */ channel_count = 0; for (i = 0; i < layer_count; i++) { /* all streams must have same samplerate as segments */ if (read_32bitBE(layer_list_offset + 0x08 + i*layer_list_entry_size + 0x00,streamFile) != sample_rate) goto fail; channel_count += read_8bit(layer_list_offset + 0x08 + i*layer_list_entry_size + 0x04,streamFile); } /* check for existence of segments */ for (i = 0; i < segment_count; i++) { off_t aixp_offset = segment_offset[i]; for (j = 0; j < layer_count; j++) { if (read_32bitBE(aixp_offset,streamFile) != 0x41495850) /* "AIXP" */ goto fail; if (read_8bit(aixp_offset+0x08,streamFile) != j) goto fail; aixp_offset += read_32bitBE(aixp_offset+0x04,streamFile) + 0x08; } } /* open base streamfile, that will be shared by all open_aix_with_STREAMFILE */ { char filename[PATH_LIMIT]; streamFile->get_name(streamFile,filename,sizeof(filename)); streamFileAIX = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE); //todo simplify if (!streamFileAIX) goto fail; } /* init layout */ { data = malloc(sizeof(aix_codec_data)); if (!data) goto fail; data->segment_count = segment_count; data->stream_count = layer_count; data->adxs = calloc(segment_count * layer_count, sizeof(VGMSTREAM*)); if (!data->adxs) goto fail; data->sample_counts = calloc(segment_count,sizeof(int32_t)); if (!data->sample_counts) goto fail; memcpy(data->sample_counts,segment_samples,segment_count*sizeof(int32_t)); } /* open each segment / layer subfile */ for (i = 0; i < segment_count; i++) { for (j = 0; j < layer_count; j++) { //;VGM_LOG("AIX: opening segment %d/%d stream %d/%d %x\n",i,segment_count,j,stream_count,segment_offset[i]); VGMSTREAM *temp_vgmstream; STREAMFILE * temp_streamFile = open_aix_with_STREAMFILE(streamFileAIX,segment_offset[i],j); if (!temp_streamFile) goto fail; temp_vgmstream = data->adxs[i*layer_count+j] = init_vgmstream_adx(temp_streamFile); close_streamfile(temp_streamFile); if (!temp_vgmstream) goto fail; /* setup layers */ if (temp_vgmstream->num_samples != data->sample_counts[i] || temp_vgmstream->loop_flag != 0) goto fail; setup_vgmstream(temp_vgmstream); /* final setup as the VGMSTREAM was created manually */ } } /* get looping and samples */ sample_count = 0; loop_flag = (segment_count > 1); for (i = 0; i < segment_count; i++) { sample_count += data->sample_counts[i]; if (i == 0) loop_start_sample = sample_count; if (i == 1) loop_end_sample = sample_count; } /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; vgmstream->num_samples = sample_count; vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; vgmstream->meta_type = meta_AIX; vgmstream->coding_type = data->adxs[0]->coding_type; vgmstream->layout_type = layout_aix; vgmstream->ch[0].streamfile = streamFileAIX; data->current_segment = 0; vgmstream->codec_data = data; free(segment_offset); free(segment_samples); return vgmstream; fail: close_streamfile(streamFileAIX); close_vgmstream(vgmstream); free(segment_samples); free(segment_offset); /* free aix layout */ if (data) { if (data->adxs) { int i; for (i = 0; i < data->segment_count*data->stream_count; i++) { close_vgmstream(data->adxs[i]); } free(data->adxs); } if (data->sample_counts) { free(data->sample_counts); } free(data); } return NULL; }