Redo ADS to fix various issues

- Fix minor samples bugs
- Fix ADSC variation [Kenka Bancho 2, Shunjuku no Okami]
- Fix misc loops [Culdcept, Katakamuna, Super Galdelic Hour]
- Fix Capcom loops [MM X7, BOF5, Clock Tower 3]
- Fix cavia loops [Drakengard 1/2, GITS: Stand Alone Complex]
- Fix Angel Studios videos [Red Read Revolver, Spy Hunter 2]
- Fix misdetected files [Gran Turismo 2000 videos]
This commit is contained in:
bnnm 2018-06-17 01:08:52 +02:00
parent a535a497f4
commit ae3307e6f0
3 changed files with 329 additions and 117 deletions

View File

@ -757,4 +757,6 @@ VGMSTREAM * init_vgmstream_dsp_sadf(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_h4m(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_ps2_ads_container(STREAMFILE *streamFile);
#endif /*_META_H*/

View File

@ -1,13 +1,16 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* .ADS - Sony's "Audio Stream" format [Edit Racing (PS2), Evergrace II (PS2), Pri-Saga! Portable (PSP)] */
VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
int loop_flag, channel_count;
off_t start_offset;
size_t stream_size;
uint32_t loop_start, loop_end;
int loop_flag, channel_count, sample_rate, interleave, is_loop_samples = 0;
size_t body_size, stream_size, file_size;
uint32_t codec, loop_start_sample = 0, loop_end_sample = 0, loop_start_offset = 0, loop_end_offset = 0;
coding_t coding_type;
int ignore_silent_frame_cavia = 0, ignore_silent_frame_capcom = 0;
/* checks */
@ -21,147 +24,282 @@ VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile) {
if (read_32bitBE(0x00,streamFile) != 0x53536864 && /* "SShd" */
read_32bitBE(0x20,streamFile) != 0x53536264) /* "SSbd" */
goto fail;
/* 0x04: header size, always 0x20 */
if (read_32bitLE(0x04,streamFile) != 0x18 && /* standard header size */
read_32bitLE(0x04,streamFile) != 0x20) /* True Fortune (PS2) */
goto fail;
/* check if file is not corrupt */ //todo ???
/* seems the Gran Turismo 4 ADS files are considered corrupt,*/
/* so I changed it to adapt the stream size if that's the case */
/* instead of failing playing them at all*/
stream_size = read_32bitLE(0x24,streamFile); /* body size */
if (stream_size + 0x28 >= get_streamfile_size(streamFile)) {
stream_size = get_streamfile_size(streamFile) - 0x28;
/* base values (a bit unorderly since devs hack ADS too much and detection is messy) */
{
codec = read_32bitLE(0x08,streamFile);
sample_rate = read_32bitLE(0x0C,streamFile);
channel_count = read_32bitLE(0x10,streamFile); /* up to 4 [Eve of Extinction (PS2)]*/
interleave = read_32bitLE(0x14,streamFile); /* set even when mono */
switch(codec) {
case 0x01: /* official definition */
case 0x80000001: /* [Evergrace II (PS2), but not other From Soft games] */
coding_type = coding_PCM16LE;
/* Angel Studios/Rockstar San Diego videos codec hijack [Red Dead Revolver (PS2), Spy Hunter 2 (PS2)] */
if (sample_rate == 12000 && interleave == 0x200) {
sample_rate = 48000;
interleave = 0x40;
coding_type = coding_DVI_IMA_int;
/* should try to detect IMA data but it's not so easy, this works ok since
* no known games use these settings, videos normally are 48000/24000hz */
}
break;
case 0x10: /* official definition */
case 0x02: /* Capcom games extension, stereo only [Megaman X7 (PS2), Breath of Fire V (PS2), Clock Tower 3 (PS2)] */
coding_type = coding_PSX;
break;
case 0x00: /* PCM16BE from official docs, probably never used */
default:
VGM_LOG("ADS: unknown codec\n");
goto fail;
}
}
/* check loop */
loop_start = read_32bitLE(0x18,streamFile);
loop_end = read_32bitLE(0x1C,streamFile);
//todo should loop if loop_start > 0 and loop_end == -1
if ((loop_end == 0xFFFFFFFF) || (loop_start == 0 && loop_end == 0)) {
/* sizes */
{
file_size = get_streamfile_size(streamFile);
body_size = read_32bitLE(0x24,streamFile);
/* bigger than file_size in rare cases, even if containing all data (ex. Megaman X7's SY04.ADS) */
if (body_size + 0x28 > file_size) {
body_size = file_size - 0x28;
}
/* True Fortune: weird stream size */
if (body_size * 2 == file_size - 0x18) {
body_size = (body_size * 2) - 0x10;
}
stream_size = body_size;
}
/* offset */
{
start_offset = 0x28;
/* start padding (body size is ok, may have end padding) [Evergrace II (PS2), Armored Core 3 (PS2)] */
/* detection depends on files being properly ripped, so broken/cut files won't play ok */
if (file_size - body_size >= 0x800) {
start_offset = 0x800; /* aligned to sector */
/* too much end padding, happens in Super Galdelic Hour's SEL.ADS, maybe in bad rips too */
VGM_ASSERT(file_size - body_size > 0x8000, "ADS: big end padding %x\n", file_size - body_size);
}
/* "ADSC" container */
if (coding_type == coding_PSX
&& read_32bitLE(0x28,streamFile) == 0x1000 /* real start */
&& read_32bitLE(0x2c,streamFile) == 0
&& read_32bitLE(0x1008,streamFile) != 0) {
int i;
int is_adsc = 1;
/* should be empty up to data start */
for (i = 0; i < 0xFDC/4; i++) {
if (read_32bitLE(0x2c+(i*4),streamFile) != 0) {
is_adsc = 0;
break;
}
}
if (is_adsc) {
start_offset = 0x1000 - 0x08; /* remove "ADSC" alignment */
/* stream_size doesn't count start offset padding */
}
}
}
/* loops */
{
uint32_t loop_start, loop_end;
loop_start = read_32bitLE(0x18,streamFile);
loop_end = read_32bitLE(0x1C,streamFile);
loop_flag = 0;
}
else {
loop_flag = 1;
/* detect loops the best we can; docs say those are loop block addresses,
* but each maker does whatever (no games seem to use PS-ADPCM loop flags though) */
if (loop_start != 0xFFFFFFFF && loop_end == 0xFFFFFFFF) {
if (codec == 0x02) { /* Capcom codec */
/* Capcom games: loop_start is address * 0x10 [Mega Man X7, Breath of Fire V, Clock Tower 3] */
loop_flag = ((loop_start * 0x10) + 0x200 < body_size); /* near the end (+0x20~80) means no loop */
loop_start_offset = loop_start * 0x10;
ignore_silent_frame_capcom = 1;
}
else if (read_32bitBE(0x28,streamFile) == 0x50414421) { /* "PAD!" padding until 0x800 */
/* Super Galdelic Hour: loop_start is PCM bytes */
loop_flag = 1;
loop_start_sample = loop_start / 2 / channel_count;
is_loop_samples = 1;
}
else if ((loop_start % 0x800 == 0) && loop_start > 0) {/* sector-aligned, min is 0x800 */
/* cavia games: loop_start is offset [Drakengard 1/2, GITS: Stand Alone Complex] */
loop_flag = 1;
loop_start_offset = loop_start;
ignore_silent_frame_cavia = 1;
}
else if (loop_start % 0x800 != 0 || loop_start == 0) { /* not sector aligned */
/* Katakamuna: loop_start is address * 0x10 */
loop_flag = 1;
loop_start_offset = loop_start * 0x10;
}
}
else if (loop_start != 0xFFFFFFFF && loop_end != 0xFFFFFFFF
&& loop_end > 0) { /* ignore Kamen Rider Blade and others */
#if 0
//todo improve detection to avoid clashing with address*0x20
if (loop_end == body_size / 0x10) { /* always body_size? but not all files should loop */
/* Akane Iro ni Somaru Saka - Parallel: loops is address * 0x10 */
loop_flag = 1;
loop_start_offset = loop_start * 0x10;
loop_end_offset = loop_end * 0x10;
}
#endif
if (loop_end <= body_size / 0x70 && coding_type == coding_PCM16LE) { /* close to body_size */
/* Armored Core - Nexus: loops is address * 0x70 */
loop_flag = 1;
loop_start_offset = loop_start * 0x70;
loop_end_offset = loop_end * 0x70;
}
else if (loop_end <= body_size / 0x20 && coding_type == coding_PCM16LE) { /* close to body_size */
/* Armored Core - Nine Breaker: loops is address * 0x20 */
loop_flag = 1;
loop_start_offset = loop_start * 0x20;
loop_end_offset = loop_end * 0x20;
}
else if (loop_end <= body_size / 0x20 && coding_type == coding_PSX) { /* close to body_size */
/* various games: loops is address * 0x20 [Fire Pro Wrestling Returns, A.C.E. - Another Century's Episode] */
loop_flag = 1;
loop_start_offset = loop_start * 0x20;
loop_end_offset = loop_end * 0x20;
}
else if ((loop_end > body_size / 0x20 && coding_type == coding_PSX) ||
(loop_end > body_size / 0x70 && coding_type == coding_PCM16LE)) {
/* various games: loops in samples [Eve of Extinction, Culdcept, WWE Smackdown! 3] */
loop_flag = 1;
loop_start_sample = loop_start;
loop_end_sample = loop_end;
is_loop_samples = 1;
VGM_LOG("1\n");
}
}
//todo Jet Ion Grand Prix seems to have some loop-like values at 0x28
//todo Yoake mae yori Ruriiro na has loops in unknown format
}
channel_count = read_32bitLE(0x10,streamFile);
/* most games have empty PS-ADPCM frames in the last interleave block that should be skipped for smooth looping */
if (coding_type == coding_PSX) {
off_t offset, min_offset;
offset = start_offset + stream_size;
min_offset = offset - interleave;
do {
offset -= 0x10;
if (read_8bit(offset+0x01,streamFile) == 0x07) {
stream_size -= 0x10*channel_count;/* ignore don't decode flag/padding frame (most common) [ex. Capcom games] */
}
else if (read_32bitBE(offset+0x00,streamFile) == 0x00000000 && read_32bitBE(offset+0x04,streamFile) == 0x00000000 &&
read_32bitBE(offset+0x08,streamFile) == 0x00000000 && read_32bitBE(offset+0x0c,streamFile) == 0x00000000) {
stream_size -= 0x10*channel_count; /* ignore null frame [ex. A.C.E. Another Century Episode 1/2/3] */
}
else if (read_32bitBE(offset+0x00,streamFile) == 0x00007777 && read_32bitBE(offset+0x04,streamFile) == 0x77777777 &&
read_32bitBE(offset+0x08,streamFile) == 0x77777777 && read_32bitBE(offset+0x0c,streamFile) == 0x77777777) {
stream_size -= 0x10*channel_count; /* ignore padding frame [ex. Akane Iro ni Somaru Saka - Parallel] */
}
else if (read_32bitBE(offset+0x00,streamFile) == 0x0C020000 && read_32bitBE(offset+0x04,streamFile) == 0x00000000 &&
read_32bitBE(offset+0x08,streamFile) == 0x00000000 && read_32bitBE(offset+0x0c,streamFile) == 0x00000000 &&
ignore_silent_frame_cavia) {
stream_size -= 0x10*channel_count; /* ignore silent frame [ex. cavia games] */
}
else if (read_32bitBE(offset+0x00,streamFile) == 0x0C010000 && read_32bitBE(offset+0x04,streamFile) == 0x00000000 &&
read_32bitBE(offset+0x08,streamFile) == 0x00000000 && read_32bitBE(offset+0x0c,streamFile) == 0x00000000 &&
ignore_silent_frame_capcom) {
stream_size -= 0x10*channel_count; /* ignore silent frame [ex. Capcom games] */
}
else {
break; /* standard frame */
}
}
while(offset > min_offset);
/* don't bother fixing loop_end_offset since will be adjusted to num_samples later, if needed */
}
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(0x0C,streamFile);
//todo use proper flags
if (read_32bitLE(0x08,streamFile)!=0x10) {
vgmstream->coding_type = coding_PCM16LE;
vgmstream->num_samples = stream_size/2/vgmstream->channels;
} else {
vgmstream->coding_type = coding_PSX;
vgmstream->num_samples = ((stream_size-0x40)/16*28)/vgmstream->channels; //todo don't - 0x40?
}
vgmstream->interleave_block_size = read_32bitLE(0x14,streamFile); /* set even in mono */
vgmstream->sample_rate = sample_rate;
vgmstream->coding_type = coding_type;
vgmstream->interleave_block_size = interleave;
vgmstream->layout_type = layout_interleave;
vgmstream->meta_type = meta_PS2_SShd;
/* loops */
switch(coding_type) {
case coding_PCM16LE:
vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16);
break;
case coding_PSX:
vgmstream->num_samples = ps_bytes_to_samples(stream_size, channel_count);
break;
case coding_DVI_IMA_int:
vgmstream->num_samples = ima_bytes_to_samples(stream_size, channel_count);
break;
default:
goto fail;
}
if (vgmstream->loop_flag) {
if ((loop_end*0x10*vgmstream->channels+0x800) == get_streamfile_size(streamFile)) {
/* full loop? */ //todo needed???
uint8_t testBuffer[0x10];
off_t readOffset = 0, loopEndOffset = 0;
readOffset = (off_t)get_streamfile_size(streamFile)-(4*vgmstream->interleave_block_size);
do {
readOffset += (off_t)read_streamfile(testBuffer,readOffset,0x10,streamFile);
if(testBuffer[0x01]==0x01) {
if(loopEndOffset==0)
loopEndOffset = readOffset-0x10;
break;
}
} while (streamFile->get_offset(streamFile)<(int32_t)get_streamfile_size(streamFile));
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = (loopEndOffset/(vgmstream->interleave_block_size)*vgmstream->interleave_block_size)/16*28;
vgmstream->loop_end_sample += (loopEndOffset%vgmstream->interleave_block_size)/16*28;
vgmstream->loop_end_sample /=vgmstream->channels;
if (is_loop_samples) {
vgmstream->loop_start_sample = loop_start_sample;
vgmstream->loop_end_sample = loop_end_sample;
}
else {
if (loop_end <= vgmstream->num_samples) {
/* assume loops are samples */
vgmstream->loop_start_sample = loop_start;
vgmstream->loop_end_sample = loop_end;
} else {
/* assume loops are addresses (official definition) */ //todo use interleave instead of 0x10?
vgmstream->loop_start_sample = (loop_start*0x10)/16*28/vgmstream->channels;
vgmstream->loop_end_sample = (loop_end*0x10)/16*28/vgmstream->channels;
switch(vgmstream->coding_type) {
case coding_PCM16LE:
vgmstream->loop_start_sample = pcm_bytes_to_samples(loop_start_offset,channel_count,16);
vgmstream->loop_end_sample = pcm_bytes_to_samples(loop_end_offset,channel_count,16);
break;
case coding_PSX:
vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start_offset,channel_count);
vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end_offset,channel_count);
break;
default:
goto fail;
}
}
/* don't know why, but it does happen, in ps2 too :( */ //todo what
/* when loop_end = 0xFFFFFFFF */
if (vgmstream->loop_end_sample == 0)
vgmstream->loop_end_sample = vgmstream->num_samples;
/* happens even when loops are directly samples, loops sound fine (ex. Culdcept) */
if (vgmstream->loop_end_sample > vgmstream->num_samples)
vgmstream->loop_end_sample = vgmstream->num_samples;
}
/* adjust */
start_offset = 0x28;
if ((stream_size * 2) == (get_streamfile_size(streamFile) - 0x18)) {
/* True Fortune (PS2) with weird stream size */ //todo try to move
stream_size = (read_32bitLE(0x24,streamFile) * 2) - 0x10;
vgmstream->num_samples = stream_size / 16 * 28 / vgmstream->channels;
}
else if(get_streamfile_size(streamFile) - read_32bitLE(0x24,streamFile) >= 0x800) {
/* Hack for files with start_offset = 0x800 (ex. Taisho Mononoke Ibunroku) */
start_offset = 0x800;
}
if (vgmstream->coding_type == coding_PSX && start_offset == 0x28) {
int i;
start_offset = 0x800;
for (i=0; i < 0x1f6; i += 4) {
if (read_32bitLE(0x28+(i*4),streamFile)!=0) {
start_offset = 0x28;
break;
}
}
}
//todo should adjust num samples after changing start_offset and stream_size?
/* check if we got a real pcm by checking PS-ADPCM flags (ex: Clock Tower 3) */
//todo check format 0x02 instead
if (vgmstream->coding_type==coding_PCM16LE) {
uint8_t isPCM = 0;
off_t check_offset;
check_offset = start_offset;
do {
if (read_8bit(check_offset+1,streamFile)>7) {
isPCM=1;
break;
}
else {
check_offset+=0x10;
}
} while (check_offset<get_streamfile_size(streamFile));
if (!isPCM) {
vgmstream->num_samples=(get_streamfile_size(streamFile)-start_offset)/16*28/vgmstream->channels;
vgmstream->coding_type=coding_PSX;
}
}
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
@ -170,3 +308,74 @@ fail:
close_vgmstream(vgmstream);
return NULL;
}
/* ****************************************************************************** */
static STREAMFILE* setup_subfile_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext);
/* ADS in containers */
VGMSTREAM * init_vgmstream_ps2_ads_container(STREAMFILE *streamFile) {
VGMSTREAM *vgmstream = NULL;
STREAMFILE *temp_streamFile = NULL;
off_t subfile_offset;
size_t subfile_size;
/* checks */
if (!check_extensions(streamFile, "ads"))
goto fail;
if (read_32bitBE(0x00,streamFile) == 0x41445343 && /* "ADSC" */
read_32bitBE(0x04,streamFile) == 0x01000000) {
/* Kenka Bancho 2, Kamen Rider Hibiki/Kabuto, Shinjuku no Okami */
subfile_offset = 0x08;
}
else if (read_32bitBE(0x00,streamFile) == 0x63617669 && /* "cavi" */
read_32bitBE(0x04,streamFile) == 0x61207374 && /* "a st" */
read_32bitBE(0x08,streamFile) == 0x7265616D) { /* "ream" */
/* cavia games: Drakengard 1/2, Dragon Quest Yangus, GITS: Stand Alone Complex */
subfile_offset = 0x7d8;
}
else {
goto fail;
}
subfile_size = get_streamfile_size(streamFile) - subfile_offset;
temp_streamFile = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL);
if (!temp_streamFile) goto fail;
vgmstream = init_vgmstream_ps2_ads(temp_streamFile);
close_streamfile(temp_streamFile);
return vgmstream;
fail:
close_streamfile(temp_streamFile);
close_vgmstream(vgmstream);
return NULL;
}
static STREAMFILE* setup_subfile_streamfile(STREAMFILE *streamFile, off_t subfile_offset, size_t subfile_size, const char* fake_ext) {
STREAMFILE *temp_streamFile = NULL, *new_streamFile = NULL;
/* setup subfile */
new_streamFile = open_wrap_streamfile(streamFile);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
new_streamFile = open_clamp_streamfile(temp_streamFile, subfile_offset,subfile_size);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
if (fake_ext) {
new_streamFile = open_fakename_streamfile(temp_streamFile, NULL,fake_ext);
if (!new_streamFile) goto fail;
temp_streamFile = new_streamFile;
}
return temp_streamFile;
fail:
close_streamfile(temp_streamFile);
return NULL;
}

View File

@ -411,6 +411,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_dsp_switch_audio,
init_vgmstream_dsp_sadf,
init_vgmstream_h4m,
init_vgmstream_ps2_ads_container,
init_vgmstream_txth, /* should go at the end (lower priority) */
#ifdef VGM_USE_FFMPEG