mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-18 07:44:43 +01:00
Fix EA SNU looping not properly saving block state
This commit is contained in:
parent
db3af4d407
commit
2889765c5c
@ -6,9 +6,9 @@
|
|||||||
/* .SNU - from EA Redwood Shores/Visceral games (Dead Space, Dante's Inferno, The Godfather 2) */
|
/* .SNU - from EA Redwood Shores/Visceral games (Dead Space, Dante's Inferno, The Godfather 2) */
|
||||||
VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
|
VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
|
||||||
VGMSTREAM * vgmstream = NULL;
|
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;
|
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;
|
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
||||||
|
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
|
|||||||
if (!check_extensions(streamFile,"snu"))
|
if (!check_extensions(streamFile,"snu"))
|
||||||
goto fail;
|
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)
|
/* 0x00(1): related to sample rate? (03=48000)
|
||||||
* 0x01(1): flags/count? (when set has extra block data before start_offset)
|
* 0x01(1): flags/count? (when set has extra block data before start_offset)
|
||||||
* 0x02(1): always 0?
|
* 0x02(1): always 0?
|
||||||
@ -32,32 +32,48 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
|
|||||||
read_32bit = read_32bitLE;
|
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) {
|
/* Beyond is the newest EA header (from EAAudioCore library) still generated by sx.exe.
|
||||||
VGM_LOG("EA SNS: unknown flag\n");
|
* 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;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
/* & 0x40: stream asset?, 0x20: full loop?, 0x00: RAM asset?, 0x01: loop? */
|
||||||
//todo not working ok with blocks in XAS
|
if (flags != 0x60 && flags != 0x40) {
|
||||||
//todo check if EA-XMA loops (Dante's Inferno doesn't)
|
VGM_LOG("EA SNS/SPS: unknown flag 0x%02x\n", flags);
|
||||||
if (flags & 0x60) { /* full loop, seen in ambient tracks */
|
}
|
||||||
|
|
||||||
|
/* full loop seen in Dead Space ambient tracks */
|
||||||
|
if (flags == 0x60) {
|
||||||
|
VGM_LOG("flg=%x\n",flags);
|
||||||
loop_flag = 1;
|
loop_flag = 1;
|
||||||
loop_start = 0;
|
loop_start = 0;
|
||||||
loop_end = num_samples;
|
loop_end = num_samples;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
//channel_count = (channel_config >> 2) + 1; //todo test
|
/* accepted channel configs only seem to be mono/stereo/quad/5.1/7.1 */
|
||||||
/* 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? */
|
//channel_count = ((channel_config >> 2) & 0xf) + 1;
|
||||||
switch(channel_config) {
|
switch(channel_config) {
|
||||||
case 0x00: channel_count = 1; break;
|
case 0x00: channel_count = 1; break;
|
||||||
case 0x04: channel_count = 2; 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 0x14: channel_count = 6; break;
|
||||||
case 0x1c: channel_count = 8; break;
|
case 0x1c: channel_count = 8; break;
|
||||||
default:
|
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;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,6 +97,7 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
|
|||||||
|
|
||||||
vgmstream->meta_type = meta_EA_SNU;
|
vgmstream->meta_type = meta_EA_SNU;
|
||||||
|
|
||||||
|
/* EA decoder list and known internal FourCCs */
|
||||||
switch(codec) {
|
switch(codec) {
|
||||||
case 0x04: /* "Xas1": EA-XAS (Dead Space PC/PS3) */
|
case 0x04: /* "Xas1": EA-XAS (Dead Space PC/PS3) */
|
||||||
vgmstream->coding_type = coding_EA_XAS;
|
vgmstream->coding_type = coding_EA_XAS;
|
||||||
@ -89,7 +106,7 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
|
|||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
#ifdef VGM_USE_MPEG
|
#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;
|
mpeg_custom_config cfg;
|
||||||
off_t mpeg_start_offset = start_offset + 0x08;
|
off_t mpeg_start_offset = start_offset + 0x08;
|
||||||
|
|
||||||
@ -133,19 +150,23 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#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 0x05: /* "EL31": EALayer3 v1 b (with PCM blocks in normal EA-frames?) */
|
||||||
case 0x06: /* "EL32P": EALayer3 v2 "P" */
|
case 0x06: /* "L32P": EALayer3 v2 "P" */
|
||||||
case 0x09: /* EASpeex? */
|
|
||||||
case 0x0c: /* EAOpus? */
|
case 0x08: /* ? */
|
||||||
case 0x0e: /* XAS variant? */
|
case 0x09: /* EASpeex? "Esp0" (libspeex variant) */
|
||||||
case 0x0f: /* EALayer3 variant? */
|
case 0x0a: /* EATrax (ATRAC9 variant, deflated frames) */
|
||||||
/* also 0x1n variations, used in other headers */
|
case 0x0b: /* ? */
|
||||||
|
case 0x0c: /* EAOpus (inside each SNS/SPS block is 16b frame size + Opus standard? packet) */
|
||||||
|
case 0x0d: /* ? */
|
||||||
|
case 0x0e: /* ? */
|
||||||
|
case 0x0f: /* ? */
|
||||||
default:
|
default:
|
||||||
VGM_LOG("EA SNU: unknown codec 0x%02x\n", codec);
|
VGM_LOG("EA SNS/SPS: unknown codec 0x%02x\n", codec);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,16 +435,17 @@ static VGMSTREAM * init_vgmstream_internal(STREAMFILE *streamFile) {
|
|||||||
|
|
||||||
|
|
||||||
#ifdef VGM_DEBUG_OUTPUT
|
#ifdef VGM_DEBUG_OUTPUT
|
||||||
|
#ifdef VGM_USE_FFMPEG
|
||||||
/* debug fun */
|
/* debug fun */
|
||||||
{
|
if (vgmstream->coding_type != coding_FFmpeg){
|
||||||
int i = 0;
|
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++) {
|
for (i = 0; i < vgmstream->channels; i++) {
|
||||||
VGM_ASSERT(vgmstream->ch[i].streamfile == NULL, "VGMSTREAM: null streamfile in ch%i\n",i);
|
VGM_ASSERT(vgmstream->ch[i].streamfile == NULL, "VGMSTREAM: null streamfile in ch%i\n",i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
#endif/*VGM_DEBUG_OUTPUT*/
|
#endif/*VGM_DEBUG_OUTPUT*/
|
||||||
|
|
||||||
|
|
||||||
@ -1970,6 +1971,7 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
|
|||||||
vgmstream->current_sample = vgmstream->loop_sample;
|
vgmstream->current_sample = vgmstream->loop_sample;
|
||||||
vgmstream->samples_into_block = vgmstream->loop_samples_into_block;
|
vgmstream->samples_into_block = vgmstream->loop_samples_into_block;
|
||||||
vgmstream->current_block_size = vgmstream->loop_block_size;
|
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->current_block_offset = vgmstream->loop_block_offset;
|
||||||
vgmstream->next_block_offset = vgmstream->loop_next_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_sample = vgmstream->current_sample;
|
||||||
vgmstream->loop_samples_into_block = vgmstream->samples_into_block;
|
vgmstream->loop_samples_into_block = vgmstream->samples_into_block;
|
||||||
vgmstream->loop_block_size = vgmstream->current_block_size;
|
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_block_offset = vgmstream->current_block_offset;
|
||||||
vgmstream->loop_next_block_offset = vgmstream->next_block_offset;
|
vgmstream->loop_next_block_offset = vgmstream->next_block_offset;
|
||||||
vgmstream->hit_loop = 1;
|
vgmstream->hit_loop = 1;
|
||||||
|
@ -721,36 +721,32 @@ typedef struct {
|
|||||||
int32_t loop_start_sample; /* first sample of the loop (included in the loop) */
|
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) */
|
int32_t loop_end_sample; /* last sample of the loop (not included in the loop) */
|
||||||
|
|
||||||
/* channels */
|
/* layouts/block */
|
||||||
VGMSTREAMCHANNEL * ch; /* pointer to array of channels */
|
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 * 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 */
|
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 current_sample; /* number of samples we've passed */
|
||||||
int32_t samples_into_block; /* number of samples into the current block */
|
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) */
|
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_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 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 */
|
off_t next_block_offset; /* offset of header of the next block */
|
||||||
int block_count; /* count of "semi" block in total block */
|
/* layout/block loop state */
|
||||||
|
|
||||||
|
|
||||||
/* loop layout (saved values) */
|
|
||||||
int32_t loop_sample; /* saved from current_sample, should be loop_start_sample... */
|
int32_t loop_sample; /* saved from current_sample, should be loop_start_sample... */
|
||||||
int32_t loop_samples_into_block;/* saved from samples_into_block */
|
int32_t loop_samples_into_block;/* saved from samples_into_block */
|
||||||
off_t loop_block_offset; /* saved from current_block_offset */
|
off_t loop_block_offset; /* saved from current_block_offset */
|
||||||
size_t loop_block_size; /* saved from current_block_size */
|
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 */
|
off_t loop_next_block_offset; /* saved from next_block_offset */
|
||||||
|
|
||||||
/* loop internals */
|
/* loop state */
|
||||||
int hit_loop; /* have we seen the loop yet? */
|
int hit_loop; /* have we seen the loop yet? */
|
||||||
/* counters for "loop + play end of the stream instead of fading" (not used/needed otherwise) */
|
/* 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) */
|
int loop_count; /* number of complete loops (1=looped once) */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user