Fix AKB2 subsongs [Mobius Final Fantasy (PC)]

This commit is contained in:
bnnm 2018-03-24 14:22:37 +01:00
parent eb4168f9b9
commit 8534006035
3 changed files with 73 additions and 45 deletions

View File

@ -3,7 +3,7 @@
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
/* AKB (AAC only) - found in SQEX iOS games */ /* AKB (AAC only) - found in SQEX iOS games */
VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_akb_mp4(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
size_t filesize; size_t filesize;
@ -34,7 +34,7 @@ fail:
/* AKB - found in SQEX iOS games */ /* AKB - found in SQEX iOS games */
VGMSTREAM * init_vgmstream_akb_multi(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset, extra_data_offset = 0; off_t start_offset, extra_data_offset = 0;
size_t file_size, header_size, extra_header_size = 0, extra_data_size = 0; size_t file_size, header_size, extra_header_size = 0, extra_data_size = 0;
@ -113,7 +113,7 @@ VGMSTREAM * init_vgmstream_akb_multi(STREAMFILE *streamFile) {
} }
case 0x06: { /* aac [The World Ends with You (iPad)] */ case 0x06: { /* aac [The World Ends with You (iPad)] */
/* init_vgmstream_akb above has priority, but this works fine too */ /* init_vgmstream_akb_mp4 above has priority, but this works fine too */
ffmpeg_codec_data *ffmpeg_data; ffmpeg_codec_data *ffmpeg_data;
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,file_size-start_offset); ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,file_size-start_offset);
@ -149,80 +149,108 @@ fail:
/* AKB2 - found in later SQEX iOS games */ /* AKB2 - found in later SQEX iOS games */
VGMSTREAM * init_vgmstream_akb2_multi(STREAMFILE *streamFile) { VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL; VGMSTREAM * vgmstream = NULL;
off_t start_offset, header_offset; off_t start_offset, material_offset, extradata_offset;
size_t datasize; size_t material_size, extradata_size, stream_size;
int loop_flag = 0, channel_count, codec; int loop_flag = 0, channel_count, encryption_flag, codec, sample_rate, num_samples, loop_start, loop_end;
int akb_header_size, sound_index = 0, sound_offset_data, sound, sound_header_size, material_offset_data, material_index = 0, material, extradata, encryption_flag; int total_subsongs, target_subsong = streamFile->stream_index;
/* check extensions */ /* check extensions */
/* .akb.bytes is the usual extension in later games */ /* .akb.bytes is the usual extension in later games */
if ( !check_extensions(streamFile, "akb") ) if ( !check_extensions(streamFile, "akb,bytes") )
goto fail; goto fail;
/* check header */ /* checks */
if (read_32bitBE(0x00,streamFile) != 0x414B4232) /* "AKB2" */ if (read_32bitBE(0x00,streamFile) != 0x414B4232) /* "AKB2" */
goto fail; goto fail;
if (read_32bitLE(0x08,streamFile) != get_streamfile_size(streamFile))
goto fail;
akb_header_size = read_16bitLE(0x06, streamFile); /* parse tables */
sound_offset_data = akb_header_size + sound_index * 0x10; {
sound = read_32bitLE(sound_offset_data + 0x04, streamFile); off_t table_offset;
sound_header_size = read_16bitLE(sound + 0x02, streamFile); size_t table_size, entry_size;
material_offset_data = sound + sound_header_size + material_index * 0x10; off_t akb_header_size = read_16bitLE(0x06, streamFile);
material = sound + read_32bitLE(material_offset_data + 0x04, streamFile); int table_count = read_8bit(0x0c, streamFile);
encryption_flag = read_8bit(material + 0x03, streamFile) & 0x08;
extradata = material + read_16bitLE(material + 0x04, streamFile);
start_offset = material + read_16bitLE(material + 0x04, streamFile) + read_32bitLE(material + 0x18, streamFile); /* probably each table has its type somewhere, but only seen last table = sound table */
header_offset = material; if (table_count > 2) /* 2 only seen in some Mobius FF sound banks */
goto fail;
entry_size = 0x10; /* technically every entry/table has its own size but to simplify... */
table_offset = read_32bitLE(akb_header_size + (table_count-1)*entry_size + 0x04, streamFile);
table_size = read_16bitLE(table_offset + 0x02, streamFile);
total_subsongs = read_8bit(table_offset + 0x0f, streamFile); /* can contain 0 entries too */
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
material_offset = table_offset + read_32bitLE(table_offset + table_size + (target_subsong-1)*entry_size + 0x04, streamFile);
}
/** stream header **/
/* 0x00: 0? */
codec = read_8bit(material_offset+0x01,streamFile);
channel_count = read_8bit(material_offset+0x02,streamFile);
encryption_flag = read_8bit(material_offset+0x03,streamFile);
material_size = read_16bitLE(material_offset+0x04,streamFile);
sample_rate = (uint16_t)read_16bitLE(material_offset+0x06,streamFile);
stream_size = read_32bitLE(material_offset+0x08,streamFile);
num_samples = read_32bitLE(material_offset+0x0c,streamFile);
loop_start = read_32bitLE(material_offset+0x10,streamFile);
loop_end = read_32bitLE(material_offset+0x14,streamFile);
extradata_size = read_32bitLE(material_offset+0x18,streamFile);
/* rest: ? (empty or 0x3f80) */
loop_flag = (loop_end > 0);
extradata_offset = material_offset + material_size;
start_offset = material_offset + material_size + extradata_size;
channel_count = read_8bit(header_offset+0x02,streamFile);
loop_flag = read_32bitLE(header_offset+0x14,streamFile) > 0; /* loop end */
/* 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;
/* 0x04: version? 0x08: filesize, 0x28: file_id?, others: no idea */ vgmstream->sample_rate = sample_rate;
codec = read_8bit(header_offset+0x01,streamFile); vgmstream->num_streams = total_subsongs;
datasize = read_32bitLE(header_offset+0x08,streamFile); vgmstream->stream_size = stream_size;
vgmstream->sample_rate = (uint16_t)read_16bitLE(header_offset+0x06,streamFile);
/* When loop_flag num_samples may be much larger than real num_samples (it's fine when looping is off)
* Actual num_samples would be loop_end_sample+1, but more testing is needed */
vgmstream->num_samples = read_32bitLE(header_offset+0x0c,streamFile);
vgmstream->loop_start_sample = read_32bitLE(header_offset+0x10,streamFile);
vgmstream->loop_end_sample = read_32bitLE(header_offset+0x14,streamFile);
vgmstream->meta_type = meta_AKB; vgmstream->meta_type = meta_AKB;
switch (codec) { switch (codec) {
case 0x02: { /* MSAPDCM [The Irregular at Magic High School Lost Zero (Android)] */ case 0x02: { /* MSADPCM [The Irregular at Magic High School Lost Zero (Android)] */
if (encryption_flag) goto fail; if (encryption_flag & 0x08) goto fail;
vgmstream->coding_type = coding_MSADPCM; vgmstream->coding_type = coding_MSADPCM;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
vgmstream->interleave_block_size = read_16bitLE(extradata + 0x02, streamFile); vgmstream->interleave_block_size = read_16bitLE(extradata_offset + 0x02, streamFile);
/* adjusted samples; bigger or smaller than base samples, but seems more accurate /* adjusted samples; bigger or smaller than base samples, but seems more accurate
* (base samples may have more than possible and read over file size otherwise, very strange) * (base samples may have more than possible and read over file size otherwise, very strange)
* loop_end seems to exist even with loop disabled */ * loop_end seems to exist even with loop disabled */
vgmstream->num_samples = read_32bitLE(extradata + 0x04, streamFile); vgmstream->num_samples = read_32bitLE(extradata_offset + 0x04, streamFile);
vgmstream->loop_start_sample = read_32bitLE(extradata + 0x08, streamFile); vgmstream->loop_start_sample = read_32bitLE(extradata_offset + 0x08, streamFile);
vgmstream->loop_end_sample = read_32bitLE(extradata + 0x0c, streamFile); vgmstream->loop_end_sample = read_32bitLE(extradata_offset + 0x0c, streamFile);
break; break;
} }
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
case 0x05: { /* ogg vorbis [The World Ends with You (iPhone / latest update)] */ case 0x05: { /* Ogg Vorbis [The World Ends with You (iOS / latest update)] */
ffmpeg_codec_data *ffmpeg_data; ffmpeg_codec_data *ffmpeg_data;
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,datasize); ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size);
if ( !ffmpeg_data ) goto fail; if ( !ffmpeg_data ) goto fail;
vgmstream->codec_data = ffmpeg_data; vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg; vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
/* When loop_flag num_samples may be much larger than real num_samples (it's fine when looping is off)
* Actual num_samples would be loop_end_sample+1, but more testing is needed */
vgmstream->num_samples = num_samples;
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
break; break;
} }
#endif #endif

View File

@ -125,7 +125,7 @@ VGMSTREAM * init_vgmstream_mp4_aac_ffmpeg(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_mp4_aac(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_mp4_aac(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_mp4_aac_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size); VGMSTREAM * init_vgmstream_mp4_aac_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_akb_mp4(STREAMFILE *streamFile);
#endif #endif
VGMSTREAM * init_vgmstream_sfl(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_sfl(STREAMFILE * streamFile);
@ -622,8 +622,8 @@ VGMSTREAM * init_vgmstream_x360_cxs(STREAMFILE* streamFile);
VGMSTREAM * init_vgmstream_dsp_adx(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_dsp_adx(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_akb_multi(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_akb(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_akb2_multi(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_akb2(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_x360_ast(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_x360_ast(STREAMFILE *streamFile);

View File

@ -69,7 +69,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_mp4_aac, init_vgmstream_mp4_aac,
#endif #endif
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
init_vgmstream_akb, init_vgmstream_akb_mp4,
#endif #endif
init_vgmstream_sadb, init_vgmstream_sadb,
init_vgmstream_ps2_bmdx, init_vgmstream_ps2_bmdx,
@ -330,8 +330,8 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_ps2_vds_vdm, init_vgmstream_ps2_vds_vdm,
init_vgmstream_x360_cxs, init_vgmstream_x360_cxs,
init_vgmstream_dsp_adx, init_vgmstream_dsp_adx,
init_vgmstream_akb_multi, init_vgmstream_akb,
init_vgmstream_akb2_multi, init_vgmstream_akb2,
#ifdef VGM_USE_FFMPEG #ifdef VGM_USE_FFMPEG
init_vgmstream_mp4_aac_ffmpeg, init_vgmstream_mp4_aac_ffmpeg,
#endif #endif