mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-17 23:36:41 +01:00
Add EA-XMA for EA SNU [Dante's Inferno (X360)]
only 1/2ch works correctly as multichannel layout need to be investigated
This commit is contained in:
parent
4bb1103e3d
commit
a8370b4892
@ -202,9 +202,8 @@ void free_at3plus(maiatrac3plus_codec_data *data);
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
/* ffmpeg_decoder */
|
||||
ffmpeg_codec_data * init_ffmpeg_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
|
||||
ffmpeg_codec_data * init_ffmpeg_offset_index(STREAMFILE *streamFile, uint64_t start, uint64_t size, int stream_index);
|
||||
ffmpeg_codec_data * init_ffmpeg_header_offset(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size);
|
||||
ffmpeg_codec_data * init_ffmpeg_header_offset_index(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, int stream_index);
|
||||
ffmpeg_codec_data * init_ffmpeg_config(STREAMFILE *streamFile, uint8_t * header, uint64_t header_size, uint64_t start, uint64_t size, ffmpeg_custom_config * config);
|
||||
|
||||
void decode_ffmpeg(VGMSTREAM *stream, sample * outbuf, int32_t samples_to_do, int channels);
|
||||
void reset_ffmpeg(VGMSTREAM *vgmstream);
|
||||
@ -212,6 +211,10 @@ void seek_ffmpeg(VGMSTREAM *vgmstream, int32_t num_sample);
|
||||
void free_ffmpeg(ffmpeg_codec_data *data);
|
||||
|
||||
void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples);
|
||||
|
||||
|
||||
size_t ffmpeg_make_opus_header(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate);
|
||||
size_t ffmpeg_get_eaxma_virtual_size(off_t real_offset, size_t real_size, STREAMFILE *streamFile);
|
||||
#endif
|
||||
|
||||
/* coding_utils */
|
||||
@ -256,5 +259,4 @@ void xma2_parse_xma2_chunk(STREAMFILE *streamFile, off_t chunk_offset, int * cha
|
||||
size_t atrac3_bytes_to_samples(size_t bytes, int full_block_align);
|
||||
size_t atrac3plus_bytes_to_samples(size_t bytes, int full_block_align);
|
||||
|
||||
|
||||
#endif /*_CODING_H*/
|
||||
|
153
src/coding/ffmpeg_decoder_utils_ea_xma.c
Normal file
153
src/coding/ffmpeg_decoder_utils_ea_xma.c
Normal file
@ -0,0 +1,153 @@
|
||||
#include "coding.h"
|
||||
#include "ffmpeg_decoder_utils.h"
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
|
||||
#define EAXMA_XMA_BLOCK_SIZE 0x800
|
||||
|
||||
/**
|
||||
* EA-XMA is XMA with padding removed (so a real 0x450 block would be padded to a virtual 0x800 block).
|
||||
* //todo missing multichannel (packet multistream) support, unknown layout
|
||||
*/
|
||||
|
||||
|
||||
int ffmpeg_custom_read_eaxma(ffmpeg_codec_data *data, uint8_t *buf, int buf_size) {
|
||||
uint8_t v_buf[0x8000]; /* intermediate buffer, could be simplified */
|
||||
int buf_done = 0;
|
||||
uint64_t real_offset = data->real_offset;
|
||||
uint64_t virtual_offset = data->virtual_offset - data->header_size;
|
||||
uint64_t virtual_base = data->virtual_base;
|
||||
|
||||
|
||||
/* read and transform SNS/EA-XMA block into XMA block by adding padding */
|
||||
while (buf_done < buf_size) {
|
||||
int bytes_to_copy;
|
||||
size_t data_size, extra_size = 0, gap_size = 0;
|
||||
size_t block_size = read_32bitBE(real_offset, data->streamfile);
|
||||
/* 0x04(4): some kind of size? 0x08(4): decoded samples */
|
||||
|
||||
/* setup */
|
||||
data_size = (block_size & 0x00FFFFFF) - 0x0c; //todo last block size may be slightly off?
|
||||
if (data_size % EAXMA_XMA_BLOCK_SIZE) /* aligned padding */
|
||||
extra_size = EAXMA_XMA_BLOCK_SIZE - (data_size % EAXMA_XMA_BLOCK_SIZE);
|
||||
if (buf_done == 0) /* first read */
|
||||
gap_size = virtual_offset - virtual_base; /* might start a few bytes into the block */
|
||||
|
||||
if (data_size + extra_size > 0x8000) {
|
||||
VGM_LOG("EA-XMA: total size bigger than buffer at %lx\n", (off_t)real_offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bytes_to_copy = data_size + extra_size - gap_size;
|
||||
if (bytes_to_copy > buf_size - buf_done)
|
||||
bytes_to_copy = buf_size - buf_done;
|
||||
|
||||
/* transform */
|
||||
read_streamfile(v_buf, real_offset + 0x0c, data_size, data->streamfile);
|
||||
memset(v_buf + data_size, 0xFF, extra_size); /* padding can be any value, typically 0xFF */
|
||||
memcpy(buf + buf_done, v_buf + gap_size, bytes_to_copy);
|
||||
|
||||
/* move when block is fully done */
|
||||
if (data_size + extra_size == bytes_to_copy + gap_size) {
|
||||
real_offset += (block_size & 0x00FFFFFF);
|
||||
virtual_base += data_size + extra_size;
|
||||
}
|
||||
|
||||
buf_done += bytes_to_copy;
|
||||
|
||||
/* exit on last block just in case, though should reach file size */
|
||||
if (block_size & 0x80000000)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
data->real_offset = real_offset;
|
||||
data->virtual_base = virtual_base;
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
int64_t ffmpeg_custom_seek_eaxma(ffmpeg_codec_data *data, int64_t virtual_offset) {
|
||||
int64_t real_offset, virtual_base;
|
||||
int64_t current_virtual_offset = data->virtual_offset;
|
||||
|
||||
/* Find SNS block start closest to offset. ie. virtual_offset 0x1A10 could mean SNS blocks
|
||||
* of 0x456+0x820 padded to 0x800+0x1000 (base) + 0x210 (extra for reads), thus real_offset = 0xC76 */
|
||||
|
||||
if (virtual_offset > current_virtual_offset) { /* seek after current: start from current block */
|
||||
real_offset = data->real_offset;
|
||||
virtual_base = data->virtual_base;
|
||||
}
|
||||
else { /* seek before current: start from the beginning */
|
||||
real_offset = data->real_start;
|
||||
virtual_base = 0;
|
||||
}
|
||||
|
||||
|
||||
/* find target block */
|
||||
while (virtual_base < virtual_offset) {
|
||||
size_t data_size, extra_size = 0;
|
||||
size_t block_size = read_32bitBE(real_offset, data->streamfile);
|
||||
|
||||
data_size = (block_size & 0x00FFFFFF) - 0x0c;
|
||||
if (data_size % EAXMA_XMA_BLOCK_SIZE)
|
||||
extra_size = EAXMA_XMA_BLOCK_SIZE - (data_size % EAXMA_XMA_BLOCK_SIZE);
|
||||
|
||||
/* stop if virtual_offset lands inside current block */
|
||||
if (data_size + extra_size > virtual_offset)
|
||||
break;
|
||||
|
||||
real_offset += (block_size & 0x00FFFFFF);
|
||||
virtual_base += data_size + extra_size;
|
||||
}
|
||||
|
||||
/* closest we can use for reads */
|
||||
data->real_offset = real_offset;
|
||||
data->virtual_base = virtual_base;
|
||||
|
||||
return virtual_offset;
|
||||
}
|
||||
|
||||
int64_t ffmpeg_custom_size_eaxma(ffmpeg_codec_data *data) {
|
||||
|
||||
uint64_t virtual_size = data->config.virtual_size;
|
||||
if (!virtual_size)
|
||||
return 0;
|
||||
|
||||
return virtual_size + data->header_size;
|
||||
}
|
||||
|
||||
/* needed to know in meta for fake RIFF */
|
||||
size_t ffmpeg_get_eaxma_virtual_size(off_t real_offset, size_t real_size, STREAMFILE *streamFile) {
|
||||
size_t virtual_size = 0;
|
||||
|
||||
/* count all SNS/EAXMA blocks size + padding size */
|
||||
while (real_offset < real_size) {
|
||||
size_t data_size;
|
||||
size_t block_size = read_32bitBE(real_offset, streamFile);
|
||||
|
||||
data_size = (block_size & 0x00FFFFFF) - 0x0c;
|
||||
|
||||
if ((block_size & 0xFF000000) && !(block_size & 0x80000000)) {
|
||||
VGM_LOG("EA-XMA: unknown flag found at %lx\n", (off_t)real_offset);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
real_offset += (block_size & 0x00FFFFFF);
|
||||
|
||||
virtual_size += data_size;
|
||||
if (data_size % EAXMA_XMA_BLOCK_SIZE) /* XMA block padding */
|
||||
virtual_size += EAXMA_XMA_BLOCK_SIZE - (data_size % EAXMA_XMA_BLOCK_SIZE);
|
||||
|
||||
/* exit on last block just in case, though should reach real_size */
|
||||
if (block_size & 0x80000000)
|
||||
break;
|
||||
}
|
||||
|
||||
return virtual_size;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -1,27 +1,38 @@
|
||||
#include "meta.h"
|
||||
#include "../layout/layout.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* .SNU - EA new-ish header (Dead Space, 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 * 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 */
|
||||
//if ((read_32bitBE(0x00,streamFile) & 0x00FFFF00 != 0x00000000) && (read_32bitBE(0x0c,streamFile) != 0x00000000))
|
||||
// goto fail;
|
||||
/* 0x00: related to sample rate?, 0x02: always 0?, 0x03: related to channels? (usually match but may be 0) */
|
||||
/* 0x04: some size, maybe >>2 ~= number of 0x4c frames (BE/LE depending on platform) */
|
||||
/* 0x08: always 0x20? (also BE/LE), 0x0c: always 0? */
|
||||
/* 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 = 0x20; /* first block */
|
||||
start_offset = read_32bit(0x08,streamFile);
|
||||
|
||||
codec = read_8bit(0x10,streamFile);
|
||||
channel_config = read_8bit(0x11,streamFile);
|
||||
@ -36,7 +47,7 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
|
||||
}
|
||||
|
||||
#if 0
|
||||
//todo not working ok with blocks
|
||||
//todo not working ok with blocks in XAS
|
||||
if (flags & 0x60) { /* full loop, seen in ambient tracks */
|
||||
loop_flag = 1;
|
||||
loop_start = 0;
|
||||
@ -45,7 +56,7 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
|
||||
#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?, 14/15/16/17 = 6/7 ch?, 1d/1e/1f = 8 ch? */
|
||||
/* 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;
|
||||
@ -68,20 +79,64 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
|
||||
vgmstream->loop_end_sample = loop_end;
|
||||
|
||||
vgmstream->meta_type = meta_EA_SNU;
|
||||
vgmstream->layout_type = layout_ea_sns_blocked;
|
||||
|
||||
switch(codec) {
|
||||
case 0x04: /* "Xas1": EA-XAS (Dead Space) */
|
||||
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 0x03: /* "EXm0": EA-XMA */
|
||||
|
||||
case 0x05: /* "EL31": EALayer3 v1 b (with PCM blocks in normal EA-frames?) */
|
||||
case 0x06: /* "EL32P": EALayer3 v2 "P" */
|
||||
case 0x07: /* "EL32S": EALayer3 v2 "S" */
|
||||
case 0x09: /* EASpeex? */
|
||||
case 0x0c: /* EAOpus? */
|
||||
case 0x0e: /* XAS variant? */
|
||||
@ -97,7 +152,8 @@ VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
|
||||
goto fail;
|
||||
|
||||
ea_sns_block_update(start_offset, vgmstream);
|
||||
if (vgmstream->layout_type == layout_ea_sns_blocked)
|
||||
ea_sns_block_update(start_offset, vgmstream);
|
||||
|
||||
return vgmstream;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user