mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-14 07:32:52 +01:00
a8370b4892
only 1/2ch works correctly as multichannel layout need to be investigated
164 lines
5.8 KiB
C
164 lines
5.8 KiB
C
#include "meta.h"
|
|
#include "../layout/layout.h"
|
|
#include "../coding/coding.h"
|
|
|
|
|
|
/* .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;
|
|
uint32_t num_samples, loop_start = 0, loop_end = 0;
|
|
off_t start_offset;
|
|
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
|
|
|
|
|
|
/* check extension, case insensitive */
|
|
if (!check_extensions(streamFile,"snu"))
|
|
goto fail;
|
|
|
|
/* check header (the first 0x10 are BE/LE depending on platform) */
|
|
/* 0x00(1): related to sample rate? (03=48000)
|
|
* 0x01(1): flags? (when set seems to be a bank and has extra data before start_offset) //todo
|
|
* 0x02(1): always 0?
|
|
* 0x03(1): channels? (usually matches but rarely may be 0)
|
|
* 0x04(4): some size, maybe >>2 ~= number of frames
|
|
* 0x08(4): start offset
|
|
* 0x0c(4): some sub-offset? (0x20, found when 0x01 is set) */
|
|
|
|
/* use start offset as endianness flag */
|
|
if ((uint32_t)read_32bitLE(0x08,streamFile) > 0x00F00000) {
|
|
read_32bit = read_32bitBE;
|
|
} else {
|
|
read_32bit = read_32bitLE;
|
|
}
|
|
|
|
start_offset = read_32bit(0x08,streamFile);
|
|
|
|
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");
|
|
goto fail;
|
|
}
|
|
|
|
#if 0
|
|
//todo not working ok with blocks in XAS
|
|
if (flags & 0x60) { /* full loop, seen in ambient tracks */
|
|
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? */
|
|
switch(channel_config) {
|
|
case 0x00: channel_count = 1; break;
|
|
case 0x04: channel_count = 2; break;
|
|
case 0x0c: channel_count = 4; break;
|
|
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);
|
|
goto fail;
|
|
}
|
|
|
|
|
|
/* build the VGMSTREAM */
|
|
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
|
if (!vgmstream) goto fail;
|
|
|
|
vgmstream->sample_rate = sample_rate;
|
|
vgmstream->num_samples = num_samples;
|
|
vgmstream->loop_start_sample = loop_start;
|
|
vgmstream->loop_end_sample = loop_end;
|
|
|
|
vgmstream->meta_type = meta_EA_SNU;
|
|
|
|
switch(codec) {
|
|
case 0x04: /* "Xas1": EA-XAS (Dead Space) */
|
|
vgmstream->coding_type = coding_EA_XAS;
|
|
vgmstream->layout_type = layout_ea_sns_blocked;
|
|
break;
|
|
|
|
#if 0
|
|
#ifdef VGM_USE_MPEG
|
|
case 0x07: { /* "EL32S": EALayer3 v2 "S" (Dante's Inferno PS3) */
|
|
mpeg_custom_config cfg;
|
|
off_t mpeg_start_offset = start_offset + 0x08;
|
|
|
|
memset(&cfg, 0, sizeof(mpeg_custom_config));
|
|
|
|
/* layout is still blocks, but should work fine with the custom mpeg decoder */
|
|
vgmstream->codec_data = init_mpeg_custom_codec_data(streamFile, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_EAL32S, &cfg);
|
|
if (!vgmstream->codec_data) goto fail;
|
|
|
|
vgmstream->layout_type = layout_ea_sns_blocked;
|
|
break;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef VGM_USE_FFMPEG
|
|
case 0x03: { /* "EXm0": EA-XMA (Dante's Inferno X360) */
|
|
uint8_t buf[0x100];
|
|
int bytes, block_size, block_count;
|
|
size_t stream_size, virtual_size;
|
|
ffmpeg_custom_config cfg;
|
|
|
|
stream_size = get_streamfile_size(streamFile) - start_offset;
|
|
virtual_size = ffmpeg_get_eaxma_virtual_size(start_offset,stream_size, streamFile);
|
|
block_size = 0x8000; /* ? */
|
|
block_count = stream_size / block_size + (stream_size % block_size ? 1 : 0);
|
|
|
|
bytes = ffmpeg_make_riff_xma2(buf, 0x100, vgmstream->num_samples, virtual_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size);
|
|
if (bytes <= 0) goto fail;
|
|
|
|
memset(&cfg, 0, sizeof(ffmpeg_custom_config));
|
|
cfg.type = FFMPEG_EA_XMA;
|
|
cfg.virtual_size = virtual_size;
|
|
|
|
vgmstream->codec_data = init_ffmpeg_config(streamFile, buf,bytes, start_offset,stream_size, &cfg);
|
|
if (!vgmstream->codec_data) goto fail;
|
|
|
|
vgmstream->coding_type = coding_FFmpeg;
|
|
vgmstream->layout_type = layout_none;
|
|
break;
|
|
}
|
|
#endif
|
|
case 0x00: /* "NONE" */
|
|
case 0x01: /* not used? */
|
|
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 */
|
|
default:
|
|
VGM_LOG("EA SNU: unknown codec 0x%02x\n", codec);
|
|
goto fail;
|
|
}
|
|
|
|
|
|
/* open the file for reading by each channel */
|
|
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
|
goto fail;
|
|
|
|
if (vgmstream->layout_type == layout_ea_sns_blocked)
|
|
ea_sns_block_update(start_offset, vgmstream);
|
|
|
|
return vgmstream;
|
|
|
|
fail:
|
|
close_vgmstream(vgmstream);
|
|
return NULL;
|
|
}
|