Fix EA SNU looping not properly saving block state

This commit is contained in:
bnnm 2017-11-26 01:25:27 +01:00
parent db3af4d407
commit 2889765c5c
3 changed files with 71 additions and 51 deletions

View File

@ -6,9 +6,9 @@
/* .SNU - from EA Redwood Shores/Visceral games (Dead Space, Dante's Inferno, The Godfather 2) */
VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
int channel_count, loop_flag = 0, channel_config, codec, sample_rate, flags;
int channel_count, loop_flag = 0, version, codec, channel_config, sample_rate, flags;
uint32_t num_samples, loop_start = 0, loop_end = 0;
off_t start_offset;
off_t start_offset, header_offset;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
@ -16,7 +16,7 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
if (!check_extensions(streamFile,"snu"))
goto fail;
/* check header (the first 0x10 are BE/LE depending on platform) */
/* EA SNU header (BE/LE depending on platform) */
/* 0x00(1): related to sample rate? (03=48000)
* 0x01(1): flags/count? (when set has extra block data before start_offset)
* 0x02(1): always 0?
@ -32,32 +32,48 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
read_32bit = read_32bitLE;
}
start_offset = read_32bit(0x08,streamFile);
header_offset = 0x10; /* points to EA SNH/SPH header */
start_offset = read_32bit(0x08,streamFile); /* points to EA SNS/SPS blocks */
codec = read_8bit(0x10,streamFile);
channel_config = read_8bit(0x11,streamFile);
sample_rate = (uint16_t)read_16bitBE(0x12,streamFile);
flags = (uint8_t)read_8bit(0x14,streamFile); /* upper nibble only? */
num_samples = (uint32_t)read_32bitBE(0x14,streamFile) & 0x00FFFFFF;
/* 0x18: null?, 0x1c: null? */
if (flags != 0x60 && flags != 0x40) {
VGM_LOG("EA SNS: unknown flag\n");
/* Beyond is the newest EA header (from EAAudioCore library) still generated by sx.exe.
* Its audio "assets" come in separate RAM headers (.SNR/SPH) and raw blocked streams (.SNS/SPS),
* or together in pseudoformats (.SNU, .SBR+.SBS banks, .AEMS, .MUS, etc).
* Some .SNR include stream data, while most (all?) .SPS have headers so .SPH is optional. */
/* EA SNR header */
version = (read_8bit(header_offset + 0x00,streamFile) >> 4) & 0xf;
codec = (read_8bit(header_offset + 0x00,streamFile) >> 0) & 0xf;
channel_config = read_8bit(header_offset + 0x01,streamFile);
sample_rate = (uint16_t)read_16bitBE(header_offset + 0x02,streamFile);
flags = (uint8_t)read_8bit(header_offset + 0x04,streamFile); /* upper nibble only? */
num_samples = (uint32_t)read_32bitBE(header_offset + 0x04,streamFile) & 0x00FFFFFF;
/* optional, in some headers:
* 0x08: null?
* 0x0c: varies (ex. null, full size) */
/* headered .SPS start with an id of 0x480000xx (not present in .SPH = not part of the header) */
/* V0: SNR+SNS, V1: SPR+SPS (not apparent differences) */
if (version != 0 && version != 1) {
VGM_LOG("EA SNS/SPS: unknown version\n");
goto fail;
}
#if 0
//todo not working ok with blocks in XAS
//todo check if EA-XMA loops (Dante's Inferno doesn't)
if (flags & 0x60) { /* full loop, seen in ambient tracks */
/* & 0x40: stream asset?, 0x20: full loop?, 0x00: RAM asset?, 0x01: loop? */
if (flags != 0x60 && flags != 0x40) {
VGM_LOG("EA SNS/SPS: unknown flag 0x%02x\n", flags);
}
/* full loop seen in Dead Space ambient tracks */
if (flags == 0x60) {
VGM_LOG("flg=%x\n",flags);
loop_flag = 1;
loop_start = 0;
loop_end = num_samples;
}
#endif
//channel_count = (channel_config >> 2) + 1; //todo test
/* 01/02/03 = 1 ch?, 05/06/07 = 2/3 ch?, 0d/0e/0f = 4/5 ch?, 15/16/17 = 6/7 ch?, 1d/1e/1f = 8 ch? */
/* accepted channel configs only seem to be mono/stereo/quad/5.1/7.1 */
//channel_count = ((channel_config >> 2) & 0xf) + 1;
switch(channel_config) {
case 0x00: channel_count = 1; break;
case 0x04: channel_count = 2; break;
@ -65,7 +81,7 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
case 0x14: channel_count = 6; break;
case 0x1c: channel_count = 8; break;
default:
VGM_LOG("EA SNU: unknown channel config 0x%02x\n", channel_config);
VGM_LOG("EA SNS/SPS: unknown channel config 0x%02x\n", channel_config);
goto fail;
}
@ -81,6 +97,7 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
vgmstream->meta_type = meta_EA_SNU;
/* EA decoder list and known internal FourCCs */
switch(codec) {
case 0x04: /* "Xas1": EA-XAS (Dead Space PC/PS3) */
vgmstream->coding_type = coding_EA_XAS;
@ -89,7 +106,7 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
#if 0
#ifdef VGM_USE_MPEG
case 0x07: { /* "EL32S": EALayer3 v2 "S" (Dante's Inferno PS3) */
case 0x07: { /* "L32S": EALayer3 v2 "S" (Dante's Inferno PS3) */
mpeg_custom_config cfg;
off_t mpeg_start_offset = start_offset + 0x08;
@ -133,19 +150,23 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
break;
}
#endif
case 0x00: /* "NONE" */
case 0x01: /* not used? */
case 0x02: /* "P6B0": PCM16BE */
case 0x00: /* "NONE" (internal codec not set flag) */
case 0x01: /* not used/reserved? MP30/P6L0/P2B0/P2L0/P8S0/P8U0/PFN0? */
case 0x02: /* "P6B0": PCM16BE */
case 0x05: /* "EL31": EALayer3 v1 b (with PCM blocks in normal EA-frames?) */
case 0x06: /* "EL32P": EALayer3 v2 "P" */
case 0x09: /* EASpeex? */
case 0x0c: /* EAOpus? */
case 0x0e: /* XAS variant? */
case 0x0f: /* EALayer3 variant? */
/* also 0x1n variations, used in other headers */
case 0x06: /* "L32P": EALayer3 v2 "P" */
case 0x08: /* ? */
case 0x09: /* EASpeex? "Esp0" (libspeex variant) */
case 0x0a: /* EATrax (ATRAC9 variant, deflated frames) */
case 0x0b: /* ? */
case 0x0c: /* EAOpus (inside each SNS/SPS block is 16b frame size + Opus standard? packet) */
case 0x0d: /* ? */
case 0x0e: /* ? */
case 0x0f: /* ? */
default:
VGM_LOG("EA SNU: unknown codec 0x%02x\n", codec);
VGM_LOG("EA SNS/SPS: unknown codec 0x%02x\n", codec);
goto fail;
}

View File

@ -435,16 +435,17 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) {
#ifdef VGM_DEBUG_OUTPUT
#ifdef VGM_USE_FFMPEG
/* debug fun */
{
if (vgmstream->coding_type != coding_FFmpeg){
int i = 0;
/* probable segfault but some layouts/codecs could ignore these */
/* probable segfault but some layouts/codecs can ignore these */
for (i = 0; i < vgmstream->channels; i++) {
VGM_ASSERT(vgmstream->ch[i].streamfile == NULL, "VGMSTREAM: null streamfile in ch%i\n",i);
}
}
#endif
#endif/*VGM_DEBUG_OUTPUT*/
@ -1970,6 +1971,7 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
vgmstream->current_sample = vgmstream->loop_sample;
vgmstream->samples_into_block = vgmstream->loop_samples_into_block;
vgmstream->current_block_size = vgmstream->loop_block_size;
vgmstream->current_block_samples = vgmstream->loop_block_samples;
vgmstream->current_block_offset = vgmstream->loop_block_offset;
vgmstream->next_block_offset = vgmstream->loop_next_block_offset;
@ -1985,6 +1987,7 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
vgmstream->loop_sample = vgmstream->current_sample;
vgmstream->loop_samples_into_block = vgmstream->samples_into_block;
vgmstream->loop_block_size = vgmstream->current_block_size;
vgmstream->loop_block_samples = vgmstream->current_block_samples;
vgmstream->loop_block_offset = vgmstream->current_block_offset;
vgmstream->loop_next_block_offset = vgmstream->next_block_offset;
vgmstream->hit_loop = 1;

View File

@ -717,40 +717,36 @@ typedef struct {
char stream_name[STREAM_NAME_SIZE]; /* name of the current stream (info), if the file stores it and it's filled */
/* looping */
int loop_flag; /* is this stream looped? */
int32_t loop_start_sample; /* first sample of the loop (included in the loop) */
int32_t loop_end_sample; /* last sample of the loop (not included in the loop) */
int loop_flag; /* is this stream looped? */
int32_t loop_start_sample; /* first sample of the loop (included in the loop) */
int32_t loop_end_sample; /* last sample of the loop (not included in the loop) */
/* channels */
VGMSTREAMCHANNEL * ch; /* pointer to array of channels */
/* layouts/block */
size_t interleave_block_size; /* interleave for this file */
size_t interleave_smallblock_size; /* smaller interleave for last block */
size_t full_block_size; /* fixed data size, from header (may include padding and other unusable data) */
/* channel copies */
/* channel state */
VGMSTREAMCHANNEL * ch; /* pointer to array of channels */
VGMSTREAMCHANNEL * start_ch; /* copies of channel status as they were at the beginning of the stream */
VGMSTREAMCHANNEL * loop_ch; /* copies of channel status as they were at the loop point */
/* layout-specific */
/* layout/block state */
int32_t current_sample; /* number of samples we've passed */
int32_t samples_into_block; /* number of samples into the current block */
/* interleave */
size_t interleave_block_size; /* interleave for this file */
size_t interleave_smallblock_size; /* smaller interleave for last block */
/* headered blocks */
off_t current_block_offset; /* start of this block (offset of block header) */
size_t current_block_size; /* size in usable bytes of the block we're in now (used to calculate num_samples per block) */
size_t current_block_samples; /* size in samples of the block we're in now (used over current_block_size if possible) */
size_t full_block_size; /* size including padding and other unusable data */
off_t next_block_offset; /* offset of header of the next block */
int block_count; /* count of "semi" block in total block */
/* loop layout (saved values) */
/* layout/block loop state */
int32_t loop_sample; /* saved from current_sample, should be loop_start_sample... */
int32_t loop_samples_into_block;/* saved from samples_into_block */
off_t loop_block_offset; /* saved from current_block_offset */
size_t loop_block_size; /* saved from current_block_size */
size_t loop_block_samples; /* saved from current_block_samples */
off_t loop_next_block_offset; /* saved from next_block_offset */
/* loop internals */
/* loop state */
int hit_loop; /* have we seen the loop yet? */
/* counters for "loop + play end of the stream instead of fading" (not used/needed otherwise) */
int loop_count; /* number of complete loops (1=looped once) */