Add .gin and EA-XAS v0 decoder [Need for Speed Most Wanted]

This commit is contained in:
bnnm 2019-01-12 02:26:38 +01:00
parent f90d9c1e5d
commit 5d610be086
11 changed files with 155 additions and 14 deletions

View File

@ -101,7 +101,8 @@ void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
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);
void decode_ea_xas_v0(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ea_xas_v1(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);

View File

@ -9,11 +9,11 @@ static const int EA_XA_TABLE[20] = {
0, -1, -3, -4
};
/* EA-XAS, evolution of EA-XA and cousin of MTA2. From FFmpeg (general info) + MTA2 (layout) + EA-XA (decoding)
/* EA-XAS v1, evolution of EA-XA/XAS and cousin of MTA2. From FFmpeg (general info) + MTA2 (layout) + EA-XA (decoding)
*
* 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) {
void decode_ea_xas_v1(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;
@ -77,3 +77,64 @@ void decode_ea_xas(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacin
stream->offset += 0x4c * channelspacing;
}
}
/* EA-XAS v0, without complex layouts and closer to EA-XA. Somewhat based on daemon1's decoder */
void decode_ea_xas_v0(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
off_t frame_offset;
int i;
int block_samples, frames_in, samples_done = 0, sample_count = 0;
/* external interleave (fixed size), mono */
block_samples = 32;
frames_in = first_sample / block_samples;
first_sample = first_sample % block_samples;
frame_offset = stream->offset + (0x0f+0x02+0x02)*frames_in;
/* process frames */
{
int coef1, coef2;
int16_t hist1, hist2;
uint8_t shift;
uint32_t frame_header = (uint32_t)read_32bitLE(frame_offset, stream->streamfile); /* always LE */
coef1 = EA_XA_TABLE[(uint8_t)(frame_header & 0x0F) + 0];
coef2 = EA_XA_TABLE[(uint8_t)(frame_header & 0x0F) + 4];
hist2 = (int16_t)(frame_header & 0xFFF0);
hist1 = (int16_t)((frame_header >> 16) & 0xFFF0);
shift = 20 - ((frame_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 */
for (i = 0; i < 0x0f*2; i++) {
uint8_t sample_byte = (uint8_t)read_8bit(frame_offset + 0x02 + 0x02 + 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;
}
}
}

View File

@ -155,6 +155,7 @@ static const char* extension_list[] = {
"gcub",
"gcw",
"genh",
"gin",
"gms",
"gsb",
//"gsf", //conflicts with GBA gsf plugins?
@ -586,7 +587,8 @@ static const coding_info coding_info_list[] = {
{coding_EA_XA_int, "Electronic Arts EA-XA 4-bit ADPCM v1 (mono/interleave)"},
{coding_EA_XA_V2, "Electronic Arts EA-XA 4-bit ADPCM v2"},
{coding_MAXIS_XA, "Maxis EA-XA 4-bit ADPCM"},
{coding_EA_XAS, "Electronic Arts EA-XAS 4-bit ADPCM"},
{coding_EA_XAS_V0, "Electronic Arts EA-XAS 4-bit ADPCM v0"},
{coding_EA_XAS_V1, "Electronic Arts EA-XAS 4-bit ADPCM v1"},
{coding_IMA, "IMA 4-bit ADPCM"},
{coding_IMA_int, "IMA 4-bit ADPCM (mono/interleave)"},
@ -1155,6 +1157,7 @@ static const meta_info meta_info_list[] = {
{meta_DSP_ADPCMX, "AQUASTYLE ADPY header"},
{meta_OGG_OPUS, "Ogg Opus header"},
{meta_IMC, "iNiS .IMC header"},
{meta_GIN, "Electronic Arts Gnsu header"},
};

View File

@ -576,10 +576,14 @@
RelativePath=".\meta\gcsw.c"
>
</File>
<File
RelativePath=".\meta\genh.c"
>
</File>
<File
RelativePath=".\meta\genh.c"
>
</File>
<File
RelativePath=".\meta\gin.c"
>
</File>
<File
RelativePath=".\meta\bar.c"
>

View File

@ -264,6 +264,7 @@
<ClCompile Include="meta\gca.c" />
<ClCompile Include="meta\gcsw.c" />
<ClCompile Include="meta\genh.c" />
<ClCompile Include="meta\gin.c" />
<ClCompile Include="meta\bar.c" />
<ClCompile Include="meta\gsp_gsb.c" />
<ClCompile Include="meta\gtd.c" />

View File

@ -364,6 +364,9 @@
<ClCompile Include="meta\genh.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\gin.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\bar.c">
<Filter>meta\Source Files</Filter>
</ClCompile>

View File

@ -640,7 +640,7 @@ static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, ST
#endif
case EAAC_CODEC_XAS: /* "Xas1": EA-XAS [Dead Space (PC/PS3)] */
vgmstream->coding_type = coding_EA_XAS;
vgmstream->coding_type = coding_EA_XAS_V1;
vgmstream->layout_type = layout_blocked_ea_sns;
break;

53
src/meta/gin.c Normal file
View File

@ -0,0 +1,53 @@
#include "meta.h"
#include "../coding/coding.h"
/* .gin - EA engine sounds [Need for Speed: Most Wanted (multi)] */
VGMSTREAM * init_vgmstream_gin(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, sample_rate, num_samples;
/* checks */
if (!check_extensions(streamFile, "gin"))
goto fail;
if (read_32bitBE(0x00,streamFile) != 0x476E7375) /* "Gnsu" */
goto fail;
/* contains mapped values for engine RPM sounds but we'll just play the whole thing */
/* 0x04: size? "20\00\00"? */
/* 0x08/0c: min/max float RPM? */
/* 0x10: RPM up? (pitch/frequency) table size */
/* 0x14: RPM ??? table size */
/* always LE even on X360/PS3 */
num_samples = read_32bitLE(0x18, streamFile);
sample_rate = read_32bitLE(0x1c, streamFile);
start_offset = 0x20 +
(read_32bitLE(0x10, streamFile) + 1) * 0x04 +
(read_32bitLE(0x14, streamFile) + 1) * 0x04;
channel_count = 1;
loop_flag = 0;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count, loop_flag);
if (!vgmstream) goto fail;
vgmstream->meta_type = meta_GIN;
vgmstream->sample_rate = sample_rate;
vgmstream->num_samples = num_samples;
vgmstream->coding_type = coding_EA_XAS_V0;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x13;
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -823,4 +823,6 @@ VGMSTREAM * init_vgmstream_imc_container(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_smp(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_gin(STREAMFILE * streamFile);
#endif /*_META_H*/

View File

@ -460,6 +460,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_imc,
init_vgmstream_imc_container,
init_vgmstream_smp,
init_vgmstream_gin,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
@ -1188,7 +1189,9 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_EA_XA_V2:
case coding_MAXIS_XA:
return 28;
case coding_EA_XAS:
case coding_EA_XAS_V0:
return 32;
case coding_EA_XAS_V1:
return 128;
case coding_MSADPCM:
@ -1370,7 +1373,9 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
return 0x0F*vgmstream->channels;
case coding_EA_XA_V2:
return 0; /* variable (ADPCM frames of 0x0f or PCM frames of 0x3d) */
case coding_EA_XAS:
case coding_EA_XAS_V0:
return 0xF+0x02+0x02;
case coding_EA_XAS_V1:
return 0x4c*vgmstream->channels;
case coding_MSADPCM:
@ -1706,9 +1711,15 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch);
}
break;
case coding_EA_XAS:
case coding_EA_XAS_V0:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_ea_xas(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
decode_ea_xas_v0(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch);
}
break;
case coding_EA_XAS_V1:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_ea_xas_v1(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
vgmstream->channels,vgmstream->samples_into_block,samples_to_do, ch);
}
break;

View File

@ -106,7 +106,8 @@ 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_EA_XAS_V0, /* Electronic Arts EA-XAS ADPCM v0 */
coding_EA_XAS_V1, /* Electronic Arts EA-XAS ADPCM v1 */
coding_IMA, /* IMA ADPCM (stereo or mono, low nibble first) */
coding_IMA_int, /* IMA ADPCM (mono/interleave, low nibble first) */
@ -712,6 +713,7 @@ typedef enum {
meta_DSP_ADPCMX,
meta_OGG_OPUS,
meta_IMC,
meta_GIN,
} meta_t;