mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-17 23:36:41 +01:00
commit
9b8fecdf28
@ -208,7 +208,7 @@ static const char* extension_list[] = {
|
||||
//"mpc", //FFmpeg, not parsed (musepack) //common
|
||||
"mpdsp",
|
||||
"mpds",
|
||||
"mps", //txh/reserved
|
||||
"mps", //txh/reserved [Scandal (PS2)]
|
||||
"ms",
|
||||
"msa",
|
||||
"msb",
|
||||
@ -876,7 +876,6 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_DSP_DDSP, ".DDSP header"},
|
||||
{meta_P3D, "Radical P3D header"},
|
||||
{meta_PS2_TK1, "Tekken TK5STRM1 Header"},
|
||||
{meta_PS2_ADSC, "ADSC Header"},
|
||||
{meta_NGC_DSP_MPDS, "MPDS DSP header"},
|
||||
{meta_DSP_STR_IG, "Infogrames .DSP header"},
|
||||
{meta_EA_SWVR, "Electronic Arts SWVR header"},
|
||||
|
@ -42,14 +42,14 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM *
|
||||
break; /* probable infinite loop otherwise */
|
||||
}
|
||||
|
||||
/* samples_this_block = 0 is allowed (empty block), will do nothing then move to next block */
|
||||
|
||||
samples_to_do = vgmstream_samples_to_do(samples_this_block, samples_per_frame, vgmstream);
|
||||
if (samples_written + samples_to_do > sample_count)
|
||||
samples_to_do = sample_count - samples_written;
|
||||
|
||||
if (vgmstream->current_block_offset >= 0) {
|
||||
decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer);
|
||||
/* samples_this_block = 0 is allowed (empty block): do nothing then move to next block */
|
||||
if (samples_to_do > 0)
|
||||
decode_vgmstream(vgmstream, samples_written, samples_to_do, buffer);
|
||||
}
|
||||
else {
|
||||
/* block end signal (used in halpst): partially 0-set buffer */
|
||||
|
@ -2,45 +2,59 @@
|
||||
#include "../coding/coding.h"
|
||||
#include "../vgmstream.h"
|
||||
|
||||
/* set up for the block at the given offset */
|
||||
/* parse EA style blocks, id+size+samples+data */
|
||||
void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
|
||||
int i;
|
||||
int new_schl = 0;
|
||||
size_t block_size = 0, block_samples = 0;
|
||||
size_t block_size, block_samples;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE;
|
||||
size_t file_size = get_streamfile_size(streamFile);
|
||||
|
||||
|
||||
while (block_offset < file_size) {
|
||||
uint32_t id = read_32bitBE(block_offset+0x00,streamFile);
|
||||
/* EOF reads: signal we have nothing and let the layout fail */
|
||||
if (block_offset >= get_streamfile_size(streamFile)) {
|
||||
vgmstream->current_block_offset = block_offset;
|
||||
vgmstream->next_block_offset = block_offset;
|
||||
vgmstream->current_block_samples = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* read a single block */
|
||||
{
|
||||
uint32_t block_id = read_32bitBE(block_offset+0x00,streamFile);
|
||||
|
||||
block_size = read_32bitLE(block_offset+0x04,streamFile);
|
||||
if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */
|
||||
block_size = read_32bitBE(block_offset+0x04,streamFile);
|
||||
|
||||
block_samples = 0;
|
||||
|
||||
if (id == 0x5343446C || id == 0x5344454E || id == 0x53444652) { /* "SCDl" "SDEN" "SDFR" audio data */
|
||||
switch(vgmstream->coding_type) {
|
||||
case coding_PSX:
|
||||
switch(block_id) {
|
||||
case 0x5343446C: /* "SCDl" */
|
||||
case 0x5344454E: /* "SDEN" */
|
||||
case 0x53444652: /* "SDFR" */
|
||||
case 0x53444745: /* "SDGE" */
|
||||
case 0x53444954: /* "SDIT" */
|
||||
case 0x53445350: /* "SDSP" */
|
||||
case 0x53445255: /* "SDRU" */
|
||||
case 0x53444A41: /* "SDJA" */
|
||||
/* audio chunk */
|
||||
if (vgmstream->coding_type == coding_PSX)
|
||||
block_samples = ps_bytes_to_samples(block_size-0x10, vgmstream->channels);
|
||||
break;
|
||||
default:
|
||||
else
|
||||
block_samples = read_32bit(block_offset+0x08,streamFile);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
/* ignore other chunks (audio "SCHl/SCCl/...", video "pIQT/MADk/...", etc) */
|
||||
block_samples = 0; /* layout ignores this */
|
||||
break;
|
||||
}
|
||||
else { /* any other chunk, audio ("SCHl" "SCCl" "SCLl" "SCEl" etc), or video ("pQGT" "pIQT "MADk" etc) */
|
||||
/* padding between "SCEl" and next "SCHl" (when subfiles exist) */
|
||||
if (id == 0x00000000) {
|
||||
block_size = 0x04;
|
||||
}
|
||||
|
||||
if (id == 0x5343486C || id == 0x5348454E || id == 0x53484652) { /* "SCHl" "SHEN" "SHFR" end block */
|
||||
new_schl = 1;
|
||||
}
|
||||
}
|
||||
/* "SCHl" start block (movie "SHxx" shouldn't use multi files) */
|
||||
if (block_id == 0x5343486C)
|
||||
new_schl = 1;
|
||||
|
||||
/* padding between "SCEl" and next "SCHl" (when subfiles exist) */
|
||||
if (block_id == 0x00000000)
|
||||
block_size = 0x04;
|
||||
|
||||
/* guard against errors (happens in bad rips/endianness, observed max is vid ~0x20000) */
|
||||
if (block_size == 0x00 || block_size > 0xFFFFF || block_samples > 0xFFFF) {
|
||||
@ -48,25 +62,12 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
|
||||
block_samples = 0;
|
||||
}
|
||||
|
||||
|
||||
if (block_samples) /* audio found */
|
||||
break;
|
||||
block_offset += block_size;
|
||||
|
||||
/* "SCEl" "SEEN" "SEFR" are aligned to 0x80 usually, but causes problems if not 32b-aligned (ex. Need for Speed 2 PC) */
|
||||
if ((id == 0x5343456C || id == 0x5345454E || id == 0x53454652) && block_offset % 0x04) {
|
||||
block_offset += 0x04 - (block_offset % 0x04);
|
||||
/* "SCEl" end chunk should be 32b-aligned, fixes some multi-SCHl [ex. Need for Speed 2 (PC) .eam] */
|
||||
if (((block_offset + block_size) % 0x04) && block_id == 0x5343456C) {
|
||||
block_size += 0x04 - ((block_offset + block_size) % 0x04);
|
||||
}
|
||||
}
|
||||
|
||||
/* EOF reads: pretend we have samples to please the layout (unsure if this helps) */
|
||||
if (block_offset >= file_size) {
|
||||
vgmstream->current_block_offset = block_offset;
|
||||
vgmstream->next_block_offset = block_offset + 0x04;
|
||||
vgmstream->current_block_samples = vgmstream->num_samples;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* set new channel offsets and ADPCM history */
|
||||
/* ADPCM hist could be considered part of the stream/decoder (some EAXA decoders call it "EAXA R1" when it has hist), and BNKs
|
||||
|
@ -830,10 +830,6 @@
|
||||
RelativePath=".\meta\ps2_ads.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ps2_adsc.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\ps2_ass.c"
|
||||
>
|
||||
|
@ -308,7 +308,6 @@
|
||||
<ClCompile Include="meta\ps2_vds_vdm.c" />
|
||||
<ClCompile Include="meta\ps2_adm.c" />
|
||||
<ClCompile Include="meta\ps2_ads.c" />
|
||||
<ClCompile Include="meta\ps2_adsc.c" />
|
||||
<ClCompile Include="meta\ps2_ass.c" />
|
||||
<ClCompile Include="meta\ps2_ast.c" />
|
||||
<ClCompile Include="meta\aus.c" />
|
||||
|
@ -499,9 +499,6 @@
|
||||
<ClCompile Include="meta\ps2_ads.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps2_adsc.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\ps2_ass.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -73,7 +73,7 @@ typedef struct {
|
||||
|
||||
static int parse_variable_header(STREAMFILE* streamFile, ea_header* ea, off_t begin_offset, int max_length);
|
||||
static uint32_t read_patch(STREAMFILE* streamFile, off_t* offset);
|
||||
static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea);
|
||||
static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream);
|
||||
static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea);
|
||||
static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_header *ea, off_t start_offset, int is_bnk, int total_streams);
|
||||
|
||||
@ -93,11 +93,16 @@ VGMSTREAM * init_vgmstream_ea_schl(STREAMFILE *streamFile) {
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x5343486C && /* "SCHl" */
|
||||
read_32bitBE(0x00,streamFile) != 0x5348454E && /* "SHEN" */
|
||||
read_32bitBE(0x00,streamFile) != 0x53484652) /* "SHFR" */
|
||||
read_32bitBE(0x00,streamFile) != 0x53484652 && /* "SHFR" */
|
||||
read_32bitBE(0x00,streamFile) != 0x53484745 && /* "SHGE" */
|
||||
read_32bitBE(0x00,streamFile) != 0x53484954 && /* "SHIT" */
|
||||
read_32bitBE(0x00,streamFile) != 0x53485350 && /* "SHSP" */
|
||||
read_32bitBE(0x00,streamFile) != 0x53485255 && /* "SHRU" */
|
||||
read_32bitBE(0x00,streamFile) != 0x53484A41) /* "SHJA" */
|
||||
goto fail;
|
||||
|
||||
/* stream is divided into blocks/chunks: SCHl=audio header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=end.
|
||||
* Video uses various blocks (MVhd/MV0K/etc) and sometimes alt audio blocks (SHxx/SCxx/SDxx/SExx where XX=language, EN/FR).
|
||||
/* Stream is divided into blocks/chunks: SCHl=audio header, SCCl=count of SCDl, SCDl=data xN, SCLl=loop end, SCEl=end.
|
||||
* Video uses picture blocks (MVhd/MV0K/etc) and sometimes multiaudio blocks (SHxx/SCxx/SDxx/SExx where xx=language=EN/FR/GE/IT/SP/RU/JA).
|
||||
* The number/size is affected by: block rate setting, sample rate, channels, CPU location (SPU/main/DSP/others), etc */
|
||||
|
||||
header_size = read_32bitLE(0x04,streamFile);
|
||||
@ -362,13 +367,6 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
|
||||
/* fix num_samples for streams with multiple SCHl */
|
||||
if (!is_bnk) {
|
||||
int total_samples = get_ea_stream_total_samples(streamFile, start_offset, ea);
|
||||
if (total_samples > vgmstream->num_samples)
|
||||
vgmstream->num_samples = total_samples;
|
||||
}
|
||||
|
||||
|
||||
if (is_bnk) {
|
||||
/* setup channel offsets */
|
||||
@ -411,6 +409,11 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* fix num_samples for streams with multiple SCHl */
|
||||
int total_samples = get_ea_stream_total_samples(streamFile, start_offset, vgmstream);
|
||||
if (total_samples > vgmstream->num_samples)
|
||||
vgmstream->num_samples = total_samples;
|
||||
|
||||
/* setup first block to update offsets */
|
||||
block_update_ea_schl(start_offset,vgmstream);
|
||||
}
|
||||
@ -755,60 +758,22 @@ fail:
|
||||
/* Get total samples by parsing block headers, needed when multiple files are stitched together.
|
||||
* Some EA files (.mus/eam/sng/etc) concat many small subfiles, used for interactive/mapped
|
||||
* music (.map/lin). Subfiles always share header, except num_samples. */
|
||||
static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, const ea_header* ea) {
|
||||
static int get_ea_stream_total_samples(STREAMFILE* streamFile, off_t start_offset, VGMSTREAM* vgmstream) {
|
||||
int num_samples = 0;
|
||||
int new_schl = 0;
|
||||
off_t block_offset = start_offset;
|
||||
size_t block_size, block_samples;
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE;
|
||||
size_t file_size = get_streamfile_size(streamFile);
|
||||
|
||||
|
||||
while (block_offset < file_size) {
|
||||
uint32_t id = read_32bitBE(block_offset+0x00,streamFile);
|
||||
|
||||
block_size = read_32bitLE(block_offset+0x04,streamFile);
|
||||
if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */
|
||||
block_size = read_32bitBE(block_offset+0x04,streamFile);
|
||||
|
||||
block_samples = 0;
|
||||
|
||||
if (id == 0x5343446C || id == 0x5344454E || id == 0x53444652) { /* "SCDl" "SDEN" "SDFR" audio data */
|
||||
switch (ea->codec2) {
|
||||
case EA_CODEC2_VAG:
|
||||
block_samples = ps_bytes_to_samples(block_size-0x10, ea->channels);
|
||||
break;
|
||||
default:
|
||||
block_samples = read_32bit(block_offset+0x08,streamFile);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else { /* any other chunk, audio ("SCHl" "SCCl" "SCLl" "SCEl" etc), or video ("pQGT" "pIQT "MADk" "MPCh" etc) */
|
||||
/* padding between "SCEl" and next "SCHl" (when subfiles exist) */
|
||||
if (id == 0x00000000) {
|
||||
block_size = 0x04;
|
||||
}
|
||||
|
||||
if (id == 0x5343486C || id == 0x5348454E || id == 0x53484652) { /* "SCHl" "SHEN" "SHFR" end block */
|
||||
/* calc num_samples as playable data size varies between files/blocks */
|
||||
{
|
||||
vgmstream->next_block_offset = start_offset;
|
||||
do {
|
||||
uint32_t block_id = read_32bitBE(vgmstream->next_block_offset+0x00,streamFile);
|
||||
if (block_id == 0x5343486C) /* "SCHl" start block (movie "SHxx" shouldn't use multi files) */
|
||||
new_schl = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* guard against errors (happens in bad rips/endianness, observed max is vid ~0x20000) */
|
||||
if (block_size == 0x00 || block_size > 0xFFFFF || block_samples > 0xFFFF) {
|
||||
VGM_LOG("EA SCHl: bad block size %x at %lx\n", block_size, block_offset);
|
||||
block_size = 0x04;
|
||||
block_samples = 0;
|
||||
}
|
||||
|
||||
num_samples += block_samples;
|
||||
block_offset += block_size;
|
||||
|
||||
/* "SCEl" "SEEN" "SEFR" are aligned to 0x80 usually, but causes problems if not 32b-aligned (ex. Need for Speed 2 PC) */
|
||||
if ((id == 0x5343456C || id == 0x5345454E || id == 0x53454652) && block_offset % 0x04) {
|
||||
VGM_LOG_ONCE("EA SCHl: mis-aligned end offset found\n");
|
||||
block_offset += 0x04 - (block_offset % 0x04);
|
||||
block_update_ea_schl(vgmstream->next_block_offset,vgmstream);
|
||||
num_samples += vgmstream->current_block_samples;
|
||||
}
|
||||
while (vgmstream->next_block_offset < get_streamfile_size(streamFile));
|
||||
}
|
||||
|
||||
/* only use calculated samples with multiple subfiles (rarely header samples may be less due to padding) */
|
||||
@ -828,22 +793,31 @@ static off_t get_ea_stream_mpeg_start_offset(STREAMFILE* streamFile, off_t start
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE;
|
||||
|
||||
while (block_offset < file_size) {
|
||||
uint32_t id, block_size;
|
||||
uint32_t block_id, block_size;
|
||||
off_t offset;
|
||||
|
||||
id = read_32bitBE(block_offset+0x00,streamFile);
|
||||
block_id = read_32bitBE(block_offset+0x00,streamFile);
|
||||
|
||||
block_size = read_32bitLE(block_offset+0x04,streamFile);
|
||||
if (block_size > 0x00F00000) /* size is always LE, except in early SAT/MAC */
|
||||
block_size = read_32bitBE(block_offset+0x04,streamFile);
|
||||
|
||||
if (id == 0x5343446C || id == 0x5344454E || id == 0x53444652) { /* "SCDl/SDEN/SDFR" data block found */
|
||||
off_t offset = read_32bit(block_offset+0x0c,streamFile); /* first value seems ok, second is something else in EALayer3 */
|
||||
return block_offset + 0x0c + ea->channels*0x04 + offset;
|
||||
} else if (id == 0x5343436C || id == 0x5343454E || id == 0x53434652) { /* "SCCl/SCEN/SCFR" data count found */
|
||||
block_offset += block_size; /* size includes header */
|
||||
continue;
|
||||
} else {
|
||||
goto fail;
|
||||
switch(block_id) {
|
||||
case 0x5343446C: /* "SCDl" */
|
||||
case 0x5344454E: /* "SDEN" */
|
||||
case 0x53444652: /* "SDFR" */
|
||||
case 0x53444745: /* "SDGE" */
|
||||
case 0x53444954: /* "SDIT" */
|
||||
case 0x53445350: /* "SDSP" */
|
||||
case 0x53445255: /* "SDRU" */
|
||||
case 0x53444A41: /* "SDJA" */
|
||||
offset = read_32bit(block_offset+0x0c,streamFile); /* first value seems ok, second is something else in EALayer3 */
|
||||
return block_offset + 0x0c + ea->channels*0x04 + offset;
|
||||
case 0x00000000:
|
||||
goto fail; /* just in case */
|
||||
default:
|
||||
block_offset += block_size; /* size includes header */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,17 +126,17 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
|
||||
|
||||
/* sample header (first stream only, not sure if there are multi-FSB1) */
|
||||
{
|
||||
off_t s_off = fsb.base_header_size;
|
||||
off_t header_offset = fsb.base_header_size;
|
||||
|
||||
fsb.name_offset = s_off;
|
||||
fsb.name_offset = header_offset;
|
||||
fsb.name_size = 0x20;
|
||||
fsb.num_samples = read_32bitLE(s_off+0x20,streamFile);
|
||||
fsb.stream_size = read_32bitLE(s_off+0x24,streamFile);
|
||||
fsb.sample_rate = read_32bitLE(s_off+0x28,streamFile);
|
||||
fsb.num_samples = read_32bitLE(header_offset+0x20,streamFile);
|
||||
fsb.stream_size = read_32bitLE(header_offset+0x24,streamFile);
|
||||
fsb.sample_rate = read_32bitLE(header_offset+0x28,streamFile);
|
||||
/* 0x2c:? 0x2e:? 0x30:? 0x32:? */
|
||||
fsb.mode = read_32bitLE(s_off+0x34,streamFile);
|
||||
fsb.loop_start = read_32bitLE(s_off+0x38,streamFile);
|
||||
fsb.loop_end = read_32bitLE(s_off+0x3c,streamFile);
|
||||
fsb.mode = read_32bitLE(header_offset+0x34,streamFile);
|
||||
fsb.loop_start = read_32bitLE(header_offset+0x38,streamFile);
|
||||
fsb.loop_end = read_32bitLE(header_offset+0x3c,streamFile);
|
||||
|
||||
fsb.channels = (fsb.mode & FSOUND_STEREO) ? 2 : 1;
|
||||
if (fsb.loop_end > fsb.num_samples) /* this seems common... */
|
||||
@ -191,42 +191,55 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
|
||||
/* sample header (N-stream) */
|
||||
{
|
||||
int i;
|
||||
off_t s_off = fsb.base_header_size;
|
||||
off_t d_off = fsb.base_header_size + fsb.sample_header_size;
|
||||
off_t header_offset = fsb.base_header_size;
|
||||
off_t data_offset = fsb.base_header_size + fsb.sample_header_size;
|
||||
|
||||
/* find target_stream header (variable sized) */
|
||||
for (i = 0; i < fsb.total_subsongs; i++) {
|
||||
size_t stream_header_size = (uint16_t)read_16bitLE(s_off+0x00,streamFile);
|
||||
fsb.name_offset = s_off+0x02;
|
||||
fsb.name_size = 0x20-0x02;
|
||||
fsb.num_samples = read_32bitLE(s_off+0x20,streamFile);
|
||||
fsb.stream_size = read_32bitLE(s_off+0x24,streamFile);
|
||||
fsb.loop_start = read_32bitLE(s_off+0x28,streamFile);
|
||||
fsb.loop_end = read_32bitLE(s_off+0x2c,streamFile);
|
||||
fsb.mode = read_32bitLE(s_off+0x30,streamFile);
|
||||
fsb.sample_rate = read_32bitLE(s_off+0x34,streamFile);
|
||||
/* 0x38:defvol 0x3a:defpan 0x3c:defpri */
|
||||
fsb.channels = read_16bitLE(s_off+0x3e,streamFile);
|
||||
/* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsb.varpan */
|
||||
/* FSB3/4: 0x50:extended_data size_32bits (not always given) */
|
||||
size_t stream_header_size;
|
||||
|
||||
if (i+1 == target_subsong) /* d_off found */
|
||||
if ((fsb.flags & FMOD_FSB_SOURCE_BASICHEADERS) && i > 0) {
|
||||
/* miniheader, all subsongs reuse first header [rare, ex. Biker Mice from Mars (PS2)] */
|
||||
stream_header_size = 0x08;
|
||||
fsb.num_samples = read_32bitLE(header_offset+0x00,streamFile);
|
||||
fsb.stream_size = read_32bitLE(header_offset+0x04,streamFile);
|
||||
fsb.loop_start = 0;
|
||||
fsb.loop_end = 0;
|
||||
}
|
||||
else {
|
||||
/* subsong header for normal files */
|
||||
stream_header_size = (uint16_t)read_16bitLE(header_offset+0x00,streamFile);
|
||||
fsb.name_offset = header_offset+0x02;
|
||||
fsb.name_size = 0x20-0x02;
|
||||
fsb.num_samples = read_32bitLE(header_offset+0x20,streamFile);
|
||||
fsb.stream_size = read_32bitLE(header_offset+0x24,streamFile);
|
||||
fsb.loop_start = read_32bitLE(header_offset+0x28,streamFile);
|
||||
fsb.loop_end = read_32bitLE(header_offset+0x2c,streamFile);
|
||||
fsb.mode = read_32bitLE(header_offset+0x30,streamFile);
|
||||
fsb.sample_rate = read_32bitLE(header_offset+0x34,streamFile);
|
||||
/* 0x38:defvol 0x3a:defpan 0x3c:defpri */
|
||||
fsb.channels = read_16bitLE(header_offset+0x3e,streamFile);
|
||||
/* FSB3.1/4: 0x40:mindistance 0x44:maxdistance 0x48:varfreq/size_32bits 0x4c:varvol 0x4e:fsb.varpan */
|
||||
/* FSB3/4: 0x50:extended_data size_32bits (not always given) */
|
||||
}
|
||||
|
||||
if (i+1 == target_subsong) /* final data_offset found */
|
||||
break;
|
||||
|
||||
s_off += stream_header_size;
|
||||
d_off += fsb.stream_size; /* there is no offset so manually count */
|
||||
header_offset += stream_header_size;
|
||||
data_offset += fsb.stream_size; /* there is no offset so manually count */
|
||||
|
||||
/* some subsongs offsets need padding (most FSOUND_IMAADPCM, few MPEG too [Hard Reset (PC) subsong 5])
|
||||
* other PADDED4 may set it (ex. XMA) but don't seem to use it and work fine */
|
||||
if (fsb.flags & FMOD_FSB_SOURCE_MPEG_PADDED4) {
|
||||
if (d_off % 0x20)
|
||||
d_off += 0x20 - (d_off % 0x20);
|
||||
if (data_offset % 0x20)
|
||||
data_offset += 0x20 - (data_offset % 0x20);
|
||||
}
|
||||
}
|
||||
if (i > fsb.total_subsongs) goto fail; /* not found */
|
||||
|
||||
start_offset = d_off;
|
||||
custom_data_offset = s_off + fsb.sample_header_min; /* DSP coefs, seek tables, etc */
|
||||
start_offset = data_offset;
|
||||
custom_data_offset = header_offset + fsb.sample_header_min; /* DSP coefs, seek tables, etc */
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,7 +260,7 @@ VGMSTREAM * init_vgmstream_fsb(STREAMFILE *streamFile) {
|
||||
&& fsb.num_samples < 20*fsb.sample_rate) /* seconds, lame but no other way to know */
|
||||
loop_flag = 0;
|
||||
|
||||
/* ping-pong looping = no looping? (forward > reverse > forward) */
|
||||
/* ping-pong looping = no looping? (forward > reverse > forward) [ex. Biker Mice from Mars (PS2)] */
|
||||
VGM_ASSERT(fsb.mode & FSOUND_LOOP_BIDI, "FSB BIDI looping found\n");
|
||||
|
||||
|
||||
|
@ -38,6 +38,7 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_ngc_dsp_csmp(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_ads(STREAMFILE *streamFile);
|
||||
VGMSTREAM * init_vgmstream_ps2_ads_container(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_npsf(STREAMFILE *streamFile);
|
||||
|
||||
@ -483,8 +484,6 @@ VGMSTREAM * init_vgmstream_dsp_ddsp(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_p3d(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_adsc(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ngc_dsp_mpds(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_dsp_str_ig(STREAMFILE* streamFile);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1,63 +0,0 @@
|
||||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* ADSC (from Kenka Bancho 2: Full Throttle) */
|
||||
VGMSTREAM * init_vgmstream_ps2_adsc(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
off_t start_offset;
|
||||
|
||||
int loop_flag;
|
||||
int channel_count;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("ads",filename_extension(filename))) goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x41445343) /* ADSC */
|
||||
goto fail;
|
||||
|
||||
loop_flag = 0;
|
||||
channel_count = read_32bitLE(0x18,streamFile);
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
start_offset = 0x1000;
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = read_32bitLE(0x14,streamFile);
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
if(read_32bitLE(0x18,streamFile)==0x01)
|
||||
vgmstream->num_samples = read_32bitLE(0x2c,streamFile)*56/32;
|
||||
else
|
||||
vgmstream->num_samples = read_32bitLE(0x2c,streamFile)*28/32;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = read_32bitLE(0x1c,streamFile);
|
||||
vgmstream->meta_type = meta_PS2_ADSC;
|
||||
|
||||
/* open the file for reading */
|
||||
{
|
||||
int i;
|
||||
STREAMFILE * file;
|
||||
file = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!file) goto fail;
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = file;
|
||||
|
||||
vgmstream->ch[i].channel_start_offset=
|
||||
vgmstream->ch[i].offset=start_offset+
|
||||
vgmstream->interleave_block_size*i;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -64,8 +64,9 @@ VGMSTREAM * init_vgmstream_ubi_lyn(STREAMFILE *streamFile) {
|
||||
}
|
||||
}
|
||||
|
||||
/* most songs simply repeat, loop if it looks long enough */
|
||||
loop_flag = (num_samples > 20*sample_rate); /* in seconds */
|
||||
/* most songs simply repeat; loop if it looks long enough,
|
||||
* but not too long (ex. Michael Jackson The Experience songs) */
|
||||
loop_flag = (num_samples > 20*sample_rate && num_samples < 60*3*sample_rate); /* in seconds */
|
||||
start_offset = data_offset;
|
||||
|
||||
|
||||
@ -246,6 +247,10 @@ VGMSTREAM * init_vgmstream_ubi_lyn_container(STREAMFILE *streamFile) {
|
||||
read_32bitBE(0x14,streamFile) == 0x52494646) { /* "RIFF" */
|
||||
subfile_offset = 0x14; /* Adventures of Tintin */
|
||||
}
|
||||
else if (read_32bitBE(0x20,streamFile) == 0x4C795345 && /* "LySE" */
|
||||
read_32bitBE(0x34,streamFile) == 0x52494646) { /* "RIFF" */
|
||||
subfile_offset = 0x34; /* Michael Jackson The Experience (Wii) */
|
||||
}
|
||||
else if (read_32bitLE(0x00,streamFile)+0x20 == get_streamfile_size(streamFile) &&
|
||||
read_32bitBE(0x20,streamFile) == 0x52494646) { /* "RIFF" */
|
||||
subfile_offset = 0x20; /* Red Steel 2, From Dust */
|
||||
|
@ -417,7 +417,8 @@ VGMSTREAM * init_vgmstream_wwise(STREAMFILE *streamFile) {
|
||||
off_t xma2_offset;
|
||||
size_t xma2_size;
|
||||
|
||||
if (ww.fmt_size != 0x20 && ww.fmt_size != 0x34 && ww.fmt_size != 0x40) goto fail; /* XMA1, XMA2old, XMA2new */
|
||||
/* endian check should be enough */
|
||||
//if (ww.fmt_size != ...) goto fail; /* XMA1 0x20, XMA2old: 0x34, XMA2new: 0x40, XMA2 Guitar Hero Live/padded: 0x64, etc */
|
||||
if (!ww.big_endian) goto fail; /* must be Wwise (real XMA are LE and parsed elsewhere) */
|
||||
|
||||
if (find_chunk(streamFile, 0x584D4132,first_offset,0, &xma2_offset,&xma2_size, ww.big_endian, 0)) { /*"XMA2"*/ /* older Wwise */
|
||||
|
@ -263,7 +263,6 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_dsp_ddsp,
|
||||
init_vgmstream_p3d,
|
||||
init_vgmstream_ps2_tk1,
|
||||
init_vgmstream_ps2_adsc,
|
||||
init_vgmstream_ngc_dsp_mpds,
|
||||
init_vgmstream_dsp_str_ig,
|
||||
init_vgmstream_ea_swvr,
|
||||
@ -411,6 +410,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
|
||||
|
@ -534,7 +534,6 @@ typedef enum {
|
||||
meta_PC_SMP, /* Ghostbusters PC .smp */
|
||||
meta_P3D, /* Prototype P3D */
|
||||
meta_PS2_TK1, /* Tekken (NamCollection) */
|
||||
meta_PS2_ADSC, /* Kenka Bancho 2: Full Throttle */
|
||||
meta_NGC_RKV, /* Legacy of Kain - Blood Omen 2 (GC) */
|
||||
meta_DSP_DDSP, /* Various (2 dsp files stuck together */
|
||||
meta_NGC_DSP_MPDS, /* Big Air Freestyle, Terminator 3 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user