Add EA SNU + EA-XAS decoder + SNS blocks [Dead Space, The Godfather 2]

This commit is contained in:
bnnm 2017-08-20 02:18:48 +02:00
parent c953685c80
commit adb225c180
15 changed files with 291 additions and 7 deletions

View File

@ -260,6 +260,7 @@ VGMSTREAM_DECLARE_FILE_TYPE("SND", snd);
VGMSTREAM_DECLARE_FILE_TYPE("SNDS", snds);
VGMSTREAM_DECLARE_FILE_TYPE("SNG", sng);
VGMSTREAM_DECLARE_FILE_TYPE("SNS", sns);
VGMSTREAM_DECLARE_FILE_TYPE("SNU", snu);
VGMSTREAM_DECLARE_FILE_TYPE("SPD", spd);
VGMSTREAM_DECLARE_FILE_TYPE("SPM", spm);
VGMSTREAM_DECLARE_FILE_TYPE("SPS", sps);

View File

@ -78,12 +78,15 @@ size_t ps_bytes_to_samples(size_t bytes, int channels);
void decode_xa(VGMSTREAM * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void init_get_high_nibble(VGMSTREAM * vgmstream);
/* ea_decoder */
/* ea_xa_decoder */
void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ea_xa_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel);
void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
/* ea_xas_decoder */
void decode_ea_xas(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
/* sdx2_decoder */
void decode_sdx2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_sdx2_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);

View File

@ -0,0 +1,78 @@
#include "coding.h"
#include "../util.h"
static const int EA_XA_TABLE[20] = {
0, 240, 460, 392,
0, 0, -208, -220,
0, 1, 3, 4,
7, 8, 10, 11,
0, -1, -3, -4
};
/* EA-XAS, evolution of EA-XA and cousin of MTA2. Layout: blocks of 0x4c per channel (128 samples),
* divided into 4 headers + 4 vertical groups of 15 bytes (for parallelism?).
* To simplify, always decodes the block and discards unneeded samples, so doesn't use external hist. */
void decode_ea_xas(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
int group, row, i;
int samples_done = 0, sample_count = 0;
/* internal interleave */
int block_samples = 128;
first_sample = first_sample % block_samples;
/* process groups */
for (group = 0; group < 4; group++) {
int coef1, coef2;
int16_t hist1, hist2;
uint8_t shift;
uint32_t group_header = (uint32_t)read_32bitLE(stream->offset + channel*0x4c + group*0x4, stream->streamfile); /* always LE */
coef1 = EA_XA_TABLE[(uint8_t)(group_header & 0x0F) + 0];
coef2 = EA_XA_TABLE[(uint8_t)(group_header & 0x0F) + 4];
hist2 = (int16_t)(group_header & 0xFFF0);
hist1 = (int16_t)((group_header >> 16) & 0xFFF0);
shift = 20 - ((group_header >> 16) & 0x0F);
/* write header samples (needed) */
if (sample_count >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = hist2;
samples_done++;
}
sample_count++;
if (sample_count >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = hist1;
samples_done++;
}
sample_count++;
/* process nibbles per group */
for (row = 0; row < 15; row++) {
for (i = 0; i < 1*2; i++) {
uint8_t sample_byte = (uint8_t)read_8bit(stream->offset + channel*0x4c + 4*4 + row*0x04 + group + i/2, stream->streamfile);
int sample;
sample = get_nibble_signed(sample_byte, !(i&1)); /* upper first */
sample = sample << shift;
sample = (sample + hist1 * coef1 + hist2 * coef2 + 128) >> 8;
sample = clamp16(sample);
if (sample_count >= first_sample && samples_done < samples_to_do) {
outbuf[samples_done * channelspacing] = sample;
samples_done++;
}
sample_count++;
hist2 = hist1;
hist1 = sample;
}
}
}
/* internal interleave (interleaved channels, but manually advances to co-exist with ea blocks) */
if (first_sample + samples_done == block_samples) {
stream->offset += 0x4c * channelspacing;
}
}

View File

@ -254,6 +254,7 @@ static const char* extension_list[] = {
"snds",
"sng",
"sns",
"snu",
"spd",
"spm",
"sps",
@ -464,6 +465,7 @@ static const coding_info coding_info_list[] = {
{coding_MTAF, "Konami MTAF 4-bit ADPCM"},
{coding_MTA2, "Konami MTA2 4-bit ADPCM"},
{coding_MC3, "Paradigm MC3 3-bit ADPCM"},
{coding_EA_XAS, "Electronic Arts EA-XAS 4-bit ADPCM"},
#ifdef VGM_USE_VORBIS
{coding_ogg_vorbis, "Ogg Vorbis"},
@ -500,8 +502,8 @@ static const layout_info layout_info_list[] = {
{layout_ast_blocked, "AST blocked"},
{layout_halpst_blocked, "HALPST blocked"},
{layout_xa_blocked, "CD-ROM XA"},
{layout_ea_blocked, "Electronic Arts Audio Blocks"},
{layout_eacs_blocked, "Electronic Arts (Old Version) Audio Blocks"},
{layout_ea_blocked, "Electronic Arts SCxx blocked"},
{layout_eacs_blocked, "Electronic Arts EACS blocked"},
{layout_caf_blocked, "CAF blocked"},
{layout_wsi_blocked, ".wsi blocked"},
{layout_xvas_blocked, ".xvas blocked"},
@ -529,6 +531,7 @@ static const layout_info layout_info_list[] = {
{layout_aix, "AIX interleave, internally 18-byte interleaved"},
{layout_aax, "AAX blocked, 18-byte interleaved"},
{layout_scd_int, "SCD multistream interleave"},
{layout_ea_sns_blocked, "Electronic Arts SNS blocked"},
#ifdef VGM_USE_VORBIS
{layout_ogg_vorbis, "Ogg"},
#endif
@ -876,6 +879,7 @@ static const meta_info meta_info_list[] = {
{meta_AHX, "CRI AHX header"},
{meta_STM, "Angel Studios/Rockstar San Diego STMA header"},
{meta_BINK, "RAD Game Tools Bink header"},
{meta_EA_SNU, "Electronic Arts SNU header"},
#ifdef VGM_USE_VORBIS
{meta_OGG_VORBIS, "Ogg Vorbis"},

View File

@ -51,7 +51,8 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM *
vgmstream->current_sample += samples_to_do;
vgmstream->samples_into_block+=samples_to_do;
if (vgmstream->samples_into_block==samples_this_block) {
if (vgmstream->samples_into_block==samples_this_block
/*&& vgmstream->current_sample < vgmstream->num_samples*/) { /* don't go past last block */
switch (vgmstream->layout_type) {
case layout_ast_blocked:
ast_block_update(vgmstream->next_block_offset,vgmstream);
@ -139,6 +140,9 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM *
break;
case layout_hwas_blocked:
hwas_block_update(vgmstream->next_block_offset,vgmstream);
break;
case layout_ea_sns_blocked:
ea_sns_block_update(vgmstream->next_block_offset,vgmstream);
break;
default:
break;

View File

@ -0,0 +1,38 @@
#include "layout.h"
#include "../coding/coding.h"
#include "../vgmstream.h"
/* EA "SNS "blocks (most common in .SNS) */
void ea_sns_block_update(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
uint32_t block_size, block_samples;
size_t file_size = get_streamfile_size(streamFile);
int i;
/* always BE */
block_size = read_32bitBE(block_offset + 0x00,streamFile);
block_samples = read_32bitBE(block_offset + 0x04,streamFile);
/* EOF */
if (block_size == 0 || block_offset >= file_size) {
vgmstream->current_block_offset = file_size;
vgmstream->next_block_offset = file_size + 0x04;
vgmstream->current_block_samples = vgmstream->num_samples;
return;
}
/* known: 0x80 = last block, 0x40, 0x08, 0x04, 0x01 */
if (block_size & 0xFF000000) {
VGM_ASSERT(!(block_size & 0x80000000), "EA SNS: unknown flag found at %lx\n", block_offset);
block_size &= 0x00FFFFFF;
}
for (i = 0; i < vgmstream->channels; i++) {
off_t channel_start = 0x00;
vgmstream->ch[i].offset = block_offset + 0x08 + channel_start;
}
vgmstream->current_block_offset = block_offset;
vgmstream->next_block_offset = block_offset + block_size;
vgmstream->current_block_samples = block_samples;
}

View File

@ -63,6 +63,8 @@ void rws_block_update(off_t block_offset, VGMSTREAM * vgmstream);
void hwas_block_update(off_t block_offset, VGMSTREAM * vgmstream);
void ea_sns_block_update(off_t block_offset, VGMSTREAM * vgmstream);
/* other layouts */
void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream);

View File

@ -348,6 +348,10 @@
RelativePath=".\meta\ea_old.c"
>
</File>
<File
RelativePath=".\meta\ea_snu.c"
>
</File>
<File
RelativePath=".\meta\emff.c"
>
@ -1343,9 +1347,13 @@
>
</File>
<File
RelativePath=".\coding\ea_decoder.c"
RelativePath=".\coding\ea_xa_decoder.c"
>
</File>
<File
RelativePath=".\coding\ea_xas_decoder.c"
>
</File>
<File
RelativePath=".\coding\ffmpeg_decoder.c"
>
@ -1534,6 +1542,10 @@
RelativePath=".\layout\ea_block.c"
>
</File>
<File
RelativePath=".\layout\ea_sns_blocked.c"
>
</File>
<File
RelativePath=".\layout\emff_blocked.c"
>

View File

@ -210,6 +210,7 @@
<ClCompile Include="meta\ea_schl.c" />
<ClCompile Include="meta\ea_schl_fixed.c" />
<ClCompile Include="meta\ea_old.c" />
<ClCompile Include="meta\ea_snu.c" />
<ClCompile Include="meta\emff.c" />
<ClCompile Include="meta\exakt_sc.c" />
<ClCompile Include="meta\ffw.c" />
@ -415,7 +416,8 @@
<ClCompile Include="coding\acm_decoder.c" />
<ClCompile Include="coding\adx_decoder.c" />
<ClCompile Include="coding\aica_decoder.c" />
<ClCompile Include="coding\ea_decoder.c" />
<ClCompile Include="coding\ea_xa_decoder.c" />
<ClCompile Include="coding\ea_xas_decoder.c" />
<ClCompile Include="coding\g719_decoder.c" />
<ClCompile Include="coding\g721_decoder.c" />
<ClCompile Include="coding\g7221_decoder.c" />
@ -454,6 +456,7 @@
<ClCompile Include="layout\caf_blocked.c" />
<ClCompile Include="layout\de2_blocked.c" />
<ClCompile Include="layout\ea_block.c" />
<ClCompile Include="layout\ea_sns_blocked.c" />
<ClCompile Include="layout\emff_blocked.c" />
<ClCompile Include="layout\filp_blocked.c" />
<ClCompile Include="layout\gsb_blocked.c" />

View File

@ -193,6 +193,9 @@
<ClCompile Include="meta\ea_old.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ea_snu.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\emff.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -784,7 +787,10 @@
<ClCompile Include="coding\aica_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\ea_decoder.c">
<ClCompile Include="coding\ea_xa_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\ea_xas_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\g721_decoder.c">
@ -898,6 +904,9 @@
<ClCompile Include="layout\ea_block.c">
<Filter>layout\Source Files</Filter>
</ClCompile>
<ClCompile Include="layout\ea_sns_blocked.c">
<Filter>layout\Source Files</Filter>
</ClCompile>
<ClCompile Include="layout\emff_blocked.c">
<Filter>layout\Source Files</Filter>
</ClCompile>

107
src/meta/ea_snu.c Normal file
View File

@ -0,0 +1,107 @@
#include "meta.h"
#include "../layout/layout.h"
/* .SNU - EA new-ish header (Dead Space, 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;
/* 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? */
start_offset = 0x20; /* first block */
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
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?, 14/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;
vgmstream->layout_type = layout_ea_sns_blocked;
switch(codec) {
case 0x04: /* "Xas1": EA-XAS (Dead Space) */
vgmstream->coding_type = coding_EA_XAS;
break;
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? */
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;
ea_sns_block_update(start_offset, vgmstream);
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -682,4 +682,6 @@ VGMSTREAM * init_vgmstream_sk_aud(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_stm(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE * streamFile);
#endif /*_META_H*/

View File

@ -38,6 +38,11 @@ void put_32bitBE(uint8_t * buf, int32_t i);
/* signed nibbles come up a lot */
static int nibble_to_int[16] = {0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1};
static inline int get_nibble_signed(uint8_t n, int upper) {
/*return ((n&0x70)-(n&0x80))>>4;*/
return nibble_to_int[(n >> (upper?4:0)) & 0x0f];
}
static inline int get_high_nibble_signed(uint8_t n) {
/*return ((n&0x70)-(n&0x80))>>4;*/
return nibble_to_int[n>>4];

View File

@ -366,6 +366,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
init_vgmstream_ea_schl_fixed,
init_vgmstream_sk_aud,
init_vgmstream_stm,
init_vgmstream_ea_snu,
init_vgmstream_txth, /* should go at the end (lower priority) */
#ifdef VGM_USE_FFMPEG
@ -931,6 +932,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
case layout_ps2_strlr_blocked:
case layout_rws_blocked:
case layout_hwas_blocked:
case layout_ea_sns_blocked:
render_vgmstream_blocked(buffer,sample_count,vgmstream);
break;
case layout_interleave_byte:
@ -1036,6 +1038,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
return 28;
case coding_MAXIS_XA:
return 14*vgmstream->channels;
case coding_EA_XAS:
return 128;
case coding_WS:
/* only works if output sample size is 8 bit, which always is for WS ADPCM */
return vgmstream->ws_output_size;
@ -1187,6 +1191,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
return 0x0F*vgmstream->channels;
case coding_EA_XA_V2:
return 1; /* the frame is variant in size (ADPCM frames of 0x0F or PCM frames) */
case coding_EA_XAS:
return 0x4c*vgmstream->channels;
case coding_WS:
return vgmstream->current_block_size;
case coding_IMA_int:
@ -1511,6 +1517,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
samples_to_do,chan);
}
break;
case coding_EA_XAS:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_ea_xas(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
vgmstream->channels,vgmstream->samples_into_block,
samples_to_do,chan);
}
break;
#ifdef VGM_USE_VORBIS
case coding_ogg_vorbis:
decode_ogg_vorbis(vgmstream->codec_data,

View File

@ -112,6 +112,7 @@ typedef enum {
coding_EA_XA_int, /* Electronic Arts EA-XA ADPCM v1 (mono/interleave) */
coding_EA_XA_V2, /* Electronic Arts EA-XA ADPCM v2 */
coding_MAXIS_XA, /* Maxis EA-XA ADPCM */
coding_EA_XAS, /* Electronic Arts EA-XAS ADPCM */
coding_XBOX, /* XBOX IMA ADPCM */
coding_XBOX_int, /* XBOX IMA ADPCM (interleaved) */
@ -234,6 +235,7 @@ typedef enum {
layout_ps2_strlr_blocked,
layout_rws_blocked,
layout_hwas_blocked,
layout_ea_sns_blocked, /* newest Electronic Arts blocks, found in SNS/SNU/SPS/etc formats */
/* otherwise odd */
layout_acm, /* libacm layout */
@ -618,6 +620,7 @@ typedef enum {
meta_AHX, /* CRI AHX header */
meta_STM, /* Angel Studios/Rockstar San Diego Games */
meta_BINK, /* RAD Game Tools BINK audio/video */
meta_EA_SNU, /* Electronic Arts SNU (Dead Space) */
#ifdef VGM_USE_VORBIS
meta_OGG_VORBIS, /* Ogg Vorbis */