Merge pull request #149 from bnnm/master

MPC, VXN, SNS/SPS, OGG, FLX, MT
This commit is contained in:
Christopher Snowhill 2017-12-03 14:07:53 -08:00 committed by GitHub
commit d2f48b2066
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1949 additions and 553 deletions

View File

@ -65,13 +65,13 @@ supports Winamp plugins you may also use ```in_vgmstream.dll``` instead.
Because the XMPlay MP3 decoder incorrectly tries to play some vgmstream exts,
you need to manually fix it by going to **options > plugins > input > vgmstream**
and in the "priority filetypes" put: ```awc,ckd,fsb,genh,msf,p3d,rak,scd,xvag```
and in the "priority filetypes" put: ```ahx,asf,awc,ckd,fsb,genh,msf,p3d,rak,scd,txth,xvag```
### foo_input_vgmstream
### foo_input_vgmstream
Every file should be installed automatically by the .fb2k-component bundle.
### Audacious plugin
Needs to be manually built. Instructions can be found in the source files.
### Audacious plugin
Needs to be manually built. Instructions can be found in the build document.
## Supported codec types
Quick list of codecs vgmstream supports, including many obscure ones that
@ -108,9 +108,10 @@ are used in few games.
- InterPlay ACM
- Visual Art's NWA
- CRI HCA
- Electronic Arts MicroTalk a.k.a. UTK or UMT
- Xiph Vorbis (Ogg, FSB5, Wwise, OGL, Silicon Knights)
- MPEG MP1/2/3 (standard, AHX, XVAG, FSB, AWC, P3D, etc)
- Electronic Arts EALayer3 v1
- Electronic Arts EALayer3
- ITU-T G.722.1 (Polycom Siren 7)
- ITU-T G.722.1 annex C (Polycom Siren 14)
- ITU G.719 annex B (Polycom Siren 22)

View File

@ -116,6 +116,7 @@ VGMSTREAM_DECLARE_FILE_TYPE("EXA", exa);
VGMSTREAM_DECLARE_FILE_TYPE("FAG", fag);
VGMSTREAM_DECLARE_FILE_TYPE("FFW", ffw);
VGMSTREAM_DECLARE_FILE_TYPE("FILP", filp);
VGMSTREAM_DECLARE_FILE_TYPE("FLX", flx);
VGMSTREAM_DECLARE_FILE_TYPE("FSB", fsb);
VGMSTREAM_DECLARE_FILE_TYPE("FWAV", fwav);
@ -185,6 +186,7 @@ VGMSTREAM_DECLARE_FILE_TYPE("MIC", mic);
VGMSTREAM_DECLARE_FILE_TYPE("MIHB", mihb);
VGMSTREAM_DECLARE_FILE_TYPE("MNSTR", mnstr);
//"mp4", //common
VGMSTREAM_DECLARE_FILE_TYPE("MPC", mpc);
VGMSTREAM_DECLARE_FILE_TYPE("MPDSP", mpdsp);
VGMSTREAM_DECLARE_FILE_TYPE("MPDS", mpds);
VGMSTREAM_DECLARE_FILE_TYPE("MSA", msa);
@ -277,6 +279,7 @@ VGMSTREAM_DECLARE_FILE_TYPE("SMPL", smpl);
VGMSTREAM_DECLARE_FILE_TYPE("SND", snd);
VGMSTREAM_DECLARE_FILE_TYPE("SNDS", snds);
VGMSTREAM_DECLARE_FILE_TYPE("SNG", sng);
VGMSTREAM_DECLARE_FILE_TYPE("SNR", snr);
VGMSTREAM_DECLARE_FILE_TYPE("SNS", sns);
VGMSTREAM_DECLARE_FILE_TYPE("SNU", snu);
VGMSTREAM_DECLARE_FILE_TYPE("SPD", spd);
@ -328,6 +331,7 @@ VGMSTREAM_DECLARE_FILE_TYPE("VOI", voi);
VGMSTREAM_DECLARE_FILE_TYPE("VPK", vpk);
VGMSTREAM_DECLARE_FILE_TYPE("VS", vs);
VGMSTREAM_DECLARE_FILE_TYPE("VSF", vsf);
VGMSTREAM_DECLARE_FILE_TYPE("VXN", vxn);
VGMSTREAM_DECLARE_FILE_TYPE("WAA", waa);
VGMSTREAM_DECLARE_FILE_TYPE("WAC", wac);

View File

@ -135,6 +135,14 @@ void decode_mta2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing,
/* mc3_decoder */
void decode_mc3(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
/* ea_mt_decoder*/
ea_mt_codec_data *init_ea_mt(int channel_count, int type);
void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void reset_ea_mt(VGMSTREAM * vgmstream);
void flush_ea_mt(VGMSTREAM *vgmstream);
void seek_ea_mt(VGMSTREAM * vgmstream, int32_t num_sample);
void free_ea_mt(ea_mt_codec_data *data);
/* hca_decoder */
void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels);
void reset_hca(VGMSTREAM *vgmstream);
@ -169,7 +177,6 @@ void free_mpeg(mpeg_codec_data *data);
void flush_mpeg(mpeg_codec_data * data);
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data);
void mpeg_set_error_logging(mpeg_codec_data * data, int enable);
#endif
#ifdef VGM_USE_G7221

577
src/coding/ea_mt_decoder.c Normal file
View File

@ -0,0 +1,577 @@
#include "coding.h"
/* Decodes EA MicroTalk (speech codec) using a copied utkencode lib.
* EA separates MT10:1 and MT5:1 (bigger frames), but apparently are the same
* with different encoding parameters. Later revisions may have PCM blocks (rare).
*
* Decoder by Andrew D'Addesio: https://github.com/daddesio/utkencode
* Info: http://wiki.niotso.org/UTK
*
* The following tries to follow the original code as close as possible, with minimal changes for vgmstream
*/
/* ************************************************************************************************* */
#define UTK_BUFFER_SIZE 0x4000
//#define UTK_MAKE_U32(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24))
#define UTK_ROUND(x) ((x) >= 0.0f ? ((x)+0.5f) : ((x)-0.5f))
#define UTK_MIN(x,y) ((x)<(y)?(x):(y))
#define UTK_MAX(x,y) ((x)>(y)?(x):(y))
#define UTK_CLAMP(x,min,max) UTK_MIN(UTK_MAX(x,min),max)
/* Note: This struct assumes a member alignment of 4 bytes.
** This matters when pitch_lag > 216 on the first subframe of any given frame. */
typedef struct UTKContext {
uint8_t buffer[UTK_BUFFER_SIZE]; //vgmstream extra
STREAMFILE * streamfile; //vgmstream extra
off_t offset; //vgmstream extra
int samples_filled; //vgmstream extra
//FILE *fp; //vgmstream extra
const uint8_t *ptr, *end;
int parsed_header;
unsigned int bits_value;
int bits_count;
int reduced_bw;
int multipulse_thresh;
float fixed_gains[64];
float rc[12];
float synth_history[12];
float adapt_cb[324];
float decompressed_frame[432];
} UTKContext;
enum {
MDL_NORMAL = 0,
MDL_LARGEPULSE = 1
};
static const float utk_rc_table[64] = {
0.0f,
-.99677598476409912109375f, -.99032700061798095703125f, -.983879029750823974609375f, -.977430999279022216796875f,
-.970982015132904052734375f, -.964533984661102294921875f, -.958085000514984130859375f, -.9516370296478271484375f,
-.930754005908966064453125f, -.904959976673126220703125f, -.879167020320892333984375f, -.853372991085052490234375f,
-.827579021453857421875f, -.801786005496978759765625f, -.775991976261138916015625f, -.75019800662994384765625f,
-.724404990673065185546875f, -.6986110210418701171875f, -.6706349849700927734375f, -.61904799938201904296875f,
-.567460000514984130859375f, -.515873014926910400390625f, -.4642859995365142822265625f, -.4126980006694793701171875f,
-.361110985279083251953125f, -.309523999691009521484375f, -.257937014102935791015625f, -.20634900033473968505859375f,
-.1547619998455047607421875f, -.10317499935626983642578125f, -.05158700048923492431640625f,
0.0f,
+.05158700048923492431640625f, +.10317499935626983642578125f, +.1547619998455047607421875f, +.20634900033473968505859375f,
+.257937014102935791015625f, +.309523999691009521484375f, +.361110985279083251953125f, +.4126980006694793701171875f,
+.4642859995365142822265625f, +.515873014926910400390625f, +.567460000514984130859375f, +.61904799938201904296875f,
+.6706349849700927734375f, +.6986110210418701171875f, +.724404990673065185546875f, +.75019800662994384765625f,
+.775991976261138916015625f, +.801786005496978759765625f, +.827579021453857421875f, +.853372991085052490234375f,
+.879167020320892333984375f, +.904959976673126220703125f, +.930754005908966064453125f, +.9516370296478271484375f,
+.958085000514984130859375f, +.964533984661102294921875f, +.970982015132904052734375f, +.977430999279022216796875f,
+.983879029750823974609375f, +.99032700061798095703125f, +.99677598476409912109375
};
static const uint8_t utk_codebooks[2][256] = {
{ /* normal model */
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 21,
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 25,
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 22,
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 0,
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 21,
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 26,
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 17,
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 22,
4, 6, 5, 9, 4, 6, 5, 13, 4, 6, 5, 10, 4, 6, 5, 18,
4, 6, 5, 9, 4, 6, 5, 14, 4, 6, 5, 10, 4, 6, 5, 2
}, { /* large-pulse model */
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 27,
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 1,
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 28,
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 3,
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 27,
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 1,
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 23,
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 28,
4, 11, 7, 15, 4, 12, 8, 19, 4, 11, 7, 16, 4, 12, 8, 24,
4, 11, 7, 15, 4, 12, 8, 20, 4, 11, 7, 16, 4, 12, 8, 3
}
};
static const struct {
int next_model;
int code_size;
float pulse_value;
} utk_commands[29] = {
{MDL_LARGEPULSE, 8, 0.0f},
{MDL_LARGEPULSE, 7, 0.0f},
{MDL_NORMAL, 8, 0.0f},
{MDL_NORMAL, 7, 0.0f},
{MDL_NORMAL, 2, 0.0f},
{MDL_NORMAL, 2, -1.0f},
{MDL_NORMAL, 2, +1.0f},
{MDL_NORMAL, 3, -1.0f},
{MDL_NORMAL, 3, +1.0f},
{MDL_LARGEPULSE, 4, -2.0f},
{MDL_LARGEPULSE, 4, +2.0f},
{MDL_LARGEPULSE, 3, -2.0f},
{MDL_LARGEPULSE, 3, +2.0f},
{MDL_LARGEPULSE, 5, -3.0f},
{MDL_LARGEPULSE, 5, +3.0f},
{MDL_LARGEPULSE, 4, -3.0f},
{MDL_LARGEPULSE, 4, +3.0f},
{MDL_LARGEPULSE, 6, -4.0f},
{MDL_LARGEPULSE, 6, +4.0f},
{MDL_LARGEPULSE, 5, -4.0f},
{MDL_LARGEPULSE, 5, +4.0f},
{MDL_LARGEPULSE, 7, -5.0f},
{MDL_LARGEPULSE, 7, +5.0f},
{MDL_LARGEPULSE, 6, -5.0f},
{MDL_LARGEPULSE, 6, +5.0f},
{MDL_LARGEPULSE, 8, -6.0f},
{MDL_LARGEPULSE, 8, +6.0f},
{MDL_LARGEPULSE, 7, -6.0f},
{MDL_LARGEPULSE, 7, +6.0f}
};
static int utk_read_byte(UTKContext *ctx)
{
if (ctx->ptr < ctx->end)
return *ctx->ptr++;
//vgmstream extra: this reads from FILE if static buffer was exhausted, now from a context buffer and STREAMFILE instead
if (ctx->streamfile) { //if (ctx->fp) {
//static uint8_t buffer[4096];
//size_t bytes_copied = fread(buffer, 1, sizeof(buffer), ctx->fp);
size_t bytes_copied = read_streamfile(ctx->buffer, ctx->offset, sizeof(ctx->buffer), ctx->streamfile);
ctx->offset += bytes_copied;
if (bytes_copied > 0 && bytes_copied <= sizeof(ctx->buffer)) {
ctx->ptr = ctx->buffer;
ctx->end = ctx->buffer + bytes_copied;
return *ctx->ptr++;
}
}
return 0;
}
static int16_t utk_read_i16(UTKContext *ctx)
{
int x = utk_read_byte(ctx);
x = (x << 8) | utk_read_byte(ctx);
return x;
}
static int utk_read_bits(UTKContext *ctx, int count)
{
int ret = ctx->bits_value & ((1 << count) - 1);
ctx->bits_value >>= count;
ctx->bits_count -= count;
if (ctx->bits_count < 8) {
/* read another byte */
ctx->bits_value |= utk_read_byte(ctx) << ctx->bits_count;
ctx->bits_count += 8;
}
return ret;
}
static void utk_parse_header(UTKContext *ctx)
{
int i;
float multiplier;
ctx->reduced_bw = utk_read_bits(ctx, 1);
ctx->multipulse_thresh = 32 - utk_read_bits(ctx, 4);
ctx->fixed_gains[0] = 8.0f * (1 + utk_read_bits(ctx, 4));
multiplier = 1.04f + utk_read_bits(ctx, 6)*0.001f;
for (i = 1; i < 64; i++)
ctx->fixed_gains[i] = ctx->fixed_gains[i-1] * multiplier;
}
static void utk_decode_excitation(UTKContext *ctx, int use_multipulse, float *out, int stride)
{
int i;
if (use_multipulse) {
/* multi-pulse model: n pulses are coded explicitly; the rest are zero */
int model, cmd;
model = 0;
i = 0;
while (i < 108) {
cmd = utk_codebooks[model][ctx->bits_value & 0xff];
model = utk_commands[cmd].next_model;
utk_read_bits(ctx, utk_commands[cmd].code_size);
if (cmd > 3) {
/* insert a pulse with magnitude <= 6.0f */
out[i] = utk_commands[cmd].pulse_value;
i += stride;
} else if (cmd > 1) {
/* insert between 7 and 70 zeros */
int count = 7 + utk_read_bits(ctx, 6);
if (i + count * stride > 108)
count = (108 - i)/stride;
while (count > 0) {
out[i] = 0.0f;
i += stride;
count--;
}
} else {
/* insert a pulse with magnitude >= 7.0f */
int x = 7;
while (utk_read_bits(ctx, 1))
x++;
if (!utk_read_bits(ctx, 1))
x *= -1;
out[i] = (float)x;
i += stride;
}
}
} else {
/* RELP model: entire residual (excitation) signal is coded explicitly */
i = 0;
while (i < 108) {
if (!utk_read_bits(ctx, 1))
out[i] = 0.0f;
else if (!utk_read_bits(ctx, 1))
out[i] = -2.0f;
else
out[i] = 2.0f;
i += stride;
}
}
}
static void rc_to_lpc(const float *rc, float *lpc)
{
int i, j;
float tmp1[12];
float tmp2[12];
for (i = 10; i >= 0; i--)
tmp2[1+i] = rc[i];
tmp2[0] = 1.0f;
for (i = 0; i < 12; i++) {
float x = -tmp2[11] * rc[11];
for (j = 10; j >= 0; j--) {
x -= tmp2[j] * rc[j];
tmp2[j+1] = x * rc[j] + tmp2[j];
}
tmp1[i] = tmp2[0] = x;
for (j = 0; j < i; j++)
x -= tmp1[i-1-j] * lpc[j];
lpc[i] = x;
}
}
static void utk_lp_synthesis_filter(UTKContext *ctx, int offset, int num_blocks)
{
int i, j, k;
float lpc[12];
float *ptr = &ctx->decompressed_frame[offset];
rc_to_lpc(ctx->rc, lpc);
for (i = 0; i < num_blocks; i++) {
for (j = 0; j < 12; j++) {
float x = *ptr;
for (k = 0; k < j; k++)
x += lpc[k] * ctx->synth_history[k-j+12];
for (; k < 12; k++)
x += lpc[k] * ctx->synth_history[k-j];
ctx->synth_history[11-j] = x;
*ptr++ = x;
}
}
}
/*
** Public functions.
*/
static void utk_decode_frame(UTKContext *ctx)
{
int i, j;
int use_multipulse = 0;
float excitation[5+108+5];
float rc_delta[12];
if (!ctx->bits_count) {
ctx->bits_value = utk_read_byte(ctx);
ctx->bits_count = 8;
}
if (!ctx->parsed_header) {
utk_parse_header(ctx);
ctx->parsed_header = 1;
}
memset(&excitation[0], 0, 5*sizeof(float));
memset(&excitation[5+108], 0, 5*sizeof(float));
/* read the reflection coefficients */
for (i = 0; i < 12; i++) {
int idx;
if (i == 0) {
idx = utk_read_bits(ctx, 6);
if (idx < ctx->multipulse_thresh)
use_multipulse = 1;
} else if (i < 4) {
idx = utk_read_bits(ctx, 6);
} else {
idx = 16 + utk_read_bits(ctx, 5);
}
rc_delta[i] = (utk_rc_table[idx] - ctx->rc[i])*0.25f;
}
/* decode four subframes */
for (i = 0; i < 4; i++) {
int pitch_lag = utk_read_bits(ctx, 8);
float pitch_gain = (float)utk_read_bits(ctx, 4)/15.0f;
float fixed_gain = ctx->fixed_gains[utk_read_bits(ctx, 6)];
if (!ctx->reduced_bw) {
utk_decode_excitation(ctx, use_multipulse, &excitation[5], 1);
} else {
/* residual (excitation) signal is encoded at reduced bandwidth */
int align = utk_read_bits(ctx, 1);
int zero = utk_read_bits(ctx, 1);
utk_decode_excitation(ctx, use_multipulse, &excitation[5+align], 2);
if (zero) {
/* fill the remaining samples with zero
** (spectrum is duplicated into high frequencies) */
for (j = 0; j < 54; j++)
excitation[5+(1-align)+2*j] = 0.0f;
} else {
/* interpolate the remaining samples
** (spectrum is low-pass filtered) */
float *ptr = &excitation[5+(1-align)];
for (j = 0; j < 108; j += 2)
ptr[j] = ptr[j-5] * 0.01803267933428287506103515625f
- ptr[j-3] * 0.114591561257839202880859375f
+ ptr[j-1] * 0.597385942935943603515625f
+ ptr[j+1] * 0.597385942935943603515625f
- ptr[j+3] * 0.114591561257839202880859375f
+ ptr[j+5] * 0.01803267933428287506103515625f;
/* scale by 0.5f to give the sinc impulse response unit energy */
fixed_gain *= 0.5f;
}
}
for (j = 0; j < 108; j++)
ctx->decompressed_frame[108*i+j] = fixed_gain * excitation[5+j]
+ pitch_gain * ctx->adapt_cb[108*i+216-pitch_lag+j];
}
for (i = 0; i < 324; i++)
ctx->adapt_cb[i] = ctx->decompressed_frame[108+i];
for (i = 0; i < 4; i++) {
for (j = 0; j < 12; j++)
ctx->rc[j] += rc_delta[j];
utk_lp_synthesis_filter(ctx, 12*i, i < 3 ? 1 : 33);
}
}
static void utk_init(UTKContext *ctx)
{
memset(ctx, 0, sizeof(*ctx));
}
#if 0 //vgmstream extra: see flush_ea_mt
static void utk_set_fp(UTKContext *ctx, FILE *fp)
{
ctx->fp = fp;
/* reset the bit reader */
ctx->bits_count = 0;
}
static void utk_set_ptr(UTKContext *ctx, const uint8_t *ptr, const uint8_t *end)
{
ctx->ptr = ptr;
ctx->end = end;
/* reset the bit reader */
ctx->bits_count = 0;
}
#endif
/*
** MicroTalk Revision 3 decoding function.
*/
static void utk_rev3_decode_frame(UTKContext *ctx)
{
int pcm_data_present = (utk_read_byte(ctx) == 0xee);
int i;
utk_decode_frame(ctx);
/* unread the last 8 bits and reset the bit reader */
ctx->ptr--;
ctx->bits_count = 0;
if (pcm_data_present) {
/* Overwrite n samples at a given offset in the decoded frame with
** raw PCM data. */
int offset = utk_read_i16(ctx);
int count = utk_read_i16(ctx);
/* sx.exe does not do any bounds checking or clamping of these two
** fields (see 004274D1 in sx.exe v3.01.01), which means a specially
** crafted MT5:1 file can crash sx.exe.
** We will throw an error instead. */
if (offset < 0 || offset > 432) {
//fprintf(stderr, "error: invalid PCM offset %d\n", offset);
//exit(EXIT_FAILURE);
return; //vgmstream extra
}
if (count < 0 || count > 432 - offset) {
//fprintf(stderr, "error: invalid PCM count %d\n", count);
//exit(EXIT_FAILURE);
return; //vgmstream extra
}
for (i = 0; i < count; i++)
ctx->decompressed_frame[offset+i] = (float)utk_read_i16(ctx);
}
}
/* ************************************************************************************************* */
ea_mt_codec_data *init_ea_mt(int channel_count, int pcm_blocks) {
ea_mt_codec_data *data = NULL;
int i;
data = calloc(channel_count, sizeof(ea_mt_codec_data));
if (!data) goto fail;
data->pcm_blocks = pcm_blocks;
data->utk_context_size = channel_count;
data->utk_context = calloc(channel_count, sizeof(UTKContext*));
if (!data->utk_context) goto fail;
for (i = 0; i < channel_count; i++) {
data->utk_context[i] = calloc(1, sizeof(UTKContext));
if (!data->utk_context[i]) goto fail;
utk_init(data->utk_context[i]);
}
return data;
fail:
free_ea_mt(data);
return NULL;
}
void decode_ea_mt(VGMSTREAM * vgmstream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
ea_mt_codec_data *data = vgmstream->codec_data;
int i, sample_count = 0, frame_samples;
UTKContext* ctx = data->utk_context[channel];
/* Use the above decoder, which expects pointers to read data. Since EA-MT frames aren't
* byte-aligned, reading new buffer data is decided by the decoder. When decoding starts
* or a SCHl block changes flush_ea_mt must be called to reset the state.
* A bit hacky but would need some restructuring otherwise. */
frame_samples = 432;
first_sample = first_sample % frame_samples;
/* don't decode again if we didn't consume the current frame.
* UTKContext saves the sample buffer, and can't re-decode a frame */
if (!ctx->samples_filled) {
if (data->pcm_blocks)
utk_rev3_decode_frame(ctx);
else
utk_decode_frame(ctx);
ctx->samples_filled = 1;
}
/* copy samples */
for (i = first_sample; i < first_sample+samples_to_do; i++) {
int x = UTK_ROUND(ctx->decompressed_frame[i]);
outbuf[sample_count] = (int16_t)UTK_CLAMP(x, -32768, 32767);
sample_count += channelspacing;
}
if (i == frame_samples)
ctx->samples_filled = 0;
}
static void flush_ea_mt_internal(VGMSTREAM *vgmstream, int is_start) {
ea_mt_codec_data *data = vgmstream->codec_data;
int i;
size_t bytes;
/* the decoder needs to be notified when offsets change */
for (i = 0; i < vgmstream->channels; i++) {
UTKContext *ctx = data->utk_context[i];
ctx->streamfile = vgmstream->ch[i].streamfile;
ctx->offset = is_start ? vgmstream->ch[i].channel_start_offset : vgmstream->ch[i].offset;
ctx->samples_filled = 0;
bytes = read_streamfile(ctx->buffer,ctx->offset,sizeof(ctx->buffer),ctx->streamfile);
ctx->offset += sizeof(ctx->buffer);
ctx->ptr = ctx->buffer;
ctx->end = ctx->buffer + bytes;
ctx->bits_count = 0;
}
}
void flush_ea_mt(VGMSTREAM *vgmstream) {
flush_ea_mt_internal(vgmstream, 0);
}
void reset_ea_mt(VGMSTREAM *vgmstream) {
flush_ea_mt_internal(vgmstream, 1);
}
void seek_ea_mt(VGMSTREAM * vgmstream, int32_t num_sample) {
flush_ea_mt_internal(vgmstream, 1);
//todo discard loop (though this should be adecuate as probably only uses full loops, if at all)
}
void free_ea_mt(ea_mt_codec_data *data) {
int i;
if (!data)
return;
for (i = 0; i < data->utk_context_size; i++) {
free(data->utk_context[i]);
}
free(data->utk_context);
free(data);
}

View File

@ -29,59 +29,57 @@ static const int EA_XA_TABLE[20] = {
0, -1, -3, -4
};
/* EA XA v2; like ea_xa_int but with "PCM samples" flag and doesn't add 128 on expand or clamp (pre-adjusted by the encoder?) */
/* EA XA v2 (always mono); like ea_xa_int but with "PCM samples" flag and doesn't add 128 on expand or clamp (pre-adjusted by the encoder?) */
void decode_ea_xa_v2(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel) {
uint8_t frame_info;
int32_t sample_count;
int32_t coef1, coef2;
int i, shift;
off_t channel_offset = stream->channel_start_offset; /* suboffset within frame */
int i, sample_count, shift;
first_sample = first_sample%28;
int pcm_frame_size = 0x01 + 2*0x02 + 28*0x02;
int xa_frame_size = 0x0f;
int frame_samples = 28;
first_sample = first_sample % frame_samples;
/* header */
frame_info = (uint8_t)read_8bit(stream->offset+channel_offset,stream->streamfile);
channel_offset++;
frame_info = read_8bit(stream->offset,stream->streamfile);
if (frame_info == 0xEE) { /* PCM frame (used in later revisions), samples always BE */
stream->adpcm_history1_32 = read_16bitBE(stream->offset+channel_offset+0x00,stream->streamfile);
stream->adpcm_history2_32 = read_16bitBE(stream->offset+channel_offset+0x02,stream->streamfile);
channel_offset += 4;
stream->adpcm_history1_32 = read_16bitBE(stream->offset + 0x01 + 0x00,stream->streamfile);
stream->adpcm_history2_32 = read_16bitBE(stream->offset + 0x01 + 0x02,stream->streamfile);
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
outbuf[sample_count] = read_16bitBE(stream->offset+channel_offset,stream->streamfile);
channel_offset+=2;
outbuf[sample_count] = read_16bitBE(stream->offset + 0x01 + 2*0x02 + i*0x02,stream->streamfile);
}
/* Only increment offset on complete frame */
if (channel_offset-stream->channel_start_offset == (2*28)+5)
stream->channel_start_offset += (2*28)+5;
} else { /* ADPCM frame */
/* only increment offset on complete frame */
if (i == frame_samples)
stream->offset += pcm_frame_size;
}
else { /* ADPCM frame */
coef1 = EA_XA_TABLE[(frame_info >> 4) + 0];
coef2 = EA_XA_TABLE[(frame_info >> 4) + 4];
shift = (frame_info & 0x0F) + 8;
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
uint8_t sample_byte, sample_nibble;
int32_t sample;
off_t byte_offset = (stream->offset + channel_offset + i/2);
int32_t new_sample;
off_t byte_offset = (stream->offset + 0x01 + i/2);
int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
sample_nibble = (!(i%2) ? sample_byte >> 4 : sample_byte & 0x0F); /* i=even > high nibble */
sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
sample = (sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32) >> 8;
sample = clamp16(sample);
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32) >> 8;
new_sample = clamp16(new_sample);
outbuf[sample_count] = sample;
outbuf[sample_count] = new_sample;
stream->adpcm_history2_32 = stream->adpcm_history1_32;
stream->adpcm_history1_32 = sample;
stream->adpcm_history1_32 = new_sample;
}
channel_offset += i/2;
/* Only increment offset on complete frame */
if (channel_offset - stream->channel_start_offset == 0x0F)
stream->channel_start_offset += 0x0F;
/* only increment offset on complete frame */
if (i == frame_samples)
stream->offset += xa_frame_size;
}
}
@ -90,43 +88,42 @@ void decode_ea_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing
uint8_t frame_info;
int32_t coef1, coef2;
int i, sample_count, shift;
off_t channel_offset = stream->channel_start_offset; /* suboffset within frame */
int hn = (channel==0); /* high nibble marker for stereo subinterleave, ch0/L=high nibble, ch1/R=low nibble */
first_sample = first_sample % 28;
int frame_size = 0x1e;
int frame_samples = 28;
first_sample = first_sample % frame_samples;
/* header (coefs ch0+ch1 + shift ch0+ch1) */
frame_info = read_8bit(stream->offset+channel_offset,stream->streamfile);
channel_offset++;
frame_info = read_8bit(stream->offset+0x00,stream->streamfile);
coef1 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 0];
coef2 = EA_XA_TABLE[(hn ? frame_info >> 4 : frame_info & 0x0F) + 4];
shift = (frame_info & 0x0F) + 8;
frame_info = read_8bit(stream->offset+channel_offset,stream->streamfile);
channel_offset++;
frame_info = read_8bit(stream->offset+0x01,stream->streamfile);
shift = (hn ? frame_info >> 4 : frame_info & 0x0F) + 8;
/* samples */
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
uint8_t sample_byte, sample_nibble;
int32_t sample;
off_t byte_offset = (stream->offset + channel_offset + i);
int32_t new_sample;
off_t byte_offset = (stream->offset + 0x02 + i);
int nibble_shift = (hn ? 4 : 0); /* high nibble first */
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
sample_nibble = (hn ? sample_byte >> 4 : sample_byte & 0x0F);
sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
sample = (sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8;
sample = clamp16(sample);
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8;
new_sample = clamp16(new_sample);
outbuf[sample_count] = sample;
outbuf[sample_count] = new_sample;
stream->adpcm_history2_32 = stream->adpcm_history1_32;
stream->adpcm_history1_32 = sample;
stream->adpcm_history1_32 = new_sample;
}
channel_offset += i;
/* Only increment offset on complete frame */
if(channel_offset - stream->channel_start_offset == 0x1E)
stream->channel_start_offset += 0x1E;
/* only increment offset on complete frame */
if (i == frame_samples)
stream->offset += frame_size;
}
/* EA-XA v1 mono/interleave */
@ -134,13 +131,13 @@ void decode_ea_xa_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspa
uint8_t frame_info;
int32_t coef1, coef2;
int i, sample_count, shift;
off_t channel_offset = stream->channel_start_offset; /* suboffset within frame */
first_sample = first_sample % 28;
int frame_size = 0x0f;
int frame_samples = 28;
first_sample = first_sample % frame_samples;
/* header (coefs+shift ch0) */
frame_info = read_8bit(stream->offset+channel_offset,stream->streamfile);
channel_offset++;
frame_info = read_8bit(stream->offset,stream->streamfile);
coef1 = EA_XA_TABLE[(frame_info >> 4) + 0];
coef2 = EA_XA_TABLE[(frame_info >> 4) + 4];
shift = (frame_info & 0x0F) + 8;
@ -148,39 +145,38 @@ void decode_ea_xa_int(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspa
/* samples */
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
uint8_t sample_byte, sample_nibble;
int32_t sample;
off_t byte_offset = (stream->offset + channel_offset + i/2);
int32_t new_sample;
off_t byte_offset = (stream->offset + 0x01 + i/2);
int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
sample_nibble = (!(i%2) ? sample_byte >> 4 : sample_byte & 0x0F); /* i=even > high nibble */
sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
sample = (sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8;
sample = clamp16(sample);
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8;
new_sample = clamp16(new_sample);
outbuf[sample_count] = sample;
outbuf[sample_count] = new_sample;
stream->adpcm_history2_32 = stream->adpcm_history1_32;
stream->adpcm_history1_32 = sample;
stream->adpcm_history1_32 = new_sample;
}
channel_offset += i/2;
/* Only increment offset on complete frame */
if(channel_offset - stream->channel_start_offset == 0x0F)
stream->channel_start_offset += 0x0F;
/* only increment offset on complete frame */
if (i == frame_samples)
stream->offset += frame_size;
}
/* Maxis EA-XA v1 (mono+stereo), differing slightly in the header layout in stereo mode */
/* Maxis EA-XA v1 (mono+stereo) with byte-interleave layout in stereo mode */
void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) {
uint8_t frame_info;
int32_t coef1, coef2;
int i, sample_count, shift;
off_t channel_offset = stream->channel_start_offset;
int frame_size = channelspacing * 15; /* mono samples have a frame of 15, stereo files have frames of 30 */
first_sample = first_sample % 28;
int frame_size = 0x0f * channelspacing; /* varies in mono/stereo */
int frame_samples = 28;
first_sample = first_sample % frame_samples;
/* header (coefs+shift ch0 + coefs+shift ch1) */
frame_info = read_8bit(channel_offset,stream->streamfile);
channel_offset += channelspacing;
frame_info = read_8bit(stream->offset + channel,stream->streamfile);
coef1 = EA_XA_TABLE[(frame_info >> 4) + 0];
coef2 = EA_XA_TABLE[(frame_info >> 4) + 4];
shift = (frame_info & 0x0F) + 8;
@ -188,27 +184,22 @@ void decode_maxis_xa(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspac
/* samples */
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
uint8_t sample_byte, sample_nibble;
int32_t sample;
off_t byte_offset = (stream->offset + channel_offset);
int32_t new_sample;
off_t byte_offset = (stream->offset + 0x01*channelspacing + (channelspacing == 2 ? i/2 + channel + (i/2)*0x01 : i/2));
int nibble_shift = (!(i&1)) ? 4 : 0; /* high nibble first */
sample_byte = (uint8_t)read_8bit(byte_offset,stream->streamfile);
sample_nibble = (i&1) ? sample_byte & 0x0F : sample_byte >> 4;
sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
sample = (sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8;
sample = clamp16(sample);
sample_nibble = (sample_byte >> nibble_shift) & 0x0F;
new_sample = (sample_nibble << 28) >> shift; /* sign extend to 32b and shift */
new_sample = (new_sample + coef1 * stream->adpcm_history1_32 + coef2 * stream->adpcm_history2_32 + 128) >> 8;
new_sample = clamp16(new_sample);
outbuf[sample_count] = sample;
outbuf[sample_count] = new_sample;
stream->adpcm_history2_32 = stream->adpcm_history1_32;
stream->adpcm_history1_32 = sample;
if(i&1)
stream->offset+=channelspacing;
stream->adpcm_history1_32 = new_sample;
}
channel_offset+=i;
/* Only increment offset on complete frame */
if (channel_offset - stream->channel_start_offset == frame_size) {
stream->channel_start_offset += frame_size;
stream->offset=0;
}
/* only increment offset on complete frame */
if (i == frame_samples)
stream->offset += frame_size;
}

View File

@ -3,7 +3,7 @@
#ifdef VGM_USE_FFMPEG
#define EAXMA_XMA_MAX_PACKETS_PER_SNS_BLOCK 3 /* only seen up to 3 (Dante's Inferno) */
#define EAXMA_XMA_MAX_PACKETS_PER_SNS_BLOCK 4 /* normally max 3 (Dante's Inferno), ~14 (1 stream) in Burnout Paradise */
#define EAXMA_XMA_MAX_STREAMS_PER_SNS_BLOCK 4 /* XMA2 max is 8ch = 4 * 2ch */
#define EAXMA_XMA_PACKET_SIZE 0x800
#define EAXMA_XMA_BUFFER_SIZE (EAXMA_XMA_MAX_PACKETS_PER_SNS_BLOCK * EAXMA_XMA_MAX_STREAMS_PER_SNS_BLOCK * EAXMA_XMA_PACKET_SIZE)
@ -48,7 +48,8 @@ int ffmpeg_custom_read_eaxma(ffmpeg_codec_data *data, uint8_t *buf, int buf_size
if (max_packets == 0) goto fail;
if (max_packets * num_streams * EAXMA_XMA_PACKET_SIZE > EAXMA_XMA_BUFFER_SIZE) {
VGM_LOG("EA XMA: block too big at %lx\n", (off_t)real_offset);
VGM_LOG("EA XMA: block too big (%i * %i * 0x%x = 0x%x vs max 0x%x) at %lx\n",
max_packets,num_streams,EAXMA_XMA_PACKET_SIZE, max_packets*num_streams*EAXMA_XMA_PACKET_SIZE, EAXMA_XMA_BUFFER_SIZE,(off_t)real_offset);
goto fail;
}

View File

@ -60,7 +60,7 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m
case MPEG_STANDARD:
case MPEG_AHX:
case MPEG_EA:
if (data->channels_per_frame != data->config.channels)
if (info.channels != data->config.channels)
goto fail; /* no multichannel expected */
break;
@ -89,7 +89,8 @@ fail:
/* writes data to the buffer and moves offsets */
int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data) {
int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) {
mpeg_custom_stream *ms = data->streams[num_stream];
mpeg_frame_info info;
size_t current_data_size = 0;
size_t current_padding = 0;
@ -137,14 +138,14 @@ int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
current_data_size = info.frame_size;
break;
}
if (!current_data_size || current_data_size > data->buffer_size) {
if (!current_data_size || current_data_size > ms->buffer_size) {
VGM_LOG("MPEG: incorrect data_size 0x%x\n", current_data_size);
goto fail;
}
/* read single frame */
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset, current_data_size, stream->streamfile);
ms->bytes_in_buffer = read_streamfile(ms->buffer,stream->offset, current_data_size, stream->streamfile);
/* update offsets */

View File

@ -3,11 +3,12 @@
#ifdef VGM_USE_MPEG
#define MPEG_AHX_EXPECTED_FRAME_SIZE 0x414
static int ahx_decrypt_type08(mpeg_codec_data *data);
static int ahx_decrypt_type08(uint8_t * buffer, mpeg_custom_config *config);
/* writes data to the buffer and moves offsets, transforming AHX frames as needed */
int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data) {
int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) {
/* 0xFFF5E0C0 header: frame size 0x414 (160kbps, 22050Hz) but they actually are much shorter */
mpeg_custom_stream *ms = data->streams[num_stream];
size_t current_data_size = 0;
size_t file_size = get_streamfile_size(stream->streamfile);
@ -33,22 +34,22 @@ int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data)
next_offset++;
}
}
if (!current_data_size || current_data_size > data->buffer_size || current_data_size > MPEG_AHX_EXPECTED_FRAME_SIZE) {
if (!current_data_size || current_data_size > ms->buffer_size || current_data_size > MPEG_AHX_EXPECTED_FRAME_SIZE) {
VGM_LOG("MPEG AHX: incorrect data_size 0x%x\n", current_data_size);
goto fail;
}
/* 0-fill up to expected size to keep mpg123 happy */
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset,current_data_size,stream->streamfile);
memset(data->buffer + data->bytes_in_buffer,0, MPEG_AHX_EXPECTED_FRAME_SIZE - data->bytes_in_buffer);
data->bytes_in_buffer = MPEG_AHX_EXPECTED_FRAME_SIZE;
ms->bytes_in_buffer = read_streamfile(ms->buffer,stream->offset,current_data_size,stream->streamfile);
memset(ms->buffer + ms->bytes_in_buffer,0, MPEG_AHX_EXPECTED_FRAME_SIZE - ms->bytes_in_buffer);
ms->bytes_in_buffer = MPEG_AHX_EXPECTED_FRAME_SIZE;
/* decrypt if needed */
switch(data->config.encryption) {
case 0x00: break;
case 0x08: ahx_decrypt_type08(data); break;
case 0x08: ahx_decrypt_type08(ms->buffer, &data->config); break;
default:
VGM_LOG("MPEG AHX: unknown encryption 0x%x\n", data->config.encryption);
break; /* garbled frame */
@ -66,7 +67,7 @@ fail:
}
/* Decrypts an AHX type 0x08 (keystring) encrypted frame. Algorithm by Thealexbarney */
static int ahx_decrypt_type08(mpeg_codec_data *data) {
static int ahx_decrypt_type08(uint8_t * buffer, mpeg_custom_config *config) {
int i, index, encrypted_bits;
uint32_t value;
uint16_t current_key;
@ -78,18 +79,18 @@ static int ahx_decrypt_type08(mpeg_codec_data *data) {
/* read 2b from a bitstream offset to decrypt, and use it as an index to get the key.
* AHX encrypted bitstream starts at 107b (0x0d*8+3), every frame, and seem to always use index 2 */
value = (uint32_t)get_32bitBE(data->buffer + 0x0d);
value = (uint32_t)get_32bitBE(buffer + 0x0d);
index = (value >> (32-3-2)) & 0x03;
switch(index) {
case 0: current_key = 0; break;
case 1: current_key = data->config.cri_key1; break;
case 2: current_key = data->config.cri_key2; break;
case 3: current_key = data->config.cri_key3; break;
case 1: current_key = config->cri_key1; break;
case 2: current_key = config->cri_key2; break;
case 3: current_key = config->cri_key3; break;
default: goto fail;
}
/* AHX for DC: 16b, normal: 6b (no idea, probably some Layer II field) */
encrypted_bits = data->config.cri_type == 0x10 ? 16 : 6;
encrypted_bits = config->cri_type == 0x10 ? 16 : 6;
/* decrypt next bitstream 2b pairs, up to 16b (max key size):
* - read 2b from bitstream (from higher to lower)
@ -101,7 +102,7 @@ static int ahx_decrypt_type08(mpeg_codec_data *data) {
}
/* write output */
put_32bitBE(data->buffer + 0x0d, value);
put_32bitBE(buffer + 0x0d, value);
return 1;
fail:

View File

@ -170,7 +170,7 @@ int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data,
goto fail;
current_data_size = info.frame_size;
data->bytes_in_buffer = read_streamfile(data->buffer,stream->offset, current_data_size, stream->streamfile);
ms->bytes_in_buffer = read_streamfile(ms->buffer,stream->offset, current_data_size, stream->streamfile);
stream->offset += current_data_size;

View File

@ -7,11 +7,12 @@
* with regular MPEG data and optional PCM blocks. We transform EA-frames to MPEG-frames on the fly
* and manually fill the sample PCM sample buffer.
*
* Layer III MPEG1 uses two granules (data chunks) per frame, while MPEG2/2.5 ("LSF mode") only one. EA-frames
* contain one granule, so to reconstruct one MPEG-frame we need two EA-frames (MPEG1) or one (MPEG2).
* Layer III MPEG1 uses two granules (data chunks) per frame, while MPEG2/2.5 ("LSF mode") only one.
* EA-frames contain one granule, so to reconstruct one MPEG-frame we need two EA-frames (MPEG1) or
* one (MPEG2). This is only for our decoder, real EALayer3 would decode EA-frames directly.
* EALayer v1 and v2 differ in part of the header, but are mostly the same.
*
* Reverse engineering: https://bitbucket.org/Zenchreal/ealayer3 (ealayer3.exe decoder)
* Reverse engineering by Zench: https://bitbucket.org/Zenchreal/ealayer3 (ealayer3.exe decoder)
* Reference: https://www.mp3-tech.org/programmer/docs/mp3_theory.pdf
* https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/mpegaudiodec_template.c#L1306
*/
@ -29,7 +30,7 @@ typedef struct {
uint8_t * buf; /* buffer to read/write*/
size_t bufsize; /* max size of the buffer */
off_t b_off; /* current offset in bits inside the buffer */
off_t offset; /* info only */
off_t info_offset; /* for logs */
} ealayer3_bitstream;
/* parsed info from a single EALayer3 frame */
@ -38,13 +39,14 @@ typedef struct {
uint32_t v1_pcm_flag;
uint32_t v1_pcm_decode_discard;
uint32_t v1_pcm_number;
uint32_t v1_pcm_unknown;
/* EALayer3 v2 header */
uint32_t v2_extended_flag;
uint32_t v2_stereo_flag;
uint32_t v2_unknown; /* unused? */
uint32_t v2_frame_size; /* full size including headers and pcm block */
uint32_t v2_mode; /* BLOCKOFFSETMODE: IGNORE = 0x0, PRESERVE = 0x1, MUTE = 0x2, MAX = 0x3 */
uint32_t v2_mode; /* discard mode */
uint32_t v2_mode_value; /* samples to use depending on mode */
uint32_t v2_pcm_number;
uint32_t v2_common_size; /* common header+data size; can be zero */
@ -80,11 +82,12 @@ typedef struct {
static int ealayer3_parse_frame(mpeg_codec_data *data, ealayer3_bitstream *is, ealayer3_frame_info * eaf);
static int ealayer3_parse_frame_v1(ealayer3_bitstream *is, ealayer3_frame_info * eaf, int channels_per_frame);
static int ealayer3_parse_frame_v1(ealayer3_bitstream *is, ealayer3_frame_info * eaf, int channels_per_frame, int is_v1b);
static int ealayer3_parse_frame_v2(ealayer3_bitstream *is, ealayer3_frame_info * eaf);
static int ealayer3_parse_frame_common(ealayer3_bitstream *is, ealayer3_frame_info * eaf);
static int ealayer3_rebuild_mpeg_frame(ealayer3_bitstream* is_0, ealayer3_frame_info* eaf_0, ealayer3_bitstream* is_1, ealayer3_frame_info* eaf_1, ealayer3_bitstream* os);
static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, ealayer3_frame_info * eaf);
static int ealayer3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start);
static int r_bits(ealayer3_bitstream * iw, int num_bits, uint32_t * value);
static int w_bits(ealayer3_bitstream * ow, int num_bits, uint32_t value);
@ -94,17 +97,14 @@ static int w_bits(ealayer3_bitstream * ow, int num_bits, uint32_t value);
/* EXTERNAL API */
/* **************************************************************************** */
/* init codec from a EALayer3 frame */
/* init codec from an EALayer3 frame */
int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) {
int ok;
ealayer3_frame_info eaf;
ealayer3_bitstream is;
ealayer3_bitstream is = {0};
uint8_t ibuf[EALAYER3_EA_FRAME_BUFFER_SIZE];
//;VGM_LOG("EAFRAME: EALayer3 init at %lx\n", start_offset);
if (data->type == MPEG_EAL32P || data->type == MPEG_EAL32S)
goto fail; /* untested */
//;VGM_LOG("init at %lx\n", start_offset);
/* get first frame for info */
{
@ -115,20 +115,12 @@ int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset,
ok = ealayer3_parse_frame(data, &is, &eaf);
if (!ok) goto fail;
}
;VGM_ASSERT(!eaf.mpeg1, "MPEG EAL3: mpeg2 found at 0x%lx\n", start_offset);
VGM_ASSERT(!eaf.mpeg1, "MPEG EAL3: mpeg2 found at 0x%lx\n", start_offset); /* untested */
*coding_type = coding_MPEG_ealayer3;
data->channels_per_frame = eaf.channels;
data->samples_per_frame = eaf.mpeg1 ? 1152 : 576;
/* extra checks */
if (!data->channels_per_frame || data->config.channels != data->channels_per_frame){
VGM_LOG("MPEG EAL3: unknown %i multichannel layout\n", data->config.channels);
goto fail; /* unknown layout */
}
/* encoder delay: EALayer3 handles this while decoding (skips samples as writes PCM blocks) */
return 1;
@ -138,15 +130,20 @@ fail:
/* writes data to the buffer and moves offsets, transforming EALayer3 frames */
int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) {
int ok;
off_t current_offset = stream->offset;
mpeg_custom_stream *ms = data->streams[num_stream];
int ok, granule_found;
off_t info_offset = stream->offset;
ealayer3_frame_info eaf_0, eaf_1;
ealayer3_bitstream is_0, is_1, os;
ealayer3_bitstream is_0 = {0}, is_1 = {0}, os = {0};
uint8_t ibuf_0[EALAYER3_EA_FRAME_BUFFER_SIZE], ibuf_1[EALAYER3_EA_FRAME_BUFFER_SIZE];
/* read first frame/granule */
{
/* read first frame/granule, or PCM-only frame (found alone at the end of SCHl streams) */
{
//;VGM_LOG("s%i: get granule0 at %lx\n", num_stream,stream->offset);
if (!ealayer3_skip_data(stream, data, num_stream, 1))
goto fail;
is_0.buf = ibuf_0;
is_0.bufsize = read_streamfile(ibuf_0,stream->offset,EALAYER3_EA_FRAME_BUFFER_SIZE, stream->streamfile); /* reads less at EOF */
is_0.b_off = 0;
@ -158,51 +155,54 @@ int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *
if (!ok) goto fail;
stream->offset += eaf_0.eaframe_size;
}
//;VGM_LOG("s%i: get granule0 done at %lx (eaf_size=%x, common_size=%x)\n", num_stream,stream->offset, eaf_0.eaframe_size, eaf_0.common_size);
/* get second frame/granule */
if (eaf_0.mpeg1) {
int granule1_found;
do {
is_1.buf = ibuf_1;
is_1.bufsize = read_streamfile(ibuf_1,stream->offset,EALAYER3_EA_FRAME_BUFFER_SIZE, stream->streamfile); /* reads less at EOF */
is_1.b_off = 0;
if (!ealayer3_skip_data(stream, data, num_stream, 0))
goto fail;
ok = ealayer3_parse_frame(data, &is_1, &eaf_1);
if (!ok) goto fail;
}
ok = ealayer3_write_pcm_block(stream, data, num_stream, &eaf_1);
if (!ok) goto fail;
/* get second frame/granule (MPEG1 only) if first granule was found */
granule_found = 0;
while (eaf_0.common_size && eaf_0.mpeg1 && !granule_found) {
//;VGM_LOG("s%i: get granule1 at %lx\n", num_stream,stream->offset);
if (!ealayer3_skip_data(stream, data, num_stream, 1))
goto fail;
stream->offset += eaf_1.eaframe_size;
is_1.buf = ibuf_1;
is_1.bufsize = read_streamfile(ibuf_1,stream->offset,EALAYER3_EA_FRAME_BUFFER_SIZE, stream->streamfile); /* reads less at EOF */
is_1.b_off = 0;
ok = ealayer3_parse_frame(data, &is_1, &eaf_1);
if (!ok) goto fail;
/* in V1 sometimes there is a PCM block between two granules, try next */
if (eaf_1.v1_pcm_flag == 0xEE)
granule1_found = 0;
else
granule1_found = 1; /* assume it does (bad infinite loops) */
}
while(!granule1_found);
}
else {
memset(&eaf_1, 0, sizeof(ealayer3_frame_info));
ok = ealayer3_write_pcm_block(stream, data, num_stream, &eaf_1);
if (!ok) goto fail;
stream->offset += eaf_1.eaframe_size;
//;VGM_LOG("s%i: get granule0 done at %lx (eaf_size=%x, common_size=%x)\n", num_stream,stream->offset, eaf_0.eaframe_size, eaf_0.common_size);
if (!ealayer3_skip_data(stream, data, num_stream, 0))
goto fail;
/* in V1a there may be PCM-only frames between granules so read until next one (or parse fails) */
if (eaf_1.common_size > 0)
granule_found = 1;
}
/* rebuild EALayer frame to MPEG frame */
{
os.buf = data->buffer;
os.bufsize = data->buffer_size;
os.buf = ms->buffer;
os.bufsize = ms->buffer_size;
os.b_off = 0;
os.offset = current_offset;
os.info_offset = info_offset;
ok = ealayer3_rebuild_mpeg_frame(&is_0, &eaf_0, &is_1, &eaf_1, &os);
if (!ok) goto fail;
data->bytes_in_buffer = os.b_off / 8; /* wrote full MPEG frame, hopefully */
ms->bytes_in_buffer = os.b_off / 8; /* wrote full MPEG frame, hopefully */
}
return 1;
fail:
return 0;
@ -216,28 +216,26 @@ fail:
static int ealayer3_parse_frame(mpeg_codec_data *data, ealayer3_bitstream *is, ealayer3_frame_info * eaf) {
int ok;
/* make sure as there is re-parsing in loops */
memset(eaf, 0, sizeof(ealayer3_frame_info));
switch(data->type) {
case MPEG_EAL31: ok = ealayer3_parse_frame_v1(is, eaf, data->channels_per_frame); break;
case MPEG_EAL31: ok = ealayer3_parse_frame_v1(is, eaf, data->channels_per_frame, 0); break;
case MPEG_EAL31b: ok = ealayer3_parse_frame_v1(is, eaf, data->channels_per_frame, 1); break;
case MPEG_EAL32P:
case MPEG_EAL32S: ok = ealayer3_parse_frame_v2(is, eaf); break;
default: goto fail;
}
if (!ok) goto fail;
//;VGM_LOG("EAFRAME: v=%i, ch=%i, sr=%i, index=%i / pre=%x, common=%x, pcm=%x, eaframe=%x\n", eaf->version, eaf->channels, eaf->sample_rate, eaf->granule_index, eaf->pre_size, eaf->common_size, eaf->pcm_size, eaf->eaframe_size);
//if (data->type==MPEG_EAL31) VGM_LOG("EAFRAME v1: pcm=%x, unk=%x, number=%x\n", eaf->v1_pcm_flag, eaf->v1_pcm_unknown, eaf->v1_pcm_number);
//else VGM_LOG("EAFRAME v2: stereo=%x, unk=%x, fs=%x, mode=%x, val=%x, number=%x, size=%x\n", eaf->v2_stereo_flag, eaf->v2_unknown, eaf->v2_frame_size, eaf->v2_mode, eaf->v2_mode_value, eaf->v2_pcm_number, eaf->v2_common_size);
return 1;
fail:
return 0;
}
static int ealayer3_parse_frame_v1(ealayer3_bitstream *is, ealayer3_frame_info * eaf, int channels_per_frame) {
/* read V1"a" (in SCHl) and V1"b" (in SNS) EALayer3 frame */
static int ealayer3_parse_frame_v1(ealayer3_bitstream *is, ealayer3_frame_info * eaf, int channels_per_frame, int is_v1b) {
int ok;
/* read EA-frame V1 header */
@ -249,25 +247,30 @@ static int ealayer3_parse_frame_v1(ealayer3_bitstream *is, ealayer3_frame_info *
VGM_LOG("MPEG EAL3 v1: header not 0x00 or 0xEE\n");
goto fail; /* wrong offset? */
}
if (eaf->v1_pcm_flag == 0xEE && !channels_per_frame) {
VGM_LOG("MPEG EAL3 v1: PCM block in first frame\n");
goto fail; /* must know from a prev frame */
}
/* read EA-frame common header (v1a PCM blocks don't have EA-frames, while v1b do) */
if (is_v1b || eaf->v1_pcm_flag == 0x00) {
ok = ealayer3_parse_frame_common(is, eaf);
if (!ok) goto fail;
}
/* check PCM block */
if (eaf->v1_pcm_flag == 0xEE) {
r_bits(is, 16,&eaf->v1_pcm_decode_discard); /* samples to discard of the next decoded (not PCM block) samples */
r_bits(is, 16,&eaf->v1_pcm_number); /* number of PCM samples, can be 0 */
if (!channels_per_frame) {
VGM_LOG("MPEG EAL3 v1: PCM block as first frame\n");
goto fail; /* must know from a prev frame */
}
eaf->pre_size += 2+2; /* 16b+16b */
eaf->pcm_size = (2*eaf->v1_pcm_number * channels_per_frame);
}
else {
/* read EA-frame common header */
ok = ealayer3_parse_frame_common(is, eaf);
if (!ok) goto fail;
if (is_v1b) { /* extra 32b in v1b */
r_bits(is, 32,&eaf->v1_pcm_unknown);
eaf->pre_size += 4; /* 32b */
VGM_ASSERT(eaf->v1_pcm_unknown != 0, "EA EAL3 v1: v1_pcm_unknown not 0\n");
}
}
eaf->eaframe_size = eaf->pre_size + eaf->common_size + eaf->pcm_size;
@ -277,7 +280,8 @@ fail:
return 0;
}
/* read V2"PCM" and V2"Spike" EALayer3 frame (exactly the same but V2P seems to have bigger
* PCM blocks and maybe smaller frames) */
static int ealayer3_parse_frame_v2(ealayer3_bitstream *is, ealayer3_frame_info * eaf) {
int ok;
@ -286,7 +290,7 @@ static int ealayer3_parse_frame_v2(ealayer3_bitstream *is, ealayer3_frame_info *
r_bits(is, 1,&eaf->v2_stereo_flag);
r_bits(is, 2,&eaf->v2_unknown);
r_bits(is, 12,&eaf->v2_frame_size);
eaf->pre_size = 2; /* 16b */
if (eaf->v2_extended_flag) {
@ -294,25 +298,24 @@ static int ealayer3_parse_frame_v2(ealayer3_bitstream *is, ealayer3_frame_info *
r_bits(is, 10,&eaf->v2_mode_value);
r_bits(is, 10,&eaf->v2_pcm_number);
r_bits(is, 10,&eaf->v2_common_size);
eaf->pre_size += 4; /* 32b */
}
/* read EA-frame common header */
ok = ealayer3_parse_frame_common(is, eaf);
if (!ok) goto fail;
//todo maybe v2 frames can be PCM-only like v1
if (!eaf->channels) {
VGM_LOG("MPEG EAL3: v2 frame with no channel number");
goto fail;
if (!eaf->v2_extended_flag || (eaf->v2_extended_flag && eaf->v2_common_size)) {
ok = ealayer3_parse_frame_common(is, eaf);
if (!ok) goto fail;
}
VGM_ASSERT(eaf->v2_extended_flag && eaf->v2_common_size == 0, "EA EAL3: v2 empty frame\n"); /* seen in V2S */
VGM_ASSERT(eaf->v2_extended_flag && eaf->v2_mode_value > 0, "EA EAL3: v2_mode=%x with 0x%x\n", eaf->v2_mode, eaf->v2_mode_value);
//VGM_ASSERT(eaf->v2_pcm_number > 0, "EA EAL3: v2_pcm_number 0x%x\n", eaf->v2_pcm_number);
eaf->pcm_size = (2*eaf->v2_pcm_number * eaf->channels);
eaf->eaframe_size = eaf->pre_size + eaf->common_size + eaf->pcm_size;
if(eaf->v2_frame_size != eaf->eaframe_size) {
if (eaf->v2_frame_size != eaf->eaframe_size) {
VGM_LOG("MPEG EAL3: different v2 frame size vs calculated (0x%x vs 0x%x)\n", eaf->v2_frame_size, eaf->eaframe_size);
goto fail;
}
@ -324,7 +327,7 @@ fail:
}
/* Parses a EALayer3 frame (common part) */
/* parses an EALayer3 frame (common part) */
static int ealayer3_parse_frame_common(ealayer3_bitstream *is, ealayer3_frame_info * eaf) {
/* index tables */
static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 };
@ -393,9 +396,11 @@ static int ealayer3_parse_frame_common(ealayer3_bitstream *is, ealayer3_frame_in
for (i = 0; i < eaf->channels; i++) { /* data size (can be 0, meaning a micro EA-frame) */
eaf->data_size_b += eaf->main_data_size[i];
}
is->b_off += eaf->data_size_b;
if ((eaf->base_size_b+eaf->data_size_b) % 8) /* aligned to closest 8b */
eaf->padding_size_b = 8 - ((eaf->base_size_b+eaf->data_size_b) % 8);
is->b_off += eaf->padding_size_b;
eaf->common_size = (eaf->base_size_b + eaf->data_size_b + eaf->padding_size_b)/8;
@ -406,36 +411,46 @@ fail:
}
/* Converts a EALAYER3 frame to a standard MPEG frame from pre-parsed info */
/* converts an EALAYER3 frame to a standard MPEG frame from pre-parsed info */
static int ealayer3_rebuild_mpeg_frame(ealayer3_bitstream* is_0, ealayer3_frame_info* eaf_0, ealayer3_bitstream* is_1, ealayer3_frame_info* eaf_1, ealayer3_bitstream* os) {
uint32_t c = 0;
int i,j;
int expected_bitrate_index, expected_frame_size;
if (!eaf_0->common_size && !eaf_1->common_size)
return 1; /* empty frames, PCM block only */
/* get bitrate: use max bitrate (320/160) to simplify calcs for now (but some EA-frames use bit reservoir) */
expected_bitrate_index = 0x0E;
if (eaf_0->mpeg1) { /* 44100=0x414, 48000=0x3C0, 32000=0x5A0 */
expected_frame_size = 144l * 320 * 1000l / eaf_0->sample_rate;
} else { /* 22050=0x20A, 24000=0x1E0, 16000=0x2D0, 11025=0x414, 12000=0x3C0, 8000=0x5A0 */
expected_frame_size = 72l * 160 * 1000l / eaf_0->sample_rate;
}
/* ignore PCM-only frames */
if (!eaf_0->common_size)
return 1;
/* extra checks */
if (eaf_0->mpeg1) {
if (!eaf_1
|| eaf_0->mpeg1 != eaf_1->mpeg1
|| eaf_0->version != eaf_1->version
|| eaf_0->granule_index == eaf_1->granule_index
|| !eaf_0->common_size || !eaf_1->common_size) {
VGM_LOG("MPEG EAL3: EA-frames for MPEG1 don't match at 0x%lx\n", os->offset);
goto fail;
}
if (eaf_0->mpeg1 && (!eaf_1
|| eaf_0->mpeg1 != eaf_1->mpeg1
|| eaf_0->version != eaf_1->version
|| eaf_0->granule_index == eaf_1->granule_index
|| !eaf_0->common_size || !eaf_1->common_size)) {
VGM_LOG("MPEG EAL3: EA-frames for MPEG1 don't match at 0x%lx\n", os->info_offset);
goto fail;
}
/* get bitrate: use "free format" (bigger bitrate) to avoid the need of bit reservoir
* this feature is in the spec but some decoders may not support it
* (free format detection is broken in some MP3 in mpg123 < 1.25.8 but works ok) */
expected_bitrate_index = 0x00;
if (eaf_0->mpeg1) {
expected_frame_size = 144l * (320*2) * 1000l / eaf_0->sample_rate;
} else {
expected_frame_size = 72l * (160*2) * 1000l / eaf_0->sample_rate;
}
#if 0
/* this uses max official bitrate (320/160) but some frames need more = complex bit reservoir */
expected_bitrate_index = 0x0E;
if (eaf_0->mpeg1) { /* 320: 44100=0x414, 48000=0x3C0, 32000=0x5A0 */
expected_frame_size = 144l * 320 * 1000l / eaf_0->sample_rate;
} else { /* 160: 22050=0x20A, 24000=0x1E0, 16000=0x2D0, 11025=0x414, 12000=0x3C0, 8000=0x5A0 */
expected_frame_size = 72l * 160 * 1000l / eaf_0->sample_rate;
}
#endif
/* write MPEG1/2 frame header */
w_bits(os, 11, 0x7FF); /* sync */
w_bits(os, 2, eaf_0->version_index);
@ -521,12 +536,8 @@ static int ealayer3_rebuild_mpeg_frame(ealayer3_bitstream* is_0, ealayer3_frame_
if (os->b_off/8 > expected_frame_size) {
VGM_LOG("MPEG EAL3: written 0x%lx but expected less than 0x%x at 0x%lx\n", os->b_off/8, expected_frame_size, os->offset);
//todo bit reservoir! (doesn't seem to affect the output too much)
//;VGM_LOG("EAFRAME: F0 v=%i, ch=%i, sr=%i, index=%i / pre=%x, common=%x, pcm=%x, eaframe=%x\n", eaf_0->version, eaf_0->channels, eaf_0->sample_rate, eaf_0->granule_index, eaf_0->pre_size, eaf_0->common_size, eaf_0->pcm_size, eaf_0->eaframe_size);
//;VGM_LOG("EAFRAME: F1 v=%i, ch=%i, sr=%i, index=%i / pre=%x, common=%x, pcm=%x, eaframe=%x\n", eaf_1->version, eaf_1->channels, eaf_1->sample_rate, eaf_1->granule_index, eaf_1->pre_size, eaf_1->common_size, eaf_1->pcm_size, eaf_1->eaframe_size);
//;VGM_LOGB(os->buf, os->b_off/8, 0);
/* bit reservoir! shouldn't happen with free bitrate, otherwise it's hard to fix as needs complex buffering/calcs */
VGM_LOG("MPEG EAL3: written 0x%lx but expected less than 0x%x at 0x%lx\n", os->b_off/8, expected_frame_size, os->info_offset);
}
else {
/* fill ancillary data (ignored) */
@ -541,7 +552,9 @@ fail:
return 0;
}
/* write PCM block directly to sample buffer (EALayer3 seems to use this as a prefectch of sorts) */
/* write PCM block directly to sample buffer and setup decode discard (EALayer3 seems to use this as a prefetch of sorts).
* Meant to be written inmediatedly, as those PCM are parts that can be found after 1 decoded frame.
* (ex. EA-frame_gr0, PCM-frame_0, EA-frame_gr1, PCM-frame_1 actually writes PCM-frame_0+1, decode of EA-frame_gr0+1 + discard part */
static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, ealayer3_frame_info * eaf) {
mpeg_custom_stream *ms = data->streams[num_stream];
size_t bytes_filled;
@ -552,13 +565,12 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
bytes_filled = sizeof(sample)*ms->samples_filled*data->channels_per_frame;
if (bytes_filled + eaf->pcm_size > ms->output_buffer_size) {
VGM_LOG("MPEG EAL3: can't fill the sample buffer with 0x%x\n", eaf->pcm_size);
goto fail;
VGM_LOG("MPEG EAL3: can't fill the sample buffer with 0x%x\n", eaf->pcm_size);
goto fail;
}
if (eaf->v1_pcm_number) {
//;VGM_LOG("pcm discard = %i, number = %i at 0x%lx\n", eaf->v1_pcm_decode_discard, eaf->v1_pcm_number, stream->offset);
VGM_ASSERT(eaf->v1_pcm_decode_discard > 576, "MPEG EAL3: big discard %i at 0x%lx\n", eaf->v1_pcm_decode_discard, stream->offset);
VGM_ASSERT(eaf->v1_pcm_number > 0x100, "MPEG EAL3: big samples %i at 0x%lx\n", eaf->v1_pcm_number, stream->offset);
@ -576,30 +588,97 @@ static int ealayer3_write_pcm_block(VGMSTREAMCHANNEL *stream, mpeg_codec_data *d
//todo should also discard v1_pcm_number, but block layout samples may be exhausted and won't move (maybe new block if offset = new offset detected)
/* special meanings */
if (decode_to_discard == 576)
decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_number;
if (data->type == MPEG_EAL31) {
if (decode_to_discard == 576)
decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_number;
}
else {
if (decode_to_discard == 0) /* seems ok? */
decode_to_discard += data->samples_per_frame;//+ eaf->v1_pcm_number;
else if (decode_to_discard == 576) /* untested */
decode_to_discard = data->samples_per_frame;//+ eaf->v1_pcm_number;
}
ms->decode_to_discard += decode_to_discard;
}
}
if (eaf->v2_extended_flag) {
if (eaf->v2_pcm_number) {
/* read + write PCM block samples (always BE) */
for (i = 0; i < eaf->v2_pcm_number * data->channels_per_frame; i++) {
off_t pcm_offset = stream->offset + eaf->pre_size + eaf->common_size + sizeof(sample)*i;
int16_t pcm_sample = read_16bitBE(pcm_offset,stream->streamfile);
put_16bitLE(ms->output_buffer + bytes_filled + sizeof(sample)*i, pcm_sample);
}
ms->samples_filled += eaf->v2_pcm_number;
}
#if 0
/* todo supposed skip modes (only seen 0x00):
*
* AB00CCCC CCCCCCCC if A is set: DDEEEEEE EEEEFFFF FFFFFFGG GGGGGGGG
* D = BLOCKOFFSETMODE: IGNORE = 0x0, PRESERVE = 0x1, MUTE = 0x2, MAX = 0x3
* E = samples to discard (mode == 0) or skip (mode == 1 or 2) before outputting the uncompressed samples
* (when mode == 3 this is ignored)
* F = number of uncompressed sample frames (pcm block)
* G = MPEG granule size (can be zero)
*
* if 0: 576 - E if G == 0 then F
* if 1: 576 if G == 0 then F
* if 2: 576 if G == 0 then F * 2
* if 3: 576
*/
/* modify decoded samples depending on flag */
if (eaf->v2_mode == 0x00) {
size_t decode_to_discard = eaf->v2_mode_value;
if (decode_to_discard == 576)
decode_to_discard = data->samples_per_frame;//+ eaf->v2_pcm_number;
ms->decode_to_discard += decode_to_discard;
}
#endif
}
return 1;
fail:
return 0;
}
/* todo V2 (id7 only?) supposed skip modes:
* BLOCKOFFSETMODE: IGNORE = 0x0, PRESERVE = 0x1, MUTE = 0x2, MAX = 0x3
*
* AB00CCCC CCCCCCCC if A is set: DDEEEEEE EEEEFFFF FFFFFFGG GGGGGGGG
* D = mode:
* E = bytes to discard (mode == 0) or skip (mode == 1 or 2) before outputting the uncompressed samples
* (when mode == 3 this is ignored)
* F = number of uncompressed sample frames
* G = MPEG granule size (can be zero)
*
* if 0: 576 - E if G == 0 then F
* if 1: 576 if G == 0 then F
* if 2: 576 if G == 0 then F * 2
* if 3: 576
*/
/* Skip EA-frames from other streams for multichannel (interleaved 1 EA-frame per stream).
* Due to EALayer3 being in blocks and other complexities (we can't go past a block) all
* streams's offsets should start in the first stream's EA-frame.
*
* So to properly read one MPEG-frame from a stream we need to:
* - skip one EA-frame per previous streams until offset is in current stream's EA-frame
* (ie. 1st stream skips 0, 2nd stream skips 1, 3rd stream skips 2)
* - read EA-frame (granule0)
* - skip one EA-frame per following streams until offset is in first stream's EA-frame
* (ie. 1st stream skips 2, 2nd stream skips 1, 3rd stream skips 0)
* - repeat again for granule1
*/
static int ealayer3_skip_data(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream, int at_start) {
int ok, i;
ealayer3_frame_info eaf;
ealayer3_bitstream is = {0};
uint8_t ibuf[EALAYER3_EA_FRAME_BUFFER_SIZE];
int skips = at_start ? num_stream : data->streams_size - 1 - num_stream;
for (i = 0; i < skips; i++) {
is.buf = ibuf;
is.bufsize = read_streamfile(ibuf,stream->offset,EALAYER3_EA_FRAME_BUFFER_SIZE, stream->streamfile); /* reads less at EOF */
is.b_off = 0;
ok = ealayer3_parse_frame(data, &is, &eaf);
if (!ok) goto fail;
stream->offset += eaf.eaframe_size;
}
//;VGM_LOG("s%i: skipped %i frames, now at %lx\n", num_stream,skips,stream->offset);
return 1;
fail:

View File

@ -13,7 +13,7 @@
* - validate of channels between streams
*/
#define MPEG_DATA_BUFFER_SIZE 0x1000 /* at least one MPEG frame (max ~0x5A1) */
#define MPEG_DATA_BUFFER_SIZE 0x1000 /* at least one MPEG frame (max ~0x5A1 plus some more in case of free bitrate) */
static mpg123_handle * init_mpg123_handle();
static void decode_mpeg_standard(VGMSTREAMCHANNEL *stream, mpeg_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels);
@ -101,6 +101,8 @@ mpeg_codec_data *init_mpeg_codec_data(STREAMFILE *streamfile, off_t start_offset
if (channels_per_frame != channels)
goto fail;
/* copy current as open_feed may invalidate until data is fed */
memcpy(&data->mi, &mi, sizeof(struct mpg123_frameinfo));
/* reinit, to ignore the reading done */
mpg123_open_feed(main_m);
@ -123,11 +125,6 @@ mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start
data = calloc(1,sizeof(mpeg_codec_data));
if (!data) goto fail;
data->buffer_size = MPEG_DATA_BUFFER_SIZE;
data->buffer = calloc(sizeof(uint8_t), data->buffer_size);
if (!data->buffer) goto fail;
/* keep around to decode */
data->custom = 1;
data->type = type;
@ -137,6 +134,7 @@ mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start
/* init per subtype */
switch(data->type) {
case MPEG_EAL31:
case MPEG_EAL31b:
case MPEG_EAL32P:
case MPEG_EAL32S: ok = mpeg_custom_setup_init_ealayer3(streamFile, start_offset, data, coding_type); break;
case MPEG_AWC: ok = mpeg_custom_setup_init_awc(streamFile, start_offset, data, coding_type); break;
@ -161,6 +159,11 @@ mpeg_codec_data *init_mpeg_custom_codec_data(STREAMFILE *streamFile, off_t start
data->streams[i]->output_buffer_size = sizeof(sample) * data->channels_per_frame * data->samples_per_frame;
data->streams[i]->output_buffer = calloc(data->streams[i]->output_buffer_size, sizeof(uint8_t));
if (!data->streams[i]->output_buffer) goto fail;
/* one per stream as sometimes mpg123 can't read the whole buffer in one pass */
data->streams[i]->buffer_size = MPEG_DATA_BUFFER_SIZE;
data->streams[i]->buffer = calloc(sizeof(uint8_t), data->streams[i]->buffer_size);
if (!data->streams[i]->buffer) goto fail;
}
@ -367,7 +370,7 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data
int rc, ok;
mpeg_custom_stream *ms = data->streams[num_stream];
//;VGM_LOG("MPEG: decode stream%i @ 0x%08lx (filled=%i, used=%i)\n", num_stream, stream->offset, ms->samples_filled, ms->samples_used);
//;VGM_LOG("MPEG: decode stream%i @ 0x%08lx (filled=%i, used=%i, buffer_full=%i)\n", num_stream, stream->offset, ms->samples_filled, ms->samples_used, ms->buffer_full);
/* wait until samples are depleted, so buffers don't grow too big */
if (ms->samples_filled - ms->samples_used > 0) {
@ -379,46 +382,47 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data
ms->samples_used = 0;
/* extra EOF check for edge cases when the caller tries to read more samples than possible */
if (!data->buffer_full && stream->offset >= stream_size) {
if (!ms->buffer_full && stream->offset >= stream_size) {
VGM_LOG("MPEG: EOF found but more data is requested\n");
goto decode_fail;
}
/* read more raw data (could fill the sample buffer too in some cases, namely EALayer3) */
if (!data->buffer_full) {
if (!ms->buffer_full) {
//;VGM_LOG("MPEG: reading more raw data\n");
switch(data->type) {
case MPEG_EAL31:
case MPEG_EAL31b:
case MPEG_EAL32P:
case MPEG_EAL32S: ok = mpeg_custom_parse_frame_ealayer3(stream, data, num_stream); break;
case MPEG_AHX: ok = mpeg_custom_parse_frame_ahx(stream, data); break;
case MPEG_AHX: ok = mpeg_custom_parse_frame_ahx(stream, data, num_stream); break;
case MPEG_AWC: ok = mpeg_custom_parse_frame_awc(stream, data, num_stream); break;
default: ok = mpeg_custom_parse_frame_default(stream, data); break;
default: ok = mpeg_custom_parse_frame_default(stream, data, num_stream); break;
}
if (!ok) {
VGM_LOG("MPEG: cannot parse frame @ around %lx\n",stream->offset);
goto decode_fail; /* mpg123 could resync but custom MPEGs wouldn't need that */
}
//;VGM_LOG("MPEG: read results: bytes_in_buffer=0x%x, new offset=%lx\n", data->bytes_in_buffer, stream->offset);
//;VGM_LOG("MPEG: read results: bytes_in_buffer=0x%x, new offset=%lx\n", ms->bytes_in_buffer, stream->offset);
/* parse frame may not touch the buffer (only move offset, or fill the sample buffer) */
if (data->bytes_in_buffer) {
data->buffer_full = 1;
data->buffer_used = 0;
if (ms->bytes_in_buffer) {
ms->buffer_full = 1;
ms->buffer_used = 0;
}
}
bytes_filled = sizeof(sample)*ms->samples_filled*data->channels_per_frame;
/* feed new raw data to the decoder if needed, copy decoded results to frame buffer output */
if (!data->buffer_used) {
//;VGM_LOG("MPEG: feed new data and get samples \n");
if (!ms->buffer_used) {
//;VGM_LOG("MPEG: feed new data and get samples\n");
rc = mpg123_decode(ms->m,
data->buffer, data->bytes_in_buffer,
ms->buffer, ms->bytes_in_buffer,
(unsigned char*)ms->output_buffer + bytes_filled, ms->output_buffer_size - bytes_filled,
&bytes_done);
data->buffer_used = 1;
ms->buffer_used = 1;
}
else {
//;VGM_LOG("MPEG: get samples from old data\n");
@ -448,11 +452,12 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL *stream, mpeg_codec_data
/* not enough raw data, set flag to request more next time
* (but only with empty mpg123 buffer, EA blocks wait for all samples decoded before advancing blocks) */
if (!bytes_done && rc == MPG123_NEED_MORE) {
//;VGM_LOG("MPEG: need more raw data to get samples (bytest_done=%x)\n", bytes_done);
data->buffer_full = 0;
//;VGM_LOG("MPEG: need more raw data to get samples (bytes_done=%x)\n", bytes_done);
ms->buffer_full = 0;
}
//;VGM_LOG("MPEG: stream samples now=%i, filled=%i)\n\n", ms->samples_filled, samples_filled);
return;
decode_fail:
@ -478,6 +483,7 @@ void free_mpeg(mpeg_codec_data *data) {
int i;
for (i=0; i < data->streams_size; i++) {
mpg123_delete(data->streams[i]->m);
free(data->streams[i]->buffer);
free(data->streams[i]->output_buffer);
free(data->streams[i]);
}
@ -525,6 +531,8 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
/* seek multistream */
if (!data->custom) {
mpg123_feedseek(data->m, num_sample,SEEK_SET,&input_offset);
/* force first offset as discard-looping needs to start from the beginning */
if (vgmstream->loop_ch)
vgmstream->loop_ch[0].offset = vgmstream->loop_ch[0].channel_start_offset + input_offset;
}
@ -536,7 +544,10 @@ void seek_mpeg(VGMSTREAM *vgmstream, int32_t num_sample) {
data->streams[i]->samples_filled = 0;
data->streams[i]->samples_used = 0;
data->streams[i]->decode_to_discard = 0;
data->streams[i]->buffer_full = 0;
data->streams[i]->buffer_used = 0;
/* force first offset as discard-looping needs to start from the beginning */
if (vgmstream->loop_ch)
vgmstream->loop_ch[i].offset = vgmstream->loop_ch[i].channel_start_offset;
}
@ -567,6 +578,9 @@ void flush_mpeg(mpeg_codec_data * data) {
data->streams[i]->samples_filled = 0;
data->streams[i]->samples_used = 0;
data->streams[i]->decode_to_discard = 0;
data->streams[i]->bytes_in_buffer = 0;
data->streams[i]->buffer_full = 0;
data->streams[i]->buffer_used = 0;
}
data->samples_to_discard = data->skip_samples; /* initial delay */
@ -580,23 +594,20 @@ void flush_mpeg(mpeg_codec_data * data) {
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data *data) {
/* if not found just return 0 and expect to fail (if used for num_samples) */
if (!data->custom) {
struct mpg123_frameinfo mi;
mpg123_handle *m = data->m;
if (m == NULL || MPG123_OK != mpg123_info(m, &mi))
return 0;
/* We would need to read all VBR frames headers to count samples */
if (mi.vbr != MPG123_CBR) //maybe abr_rate could be used to get an approx
if (data->mi.vbr != MPG123_CBR) { //maybe abr_rate could be used to get an approx
VGM_LOG("MPEG: vbr mp3 can't do bytes_to_samples\n");
return 0;
}
return (int64_t)bytes * mi.rate * 8 / (mi.bitrate * 1000);
return (int64_t)bytes * data->mi.rate * 8 / (data->mi.bitrate * 1000);
}
else {
return 0; /* a bit too complex for what is worth */
}
}
#if 0
/* disables/enables stderr output, for MPEG known to contain recoverable errors */
void mpeg_set_error_logging(mpeg_codec_data * data, int enable) {
if (!data->custom) {
@ -609,5 +620,5 @@ void mpeg_set_error_logging(mpeg_codec_data * data, int enable) {
}
}
}
#endif
#endif

View File

@ -21,8 +21,8 @@ int mpeg_custom_setup_init_default(STREAMFILE *streamFile, off_t start_offset, m
int mpeg_custom_setup_init_ealayer3(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type);
int mpeg_custom_setup_init_awc(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type);
int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data);
int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data);
int mpeg_custom_parse_frame_default(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream);
int mpeg_custom_parse_frame_ahx(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream);
int mpeg_custom_parse_frame_ealayer3(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream);
int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream);

View File

@ -50,6 +50,7 @@ vorbis_custom_codec_data * init_vorbis_custom_codec_data(STREAMFILE *streamFile,
case VORBIS_WWISE: ok = vorbis_custom_setup_init_wwise(streamFile, start_offset, data); break;
case VORBIS_OGL: ok = vorbis_custom_setup_init_ogl(streamFile, start_offset, data); break;
case VORBIS_SK: ok = vorbis_custom_setup_init_sk(streamFile, start_offset, data); break;
case VORBIS_VID1: ok = vorbis_custom_setup_init_vid1(streamFile, start_offset, data); break;
default: goto fail;
}
if(!ok) goto fail;
@ -131,6 +132,7 @@ void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t sample
case VORBIS_WWISE: ok = vorbis_custom_parse_packet_wwise(stream, data); break;
case VORBIS_OGL: ok = vorbis_custom_parse_packet_ogl(stream, data); break;
case VORBIS_SK: ok = vorbis_custom_parse_packet_sk(stream, data); break;
case VORBIS_VID1: ok = vorbis_custom_parse_packet_vid1(stream, data); break;
default: goto decode_fail;
}
if(!ok) {
@ -142,8 +144,9 @@ void decode_vorbis_custom(VGMSTREAM * vgmstream, sample * outbuf, int32_t sample
/* parse the fake ogg packet into a logical vorbis block */
rc = vorbis_synthesis(&data->vb,&data->op);
if (rc == OV_ENOTAUDIO) {
VGM_LOG("Vorbis: not an audio packet @ %lx\n",stream->offset);
continue; /* not tested */
VGM_LOG("Vorbis: not an audio packet (size=0x%x) @ %lx\n",(size_t)data->op.bytes,stream->offset);
//VGM_LOGB(data->op.packet, (size_t)data->op.bytes,0);
continue; /* seems ok? */
} else if (rc != 0) {
VGM_LOG("Vorbis: cannot parse Vorbis block @ %lx\n",stream->offset);
goto decode_fail;

View File

@ -9,11 +9,13 @@ int vorbis_custom_setup_init_fsb(STREAMFILE *streamFile, off_t start_offset, vor
int vorbis_custom_setup_init_wwise(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data);
int vorbis_custom_setup_init_ogl(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data);
int vorbis_custom_setup_init_sk(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data);
int vorbis_custom_setup_init_vid1(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data);
int vorbis_custom_parse_packet_fsb(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data);
int vorbis_custom_parse_packet_wwise(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data);
int vorbis_custom_parse_packet_ogl(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data);
int vorbis_custom_parse_packet_sk(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data);
int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data);
#endif/* VGM_USE_VORBIS */
#endif/*_VORBIS_CUSTOM_DECODER_H_ */

View File

@ -0,0 +1,191 @@
#include "vorbis_custom_decoder.h"
#ifdef VGM_USE_VORBIS
#include <vorbis/codec.h>
/* **************************************************************************** */
/* DEFS */
/* **************************************************************************** */
/* An internal struct to pass around and simulate a bitstream. */
typedef struct {
uint8_t * buf; /* buffer to read/write*/
size_t bufsize; /* max size of the buffer */
off_t b_off; /* current offset in bits inside the buffer */
} vgm_bitstream;
static int r_bits(vgm_bitstream * iw, int num_bits, uint32_t * value);
static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size);
static int build_header_comment(uint8_t * buf, size_t bufsize);
/* **************************************************************************** */
/* EXTERNAL API */
/* **************************************************************************** */
/**
* VID1 removes the Ogg layer and uses a block layout with custom packet headers.
*
* Info from hcs's vid1_2ogg: https://github.com/hcs64/vgm_ripping/tree/master/demux/vid1_2ogg
*/
int vorbis_custom_setup_init_vid1(STREAMFILE *streamFile, off_t start_offset, vorbis_custom_codec_data *data) {
off_t offset = start_offset;
size_t packet_size = 0;
/* read header packets (id/setup), each with an VID1 header */
/* normal identificacion packet */
get_packet_header(streamFile, &offset, &packet_size);
if (packet_size > data->buffer_size) goto fail;
data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile);
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse identification header */
offset += packet_size;
/* generate comment packet */
data->op.bytes = build_header_comment(data->buffer, data->buffer_size);
if (!data->op.bytes) goto fail;
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) !=0 ) goto fail; /* parse comment header */
/* normal setup packet */
get_packet_header(streamFile, &offset, &packet_size);
if (packet_size > data->buffer_size) goto fail;
data->op.bytes = read_streamfile(data->buffer,offset,packet_size, streamFile);
if (vorbis_synthesis_headerin(&data->vi, &data->vc, &data->op) != 0) goto fail; /* parse setup header */
offset += packet_size;
return 1;
fail:
return 0;
}
int vorbis_custom_parse_packet_vid1(VGMSTREAMCHANNEL *stream, vorbis_custom_codec_data *data) {
size_t bytes;
/* test block start */
if (read_32bitBE(stream->offset + 0x00,stream->streamfile) == 0x4652414D && /* "FRAM" */
read_32bitBE(stream->offset + 0x20,stream->streamfile) == 0x41554444) { /* "AUDD" */
data->block_offset = stream->offset;
data->block_size = read_32bitBE(stream->offset + 0x2c,stream->streamfile);
stream->offset += 0x34; /* actual start, rest is chunk sizes and maybe granule info */
}
/* get packet info the VID1 header */
get_packet_header(stream->streamfile, &stream->offset, (uint32_t*)&data->op.bytes);
if (data->op.bytes == 0) {
VGM_LOG("VID1 Vorbis: wrong packet (0x%lx) @ %lx\n", data->op.bytes, stream->offset);
goto fail; /* EOF or end padding */
}
/* read raw block */
bytes = read_streamfile(data->buffer,stream->offset, data->op.bytes,stream->streamfile);
stream->offset += data->op.bytes;
if (bytes != data->op.bytes) {
VGM_LOG("VID1 Vorbis: wrong bytes (0x%lx) @ %lx\n", data->op.bytes, stream->offset-bytes);
goto fail; /* wrong packet? */
}
//todo: sometimes there are short packets like 01be590000 and Vorbis complains and skips, no idea
/* test block end (weird size calc but seems ok) */
if ((stream->offset - (data->block_offset + 0x34)) >= (data->block_size - 0x06)) {
stream->offset = data->block_offset + read_32bitBE(data->block_offset + 0x04,stream->streamfile);
}
return 1;
fail:
return 0;
}
/* **************************************************************************** */
/* INTERNAL HELPERS */
/* **************************************************************************** */
static int build_header_comment(uint8_t * buf, size_t bufsize) {
int bytes = 0x19;
if (bytes > bufsize) return 0;
put_8bit (buf+0x00, 0x03); /* packet_type (comments) */
memcpy (buf+0x01, "vorbis", 6); /* id */
put_32bitLE(buf+0x07, 0x09); /* vendor_length */
memcpy (buf+0x0b, "vgmstream", 9); /* vendor_string */
put_32bitLE(buf+0x14, 0x00); /* user_comment_list_length */
put_8bit (buf+0x18, 0x01); /* framing_flag (fixed) */
return bytes;
}
/* read header in Vorbis bitpacking format */
static int get_packet_header(STREAMFILE *streamFile, off_t *offset, size_t *size) {
uint8_t ibuf[0x04]; /* header buffer */
size_t ibufsize = 0x04; /* header ~max */
vgm_bitstream ib = {0};
uint32_t size_bits;
if (read_streamfile(ibuf,(*offset),ibufsize, streamFile) != ibufsize)
goto fail;
ib.buf = ibuf;
ib.bufsize = ibufsize;
ib.b_off = 0;
/* read using Vorbis weird LSF */
r_bits(&ib, 4,&size_bits);
r_bits(&ib, (size_bits+1),(uint32_t*)size);
/* special meaning, seen in silent frames */
if (size_bits == 0 && *size == 0 && (uint8_t)read_8bit(*offset, streamFile)==0x80) {
*size = 0x01;
}
/* pad and convert to byte offset */
if (ib.b_off % 8)
ib.b_off += 8 - (ib.b_off % 8);
*offset += (ib.b_off/8);
return 1;
fail:
return 0;
}
/* Read bits (max 32) from buf and update the bit offset. Vorbis packs values in LSB order and byte by byte.
* (ex. from 2 bytes 00100111 00000001 we can could read 4b=0111 and 6b=010010, 6b=remainder (second value is split into the 2nd byte) */
static int r_bits(vgm_bitstream * ib, int num_bits, uint32_t * value) {
off_t off, pos;
int i, bit_buf, bit_val;
if (num_bits == 0) return 1;
if (num_bits > 32 || num_bits < 0 || ib->b_off + num_bits > ib->bufsize*8) goto fail;
*value = 0; /* set all bits to 0 */
off = ib->b_off / 8; /* byte offset */
pos = ib->b_off % 8; /* bit sub-offset */
for (i = 0; i < num_bits; i++) {
bit_buf = (1U << pos) & 0xFF; /* bit check for buf */
bit_val = (1U << i); /* bit to set in value */
if (ib->buf[off] & bit_buf) /* is bit in buf set? */
*value |= bit_val; /* set bit */
pos++; /* new byte starts */
if (pos%8 == 0) {
pos = 0;
off++;
}
}
ib->b_off += num_bits;
return 1;
fail:
return 0;
}
#endif

View File

@ -110,6 +110,7 @@ static const char* extension_list[] = {
"fag",
"ffw",
"filp",
"flx",
"fsb",
"fwav",
@ -179,6 +180,7 @@ static const char* extension_list[] = {
"mihb",
"mnstr",
//"mp4", //common
"mpc", //FFmpeg, not parsed (musepack)
"mpdsp",
"mpds",
"msa",
@ -272,6 +274,7 @@ static const char* extension_list[] = {
"snd",
"snds",
"sng",
"snr",
"sns",
"snu",
"spd",
@ -317,16 +320,17 @@ static const char* extension_list[] = {
"vawx",
"vb",
"vbk",
"vds",
"vdm",
"vgs",
"vgv",
"vig",
"vds",
"vdm",
"vms",
"voi",
"vpk",
"vs",
"vsf",
"vxn",
"waa",
"wac",
@ -485,6 +489,8 @@ static const coding_info coding_info_list[] = {
{coding_NWA4, "NWA DPCM Level 4"},
{coding_NWA5, "NWA DPCM Level 5"},
{coding_EA_MT, "Electronic Arts MicroTalk"},
{coding_CRI_HCA, "CRI HCA"},
#ifdef VGM_USE_VORBIS
@ -908,6 +914,12 @@ static const meta_info meta_info_list[] = {
{meta_UBI_SB, "Ubisoft SBx header"},
{meta_NAAC, "Namco NAAC header"},
{meta_EZW, "EZ2DJ EZWAVE header"},
{meta_VXN, "Gameloft VXN header"},
{meta_EA_SNR_SNS, "Electronic Arts SNR+SNS header"},
{meta_EA_SPS, "Electronic Arts SPS header"},
{meta_NGC_VID1, "Neversoft VID1 header"},
{meta_PC_FLX, "Ultima IX .FLX header"},
#ifdef VGM_USE_VORBIS
{meta_OGG_VORBIS, "Ogg Vorbis"},

View File

@ -119,6 +119,17 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
break;
/* id, size, samples, offsets-per-channel, flag (0x01 = data start), data */
case coding_EA_MT:
for (i = 0; i < vgmstream->channels; i++) {
off_t channel_start = read_32bit(block_offset + 0x0C + (0x04*i),streamFile);
vgmstream->ch[i].offset = block_offset + 0x0C + (0x04*vgmstream->channels) + channel_start + 0x01;
}
/* flush decoder in every block change */
flush_ea_mt(vgmstream);
break;
#ifdef VGM_USE_MPEG
/* id, size, samples, offset?, unknown (null for MP2, some constant for all blocks for EALayer3) */
case coding_MPEG_custom:
@ -161,14 +172,4 @@ void block_update_ea_schl(off_t block_offset, VGMSTREAM * vgmstream) {
vgmstream->next_block_offset = block_offset + block_size;
vgmstream->current_block_samples = block_samples;
vgmstream->current_block_size = 0; /* uses current_block_samples instead */
/* reset channel sub offset for codecs using it */
if (vgmstream->coding_type == coding_EA_XA
|| vgmstream->coding_type == coding_EA_XA_int
|| vgmstream->coding_type == coding_EA_XA_V2) {
for(i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].channel_start_offset=0;
}
}
}

View File

@ -21,15 +21,25 @@ void block_update_ea_sns(off_t block_offset, VGMSTREAM * vgmstream) {
return;
}
/* known: 0x80 = last block, 0x40, 0x08, 0x04, 0x01 */
/* 0x80: last block
* 0x40: new block for some codecs?
* 0x08: ?
* 0x04: new block for some codecs?
* 0x01: last block for some codecs?
* 0x00: none? */
if (block_size & 0xFF000000) {
VGM_ASSERT(!(block_size & 0x80000000), "EA SNS: unknown flag found at %lx\n", block_offset);
//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;
/* also fix first offset (for EALayer3) */
if (block_offset == vgmstream->ch[i].channel_start_offset) {
vgmstream->ch[i].channel_start_offset = vgmstream->ch[i].offset;
}
}
vgmstream->current_block_offset = block_offset;

View File

@ -357,7 +357,7 @@
>
</File>
<File
RelativePath=".\meta\ea_snu.c"
RelativePath=".\meta\ea_eaac.c"
>
</File>
<File
@ -384,6 +384,10 @@
RelativePath=".\meta\ffw.c"
>
</File>
<File
RelativePath=".\meta\flx.c"
>
</File>
<File
RelativePath=".\meta\fsb.c"
>
@ -625,6 +629,10 @@
<File
RelativePath=".\meta\ngc_ulw.c"
>
</File>
<File
RelativePath=".\meta\ngc_vid1.c"
>
</File>
<File
RelativePath=".\meta\ngca.c"
@ -1198,6 +1206,10 @@
RelativePath=".\meta\vsf.c"
>
</File>
<File
RelativePath=".\meta\vxn.c"
>
</File>
<File
RelativePath=".\meta\waa_wac_wad_wam.c"
>
@ -1385,6 +1397,10 @@
<File
RelativePath=".\coding\coding_utils.c"
>
</File>
<File
RelativePath=".\coding\ea_mt_decoder.c"
>
</File>
<File
RelativePath=".\coding\ea_xa_decoder.c"
@ -1550,6 +1566,10 @@
RelativePath=".\coding\vorbis_custom_utils_sk.c"
>
</File>
<File
RelativePath=".\coding\vorbis_custom_utils_vid1.c"
>
</File>
<File
RelativePath=".\coding\vorbis_custom_utils_wwise.c"
>

View File

@ -225,10 +225,11 @@
<ClCompile Include="meta\ea_schl.c" />
<ClCompile Include="meta\ea_schl_fixed.c" />
<ClCompile Include="meta\ea_1snh.c" />
<ClCompile Include="meta\ea_snu.c" />
<ClCompile Include="meta\ea_eaac.c" />
<ClCompile Include="meta\emff.c" />
<ClCompile Include="meta\exakt_sc.c" />
<ClCompile Include="meta\ffw.c" />
<ClCompile Include="meta\flx.c" />
<ClCompile Include="meta\fsb.c" />
<ClCompile Include="meta\fsb5.c" />
<ClCompile Include="meta\gca.c" />
@ -287,6 +288,7 @@
<ClCompile Include="meta\ngc_tydsp.c" />
<ClCompile Include="meta\ngc_ymf.c" />
<ClCompile Include="meta\ngc_ulw.c" />
<ClCompile Include="meta\ngc_vid1.c" />
<ClCompile Include="meta\nub_xma.c" />
<ClCompile Include="meta\nwa.c" />
<ClCompile Include="meta\ogg_vorbis_file.c" />
@ -405,6 +407,7 @@
<ClCompile Include="meta\ubi_sb.c" />
<ClCompile Include="meta\vs.c" />
<ClCompile Include="meta\vsf.c" />
<ClCompile Include="meta\vxn.c" />
<ClCompile Include="meta\waa_wac_wad_wam.c" />
<ClCompile Include="meta\wii_04sw.c" />
<ClCompile Include="meta\wii_bns.c" />
@ -434,6 +437,7 @@
<ClCompile Include="coding\acm_decoder.c" />
<ClCompile Include="coding\adx_decoder.c" />
<ClCompile Include="coding\aica_decoder.c" />
<ClCompile Include="coding\ea_mt_decoder.c" />
<ClCompile Include="coding\ea_xa_decoder.c" />
<ClCompile Include="coding\ea_xas_decoder.c" />
<ClCompile Include="coding\g719_decoder.c" />
@ -464,6 +468,7 @@
<ClCompile Include="coding\vorbis_custom_utils_fsb.c" />
<ClCompile Include="coding\vorbis_custom_utils_ogl.c" />
<ClCompile Include="coding\vorbis_custom_utils_sk.c" />
<ClCompile Include="coding\vorbis_custom_utils_vid1.c" />
<ClCompile Include="coding\vorbis_custom_utils_wwise.c" />
<ClCompile Include="coding\ws_decoder.c" />
<ClCompile Include="coding\xa_decoder.c" />

View File

@ -211,7 +211,7 @@
<ClCompile Include="meta\ea_1snh.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ea_snu.c">
<ClCompile Include="meta\ea_eaac.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\emff.c">
@ -223,6 +223,9 @@
<ClCompile Include="meta\ffw.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\flx.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\fsb.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -376,6 +379,9 @@
<ClCompile Include="meta\ngc_ulw.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\ngc_vid1.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\nub_xma.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -730,6 +736,9 @@
<ClCompile Include="meta\vsf.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\vxn.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\waa_wac_wad_wam.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -814,6 +823,9 @@
<ClCompile Include="coding\aica_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\ea_mt_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\ea_xa_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
@ -904,6 +916,9 @@
<ClCompile Include="coding\vorbis_custom_utils_sk.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\vorbis_custom_utils_vid1.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\vorbis_custom_utils_wwise.c">
<Filter>coding\Source Files</Filter>
</ClCompile>

253
src/meta/ea_eaac.c Normal file
View File

@ -0,0 +1,253 @@
#include "meta.h"
#include "../layout/layout.h"
#include "../coding/coding.h"
/* EAAudioCore formats, EA's current audio middleware */
static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, STREAMFILE * streamData, off_t header_offset, off_t start_offset, meta_t meta_type);
/* .SNR+SNS - from EA latest games (~2008-2013), v0 header */
VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile) {
VGMSTREAM * vgmstream = NULL;
STREAMFILE * streamData = NULL;
/* check extension, case insensitive */
if (!check_extensions(streamFile,"snr"))
goto fail;
/* SNR headers normally need an external SNS file, but some have data */
if (get_streamfile_size(streamFile) > 0x10) {
/* for Burnout Paradise has this, not sure if extension */
off_t start_offset = (read_32bitBE(0x0c, streamFile) == 0) ? 0x0c : 0x08;
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, 0x00, start_offset, meta_EA_SNR_SNS);
if (!vgmstream) goto fail;
}
else {
streamData = open_stream_ext(streamFile,"sns");
if (!streamData) goto fail;
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamData, 0x00, 0x00, meta_EA_SNR_SNS);
if (!vgmstream) goto fail;
}
if (streamData) close_streamfile(streamData);
return vgmstream;
fail:
if (streamData) close_streamfile(streamData);
return NULL;
}
/* .SPS - from EA latest games (~2014), v1 header */
VGMSTREAM * init_vgmstream_ea_sps(STREAMFILE * streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
/* check extension, case insensitive */
if (!check_extensions(streamFile,"sps"))
goto fail;
/* seems to be fixed */
if ((read_32bitBE(0x00,streamFile) & 0xFFFFFF00) != 0x48000000)
goto fail;
start_offset = read_8bit(0x03, streamFile);
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, 0x04, start_offset, meta_EA_SPS);
if (!vgmstream) goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* .SNU - from EA Redwood Shores/Visceral games (Dead Space, Dante's Inferno, The Godfather 2), v0 header */
VGMSTREAM * init_vgmstream_ea_snu(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, header_offset;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
/* check extension, case insensitive */
if (!check_extensions(streamFile,"snu"))
goto fail;
/* EA SNU header (BE/LE depending on platform) */
/* 0x00(1): related to sample rate? (03=48000)
* 0x01(1): flags/count? (when set has extra block data before start_offset)
* 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) > 0x0000FFFF) {
read_32bit = read_32bitBE;
} else {
read_32bit = read_32bitLE;
}
header_offset = 0x10; /* SNR header */
start_offset = read_32bit(0x08,streamFile); /* SPS blocks */
vgmstream = init_vgmstream_eaaudiocore_header(streamFile, streamFile, header_offset, start_offset, meta_EA_SNU);
if (!vgmstream) goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}
/* EA newest header from RwAudioCore (RenderWare?) / EAAudioCore library (still generated by sx.exe).
* 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 .SPS have headers so .SPH is optional. */
static VGMSTREAM * init_vgmstream_eaaudiocore_header(STREAMFILE * streamHead, STREAMFILE * streamData, off_t header_offset, off_t start_offset, meta_t meta_type) {
VGMSTREAM * vgmstream = NULL;
int channel_count, loop_flag = 0, version, codec, channel_config, sample_rate, flags;
uint32_t num_samples, loop_start = 0, loop_end = 0;
/* EA SNR/SPH header */
version = (read_8bit(header_offset + 0x00,streamHead) >> 4) & 0xf;
codec = (read_8bit(header_offset + 0x00,streamHead) >> 0) & 0xf;
channel_config = read_8bit(header_offset + 0x01,streamHead);
sample_rate = (uint16_t)read_16bitBE(header_offset + 0x02,streamHead);
flags = (uint8_t)read_8bit(header_offset + 0x04,streamHead); /* upper nibble only? */
num_samples = (uint32_t)read_32bitBE(header_offset + 0x04,streamHead) & 0x00FFFFFF;
/* optional, in some headers: 0x08: null? 0x0c: varies (ex. null, full size) */
/* V0: SNR+SNS, V1: SPR+SPS (not apparent differences) */
if (version != 0 && version != 1) {
VGM_LOG("EA SNS/SPS: unknown version\n");
goto fail;
}
/* 0x40: stream asset, 0x20: full loop, 0x00: default/RAM asset, 0x01: loop? */
if (flags != 0x60 && flags != 0x40 && flags != 0x20 && flags != 0x00) {
VGM_LOG("EA SNS/SPS: unknown flag 0x%02x\n", flags);
}
/* seen in sfx and Dead Space ambient tracks */
if (flags & 0x20) {
loop_flag = 1;
loop_start = 0;
loop_end = num_samples;
}
/* accepted channel configs only seem to be mono/stereo/quad/5.1/7.1 */
//channel_count = ((channel_config >> 2) & 0xf) + 1; /* likely, but better fail with unknown values */
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 SNS/SPS: 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_type;
/* EA decoder list and known internal FourCCs */
switch(codec) {
case 0x02: /* "P6B0": PCM16BE (NBA Jam Wii) */
vgmstream->coding_type = coding_PCM16_int;
vgmstream->codec_endian = 1;
vgmstream->layout_type = layout_blocked_ea_sns;
break;
#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 = {0};
stream_size = get_streamfile_size(streamData) - start_offset;
virtual_size = ffmpeg_get_eaxma_virtual_size(vgmstream->channels, start_offset,stream_size, streamData);
block_size = 0x10000; /* todo unused and not correctly done by the parser */
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;
cfg.type = FFMPEG_EA_XMA;
cfg.virtual_size = virtual_size;
cfg.channels = vgmstream->channels;
vgmstream->codec_data = init_ffmpeg_config(streamData, 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 0x04: /* "Xas1": EA-XAS (Dead Space PC/PS3) */
vgmstream->coding_type = coding_EA_XAS;
vgmstream->layout_type = layout_blocked_ea_sns;
break;
#ifdef VGM_USE_MPEG
case 0x05: /* "EL31": EALayer3 v1 (Need for Speed: Hot Pursuit PS3) */
case 0x06: /* "L32P": EALayer3 v2 "PCM" (Battlefield 1943 PS3) */
case 0x07: { /* "L32S": EALayer3 v2 "Spike" (Dante's Inferno PS3) */
mpeg_custom_config cfg = {0};
off_t mpeg_start_offset = start_offset + 0x08;
mpeg_custom_t type = (codec == 0x05 ? MPEG_EAL31b : (codec == 0x06) ? MPEG_EAL32P : MPEG_EAL32S);
/* layout is still blocks, but should work fine with the custom mpeg decoder */
vgmstream->codec_data = init_mpeg_custom_codec_data(streamData, mpeg_start_offset, &vgmstream->coding_type, vgmstream->channels, type, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_blocked_ea_sns;
break;
}
#endif
case 0x00: /* "NONE" (internal 'codec not set' flag) */
case 0x01: /* not used/reserved? Gca0/MP30/P6L0/P2B0/P2L0/P8S0/P8U0/PFN0? */
case 0x08: /* ? */
case 0x09: /* EASpeex (libspeex variant, base versions vary: 1.0.5, 1.2beta3) */
case 0x0a: /* EATrax (ATRAC9 variant, has deflated fillers a la EA-XMA) */
case 0x0b: /* ? */
case 0x0c: /* EAOpus (inside each SNS/SPS block is 16b frame size + standard? Opus packet) */
case 0x0d: /* ? */
case 0x0e: /* ? */
case 0x0f: /* ? */
default:
VGM_LOG("EA SNS/SPS: unknown codec 0x%02x\n", codec);
goto fail;
}
/* open the file for reading by each channel */
if (!vgmstream_open_stream(vgmstream,streamData,start_offset))
goto fail;
if (vgmstream->layout_type == layout_blocked_ea_sns)
block_update_ea_sns(start_offset, vgmstream);
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -140,7 +140,7 @@ VGMSTREAM * init_vgmstream_ea_bnk(STREAMFILE *streamFile) {
goto fail;
/* use header size as endianness flag */
if ((uint32_t)read_32bitLE(0x08,streamFile) > 0x00F00000) {
if ((uint32_t)read_32bitLE(0x08,streamFile) > 0x000F0000) { /* todo not very accurate */
read_32bit = read_32bitBE;
read_16bit = read_16bitBE;
} else {
@ -311,14 +311,12 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
#ifdef VGM_USE_MPEG
case EA_CODEC2_LAYER2: /* MPEG Layer II, aka MP2 */
case EA_CODEC2_LAYER3: { /* MPEG Layer III, aka MP3 */
mpeg_custom_config cfg;
mpeg_custom_config cfg = {0};
off_t mpeg_start_offset = is_bnk ?
start_offset :
get_ea_stream_mpeg_start_offset(streamFile, start_offset, ea);
if (!mpeg_start_offset) goto fail;
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_EA, &cfg);
if (!vgmstream->codec_data) goto fail;
@ -326,14 +324,12 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
}
case EA_CODEC2_EALAYER3: { /* MP3 variant */
mpeg_custom_config cfg;
mpeg_custom_config cfg = {0};
off_t mpeg_start_offset = is_bnk ?
start_offset :
get_ea_stream_mpeg_start_offset(streamFile, start_offset, ea);
if (!mpeg_start_offset) goto fail;
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_EAL31, &cfg);
if (!vgmstream->codec_data) goto fail;
@ -343,6 +339,12 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
case EA_CODEC2_MT10: /* MicroTalk (10:1 compression) */
case EA_CODEC2_MT5: /* MicroTalk (5:1 compression) */
vgmstream->coding_type = coding_EA_MT;
VGM_LOG("mt: codec=%x, cv=%x, v=%x\n", ea->codec2, ea->codec_version, ea->version);
vgmstream->codec_data = init_ea_mt(vgmstream->channels, ea->version == EA_VERSION_V3);
if (!vgmstream->codec_data) goto fail;
break;
case EA_CODEC2_ATRAC3PLUS: /* regular ATRAC3plus chunked in SCxx blocks, including RIFF header */
default:
VGM_LOG("EA SCHl: unknown codec2 0x%02x for platform 0x%02x\n", ea->codec2, ea->platform);
@ -401,15 +403,6 @@ static VGMSTREAM * init_vgmstream_ea_variable_header(STREAMFILE *streamFile, ea_
}
break;
}
/* reset channel sub offset for codecs using it */
if (vgmstream->coding_type == coding_EA_XA
|| vgmstream->coding_type == coding_EA_XA_int
|| vgmstream->coding_type == coding_EA_XA_V2) {
for(i=0;i<vgmstream->channels;i++) {
vgmstream->ch[i].channel_start_offset = 0;
}
}
}
else {
/* setup first block to update offsets */

View File

@ -1,186 +0,0 @@
#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, version, codec, channel_config, sample_rate, flags;
uint32_t num_samples, loop_start = 0, loop_end = 0;
off_t start_offset, header_offset;
int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL;
/* check extension, case insensitive */
if (!check_extensions(streamFile,"snu"))
goto fail;
/* EA SNU header (BE/LE depending on platform) */
/* 0x00(1): related to sample rate? (03=48000)
* 0x01(1): flags/count? (when set has extra block data before start_offset)
* 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) > 0x0000FFFF) {
read_32bit = read_32bitBE;
} else {
read_32bit = read_32bitLE;
}
header_offset = 0x10; /* points to EA SNH/SPH header */
start_offset = read_32bit(0x08,streamFile); /* points to EA SNS/SPS blocks */
/* Beyond is the newest EA header (from EAAudioCore library) still generated by sx.exe.
* 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;
}
/* & 0x40: stream asset?, 0x20: full loop?, 0x00: RAM asset?, 0x01: loop? */
if (flags != 0x60 && flags != 0x40) {
VGM_LOG("EA SNS/SPS: unknown flag 0x%02x\n", flags);
}
/* full loop seen in Dead Space ambient tracks */
if (flags == 0x60) {
VGM_LOG("flg=%x\n",flags);
loop_flag = 1;
loop_start = 0;
loop_end = num_samples;
}
/* accepted channel configs only seem to be mono/stereo/quad/5.1/7.1 */
//channel_count = ((channel_config >> 2) & 0xf) + 1;
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 SNS/SPS: 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;
/* EA decoder list and known internal FourCCs */
switch(codec) {
case 0x04: /* "Xas1": EA-XAS (Dead Space PC/PS3) */
vgmstream->coding_type = coding_EA_XAS;
vgmstream->layout_type = layout_blocked_ea_sns;
break;
#if 0
#ifdef VGM_USE_MPEG
case 0x07: { /* "L32S": 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_blocked_ea_sns;
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(vgmstream->channels, start_offset,stream_size, streamFile);
block_size = 0x10000; /* todo unused and not correctly done by the parser */
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;
cfg.channels = vgmstream->channels;
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" (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 0x06: /* "L32P": EALayer3 v2 "P" */
case 0x08: /* ? */
case 0x09: /* EASpeex? "Esp0" (libspeex variant) */
case 0x0a: /* EATrax (ATRAC9 variant, deflated frames) */
case 0x0b: /* ? */
case 0x0c: /* EAOpus (inside each SNS/SPS block is 16b frame size + Opus standard? packet) */
case 0x0d: /* ? */
case 0x0e: /* ? */
case 0x0f: /* ? */
default:
VGM_LOG("EA SNS/SPS: 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_blocked_ea_sns)
block_update_ea_sns(start_offset, vgmstream);
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

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

@ -0,0 +1,107 @@
#include "meta.h"
#include "../coding/coding.h"
/* FLX - from Ultima IX (.FLX is actually an archive format with sometimes sound data, let's support both anyway) */
VGMSTREAM * init_vgmstream_flx(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, stream_offset = 0;
size_t data_size;
int loop_flag, channel_count, codec;
int total_streams = 0, target_stream = streamFile->stream_index;
/* check extensions (.flx: name of archive, files inside don't have extensions) */
if (!check_extensions(streamFile,"flx"))
goto fail;
/* all spaces up to 0x50 = archive FLX */
if (read_32bitBE(0x00,streamFile) == 0x20202020 && read_32bitBE(0x40,streamFile) == 0x20202020) {
int i;
int entries = read_32bitLE(0x50,streamFile);
off_t offset = 0x80;
if (read_32bitLE(0x54,streamFile) != 0x02
|| read_32bitLE(0x58,streamFile) != get_streamfile_size(streamFile))
goto fail;
if (target_stream == 0) target_stream = 1;
for (i = 0; i < entries; i++) {
off_t entry_offset = read_32bitLE(offset + 0x00, streamFile);
/* 0x04: stream size */
offset += 0x08;
if (entry_offset != 0x00)
total_streams++; /* many entries are empty */
if (total_streams == target_stream && stream_offset == 0)
stream_offset = entry_offset; /* found but let's keep adding total_streams */
}
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
if (stream_offset == 0x00) goto fail;
}
else {
stream_offset = 0x00;
}
if (read_32bitLE(stream_offset + 0x30,streamFile) != 0x10)
goto fail;
data_size = read_32bitLE(stream_offset + 0x28,streamFile);
channel_count = read_32bitLE(stream_offset + 0x34,streamFile);
codec = read_32bitLE(stream_offset + 0x38,streamFile);
loop_flag = (channel_count > 1); /* full seamless repeats in music */
start_offset = stream_offset + 0x3c;
/* 0x00: id */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitLE(stream_offset + 0x2c,streamFile);
vgmstream->num_streams = total_streams;
vgmstream->meta_type = meta_PC_FLX;
switch(codec) {
case 0x00: /* PCM (sfx) */
vgmstream->coding_type = coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16);
break;
case 0x01: /* EA-XA (music, sfx) */
vgmstream->coding_type = channel_count > 1 ? coding_EA_XA : coding_EA_XA_int;
vgmstream->layout_type = layout_none;
vgmstream->num_samples = read_32bitLE(stream_offset + 0x28,streamFile) / 0x0f*channel_count * 28; /* ea_xa_bytes_to_samples */
vgmstream->loop_start_sample = 0;
vgmstream->loop_end_sample = vgmstream->num_samples;
break;
case 0x02: /* EA-MT (voices) */
vgmstream->coding_type = coding_EA_MT;
vgmstream->codec_data = init_ea_mt(vgmstream->channels, 0);
if (!vgmstream->codec_data) goto fail;
vgmstream->num_samples = read_32bitLE(start_offset,streamFile);
start_offset += 0x04;
break;
default:
VGM_LOG("FLX: unknown codec 0x%x\n", codec);
goto fail;
}
read_string(vgmstream->stream_name,0x20+1, stream_offset + 0x04,streamFile);
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -5,7 +5,7 @@
VGMSTREAM * init_vgmstream_maxis_xa(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset;
int loop_flag, channel_count, i;
int loop_flag, channel_count;
/* check extension, case insensitive */
if (!check_extensions(streamFile,"xa"))
@ -18,30 +18,22 @@ VGMSTREAM * init_vgmstream_maxis_xa(STREAMFILE *streamFile) {
loop_flag = 0;
channel_count = read_16bitLE(0x0A,streamFile);
start_offset = 0x18;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
start_offset = 0x18;
vgmstream->channels = channel_count;
vgmstream->sample_rate = read_32bitLE(0x0C,streamFile);
vgmstream->coding_type = coding_MAXIS_XA;
vgmstream->num_samples = read_32bitLE(0x04,streamFile)/2/channel_count;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_MAXIS_XA;
vgmstream->coding_type = coding_MAXIS_XA;
vgmstream->layout_type = layout_none;
/* open streams */
if (!vgmstream_open_stream(vgmstream,streamFile,start_offset))
goto fail;
/* fix channel offsets as needed by the codec (could be simplified) */
for (i = 0; i < channel_count; i++) {
vgmstream->ch[i].channel_start_offset = start_offset+i;
vgmstream->ch[i].offset = 0;
}
return vgmstream;
fail:

View File

@ -693,4 +693,14 @@ VGMSTREAM * init_vgmstream_ubi_sb(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ezw(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_vxn(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_snr_sns(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ea_sps(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_ngc_vid1(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_flx(STREAMFILE * streamFile);
#endif /*_META_H*/

75
src/meta/ngc_vid1.c Normal file
View File

@ -0,0 +1,75 @@
#include "meta.h"
#include "../util.h"
#include "../coding/coding.h"
/* VID1 - from Neversoft games (Gun, Tony Hawk's American Wasteland GC) */
VGMSTREAM * init_vgmstream_ngc_vid1(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
off_t start_offset, header_offset, header_size;
int loop_flag = 0, channel_count;
/* check extension */
if (!check_extensions(streamFile,"ogg,logg"))
goto fail;
/* chunked/blocked format containing video or audio frames */
if (read_32bitBE(0x00,streamFile) != 0x56494431) /* "VID1" */
goto fail;
/* find actual header start/size in the chunks */
{
header_offset = read_32bitBE(0x04, streamFile);
if (read_32bitBE(header_offset,streamFile) != 0x48454144) /* "HEAD" */
goto fail;
start_offset = header_offset + read_32bitBE(header_offset + 0x04, streamFile);
header_offset += 0x0c;
/* videos have VIDH before AUDH, and VIDD in blocks, but aren't parsed ATM */
if (read_32bitBE(header_offset,streamFile) != 0x41554448) /* "AUDH" */
goto fail;
header_size = read_32bitBE(header_offset + 0x04, streamFile);
header_offset += 0x0c;
if (read_32bitBE(header_offset,streamFile) != 0x56415544) /* "VAUD" (Vorbis audio?) */
goto fail;
header_offset += 0x04;
header_size -= 0x10;
}
channel_count = read_8bit(header_offset + 0x04,streamFile);
/* other values unknown, maybe related to vorbis (ex. bitrate/encoding modes) */
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(channel_count,loop_flag);
if (!vgmstream) goto fail;
vgmstream->sample_rate = read_32bitBE(header_offset + 0x00, streamFile);
vgmstream->num_samples = read_32bitBE(header_offset + 0x1c, streamFile);
vgmstream->meta_type = meta_NGC_VID1;
#ifdef VGM_USE_VORBIS
{
vorbis_custom_config cfg = {0};
vgmstream->layout_type = layout_none;
vgmstream->coding_type = coding_VORBIS_custom;
vgmstream->codec_data = init_vorbis_custom_codec_data(streamFile, header_offset + 0x20, VORBIS_VID1, &cfg);
if (!vgmstream->codec_data) goto fail;
}
#else
goto fail;
#endif
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -266,16 +266,22 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
&& strcasecmp("med",filename_extension(filename))
)
{
if (!strcasecmp("mwv",filename_extension(filename)))
if (!strcasecmp("mwv",filename_extension(filename))) {
mwv = 1;
else if (!strcasecmp("sns",filename_extension(filename)))
}
else if (!strcasecmp("sns",filename_extension(filename))) {
sns = 1;
}
#if defined(VGM_USE_MAIATRAC3PLUS) || defined(VGM_USE_FFMPEG)
else if ( check_extensions(streamFile, "at3,rws") ) /* Renamed .RWS AT3 found in Climax games (Silent Hill Origins PSP, Oblivion PSP) */
/* .RWS: AT3 found in Climax games (Silent Hill Origins PSP, Oblivion PSP)
* .AUD: EA Replay */
else if ( check_extensions(streamFile, "at3,rws,aud") ) {
at3 = 1;
}
#endif
else
else {
goto fail;
}
}
/* check header */

View File

@ -571,6 +571,22 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile)
return 1;
}
/* Myst IV Demo (2004)(PC) (final game is different) */
if (sb->version == 0x00100000 && is_sb0) {
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0xa4;
sb->external_flag_offset = 0x24;
sb->num_samples_offset = 0x34;
sb->sample_rate_offset = 0x44;
sb->channels_offset = 0x4c;
sb->stream_type_offset = 0x50;
sb->stream_name_offset = 0x54;
sb->has_internal_names = 1;
return 1;
}
/* Prince of Persia: Warrior Within (2004)(PC) */
if (sb->version == 0x00120009 && is_sb0) {
sb->section1_entry_size = 0x6c;
@ -761,5 +777,36 @@ static int config_sb_header_version(ubi_sb_header * sb, STREAMFILE *streamFile)
return 1;
}
/* TMNT (2007)(PS2) */
if (sb->version == 0x00190002 && is_sb1) {
sb->section1_entry_size = 0x48;
sb->section2_entry_size = 0x5c;
sb->external_flag_offset = 0;
sb->channels_offset = 0x28;
sb->sample_rate_offset = 0x2c;
//sb->num_samples_offset = 0x34 or 0x3c /* varies */
sb->extra_name_offset = 0x44;
sb->stream_type_offset = 0x48;
sb->has_extra_name_flag = 1;
return 1;
}
/* TMNT (2007)(GC) */
if (sb->version == 0x00190002 && is_sb3) { /* same as 0x00190003 */
sb->section1_entry_size = 0x68;
sb->section2_entry_size = 0x6c;
sb->external_flag_offset = 0x28; /* maybe 0x2c */
sb->channels_offset = 0x3c;
sb->sample_rate_offset = 0x40;
sb->num_samples_offset = 0x48;
sb->stream_type_offset = 0x5c;
sb->extra_name_offset = 0x58;
return 1;
}
return 0;
}

102
src/meta/vxn.c Normal file
View File

@ -0,0 +1,102 @@
#include "meta.h"
#include "../coding/coding.h"
/* VXN - from Gameloft mobile games */
VGMSTREAM * init_vgmstream_vxn(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
int loop_flag = 0, channel_count, codec, sample_rate, block_align, bits, num_samples;
off_t start_offset, stream_offset, chunk_offset, first_offset = 0x00;
size_t stream_size;
int total_streams, target_stream = streamFile->stream_index;
/* check extensions */
if (!check_extensions(streamFile,"vxn"))
goto fail;
/* check header/version chunk (RIFF-like format with many custom chunks) */
if (read_32bitBE(0x00,streamFile) != 0x566F784E) /* "VoxN" */
goto fail;
if (read_32bitLE(0x10,streamFile) != get_streamfile_size(streamFile) )
goto fail;
if (!find_chunk_le(streamFile, 0x41666D74,first_offset,0, &chunk_offset,NULL)) /* "Afmt" */
goto fail;
codec = (uint16_t)read_16bitLE(chunk_offset+0x00, streamFile);
channel_count = (uint16_t)read_16bitLE(chunk_offset+0x02, streamFile);
sample_rate = read_32bitLE(chunk_offset+0x04, streamFile);
block_align = (uint16_t)read_16bitLE(chunk_offset+0x08, streamFile);
bits = (uint16_t)read_16bitLE(chunk_offset+0x0a, streamFile);
/* files are divided into segment subsongs, often a leadout and loop in that order
* (the "Plst" and "Rule" chunks may have order info) */
if (!find_chunk_le(streamFile, 0x5365676D,first_offset,0, &chunk_offset,NULL)) /* "Segm" */
goto fail;
total_streams = read_32bitLE(chunk_offset+0x00, streamFile);
if (target_stream == 0) target_stream = 1;
if (target_stream < 0 || target_stream > total_streams || total_streams < 1) goto fail;
stream_offset = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x00, streamFile);
stream_size = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x04, streamFile);
num_samples = read_32bitLE(chunk_offset+0x04 + (target_stream-1)*0x18 + 0x08, streamFile);
if (!find_chunk_le(streamFile, 0x44617461,first_offset,0, &chunk_offset,NULL)) /* "Data" */
goto fail;
start_offset = chunk_offset + stream_offset;
/* 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->num_streams = total_streams;
vgmstream->meta_type = meta_VXN;
switch (codec) {
case 0x0002: /* MSADPCM (ex. Asphalt 7) */
if (bits != 4) goto fail;
vgmstream->coding_type = coding_MSADPCM;
vgmstream->interleave_block_size = block_align;
vgmstream->layout_type = layout_none;
break;
case 0x0011: /* MS-IMA (ex. Asphalt 6) */
if (bits != 16) goto fail;
vgmstream->coding_type = coding_MS_IMA;
vgmstream->interleave_block_size = block_align;
vgmstream->layout_type = layout_none;
break;
#ifdef VGM_USE_FFMPEG
case 0x0800: { /* Musepack (ex. Asphalt Xtreme) */
ffmpeg_codec_data * ffmpeg_data = NULL;
if (bits != 0xFFFF) goto fail;
ffmpeg_data = init_ffmpeg_offset(streamFile, start_offset,stream_size);
if (!ffmpeg_data) goto fail;
vgmstream->codec_data = ffmpeg_data;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
break;
}
#endif
default:
VGM_LOG("VXN: unknown codec 0x%02x\n", codec);
goto fail;
}
/* open the file for reading */
if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) )
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -365,7 +365,12 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_pc_ast,
init_vgmstream_naac,
init_vgmstream_ubi_sb,
init_vgmstream_ezw,
init_vgmstream_ezw,
init_vgmstream_vxn,
init_vgmstream_ea_snr_sns,
init_vgmstream_ea_sps,
init_vgmstream_ngc_vid1,
init_vgmstream_flx,
init_vgmstream_txth, /* should go at the end (lower priority) */
#ifdef VGM_USE_FFMPEG
@ -517,6 +522,10 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
reset_hca(vgmstream);
}
if (vgmstream->coding_type==coding_EA_MT) {
reset_ea_mt(vgmstream);
}
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type==coding_MP4_AAC) {
reset_mp4_aac(vgmstream);
@ -697,6 +706,11 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
vgmstream->codec_data = NULL;
}
if (vgmstream->coding_type==coding_EA_MT) {
free_ea_mt(vgmstream->codec_data);
vgmstream->codec_data = NULL;
}
#ifdef VGM_USE_FFMPEG
if (vgmstream->coding_type==coding_FFmpeg) {
free_ffmpeg(vgmstream->codec_data);
@ -1058,9 +1072,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_EA_XA:
case coding_EA_XA_int:
case coding_EA_XA_V2:
return 28;
case coding_MAXIS_XA:
return 14*vgmstream->channels;
case coding_MAXIS_XA:
return 28;
case coding_EA_XAS:
return 128;
case coding_WS:
@ -1106,11 +1119,13 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_LSF:
return 54;
case coding_MTAF:
return 0x80*2;
return 128*2;
case coding_MTA2:
return 0x80*2;
return 128*2;
case coding_MC3:
return 10;
case coding_EA_MT:
return 432;
case coding_CRI_HCA:
return clHCA_samplesPerBlock;
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
@ -1190,6 +1205,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
case coding_G721:
case coding_SNDS_IMA:
case coding_OTNS_IMA:
return 0;
case coding_UBI_IMA: /* variable (PCM then IMA) */
return 0;
case coding_NGC_AFC:
@ -1215,7 +1231,7 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
case coding_MAXIS_XA:
return 0x0F*vgmstream->channels;
case coding_EA_XA_V2:
return 1; /* the frame is variant in size (ADPCM frames of 0x0F or PCM frames) */
return 0; /* variable (ADPCM frames of 0x0f or PCM frames of 0x3d) */
case coding_EA_XAS:
return 0x4c*vgmstream->channels;
case coding_WS:
@ -1251,7 +1267,9 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
case coding_MTA2:
return 0x90;
case coding_MC3:
return 4;
return 0x04;
case coding_EA_MT:
return 0; /* variable (frames of bit counts or PCM frames) */
default:
return 0;
}
@ -1843,6 +1861,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
chan);
}
break;
case coding_EA_MT:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_ea_mt(vgmstream, buffer+samples_written*vgmstream->channels+chan,
vgmstream->channels, vgmstream->samples_into_block, samples_to_do,
chan);
}
break;
default:
break;
}
@ -1918,6 +1943,10 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
loop_hca(vgmstream);
}
if (vgmstream->coding_type==coding_EA_MT) {
seek_ea_mt(vgmstream, vgmstream->loop_start_sample);
}
#ifdef VGM_USE_VORBIS
if (vgmstream->coding_type==coding_ogg_vorbis) {
seek_ogg_vorbis(vgmstream, vgmstream->loop_sample);
@ -2514,6 +2543,11 @@ int vgmstream_open_stream(VGMSTREAM * vgmstream, STREAMFILE *streamFile, off_t s
}
}
/* EA-MT decoder is a bit finicky and needs this when channel offsets change */
if (vgmstream->coding_type == coding_EA_MT) {
flush_ea_mt(vgmstream);
}
return 1;
fail:

View File

@ -161,6 +161,8 @@ typedef enum {
coding_NWA4,
coding_NWA5,
coding_EA_MT, /* Electronic Arts MicroTalk (linear-predictive speech codec) */
coding_CRI_HCA, /* CRI High Compression Audio (MDCT-based) */
#ifdef VGM_USE_VORBIS
@ -628,7 +630,12 @@ typedef enum {
meta_PC_AST, /* Dead Rising (PC) */
meta_NAAC, /* Namco AAC (3DS) */
meta_UBI_SB, /* Ubisoft banks */
meta_EZW, /* EZ2DJ (Arcade) EZWAV */
meta_EZW, /* EZ2DJ (Arcade) EZWAV */
meta_VXN, /* Gameloft mobile games */
meta_EA_SNR_SNS, /* Electronic Arts SNR+SNS (Burnout Paradise) */
meta_EA_SPS, /* Electronic Arts SPS (Burnout Crash) */
meta_NGC_VID1, /* Neversoft .ogg (Gun GC) */
meta_PC_FLX, /* Ultima IX PC */
#ifdef VGM_USE_VORBIS
meta_OGG_VORBIS, /* Ogg Vorbis */
@ -801,11 +808,12 @@ typedef struct {
/* custom Vorbis modes */
typedef enum {
VORBIS_FSB, /* simplified/external setup packets, custom packet headers */
VORBIS_WWISE, /* many variations (custom setup, headers and data) */
VORBIS_OGL, /* custom packet headers */
VORBIS_SK /* "OggS" replaced by "SK" */
//VORBIS_LYN /* two interleaved Ogg (including setup, duplicated) */
VORBIS_FSB, /* FMOD FSB: simplified/external setup packets, custom packet headers */
VORBIS_WWISE, /* Wwise WEM: many variations (custom setup, headers and data) */
VORBIS_OGL, /* Shin'en OGL: custom packet headers */
VORBIS_SK, /* Silicon Knights AUD: "OggS" replaced by "SK" */
VORBIS_VID1, /* Neversoft VID1: custom packet blocks/headers */
//VORBIS_LYN /* Ubisoft LyN: two interleaved Ogg (including setup, duplicated) */
} vorbis_custom_t;
/* config for Wwise Vorbis (3 types for flexibility though not all combinations exist) */
@ -856,6 +864,9 @@ typedef struct {
uint8_t prev_blockflag; /* blockflag in the last decoded packet */
/* Ogg-style Vorbis: packet within a page */
int current_packet;
/* reference for page/blocks */
off_t block_offset;
size_t block_size;
} vorbis_custom_codec_data;
#endif
@ -870,9 +881,10 @@ typedef enum {
MPEG_FSB, /* N streams of 1 data-frame+padding (=interleave) */
MPEG_P3D, /* N streams of fixed interleave (not frame-aligned) */
MPEG_EA, /* 1 stream (maybe N streams in absolute offsets?) */
MPEG_EAL31, /* EALayer3 v1, custom frames with v1 header */
MPEG_EAL32P, /* EALayer3 v2 "P" (PCM?), custom frames with v2 header */
MPEG_EAL32S, /* EALayer3 v2 "S" (Spike?), custom frames with v2 header */
MPEG_EAL31, /* EALayer3 v1 (SCHl), custom frames with v1 header */
MPEG_EAL31b, /* EALayer3 v1 (SNS), custom frames with v1 header + minor changes */
MPEG_EAL32P, /* EALayer3 v2 "PCM", custom frames with v2 header + bigger PCM blocks? */
MPEG_EAL32S, /* EALayer3 v2 "Spike", custom frames with v2 header + smaller PCM blocks? */
MPEG_LYN, /* N streams of fixed interleave */
MPEG_AWC /* N streams in block layout (music) or absolute offsets (sfx) */
} mpeg_custom_t;
@ -894,6 +906,12 @@ typedef struct {
/* represents a single MPEG stream */
typedef struct {
/* per stream as sometimes mpg123 must be fed in passes if data is big enough (ex. EALayer3 multichannel) */
uint8_t *buffer; /* raw data buffer */
size_t buffer_size;
size_t bytes_in_buffer;
int buffer_full; /* raw buffer has been filled */
int buffer_used; /* raw buffer has been fed to the decoder */
mpg123_handle *m; /* MPEG decoder */
uint8_t *output_buffer; /* decoded samples from this stream (in bytes for mpg123) */
@ -905,16 +923,18 @@ typedef struct {
size_t current_size_target; /* max data, until something happens */
size_t decode_to_discard; /* discard from this stream only (for EALayer3 or AWC) */
} mpeg_custom_stream;
typedef struct {
uint8_t *buffer; /* internal raw data buffer */
/* regular/single MPEG internals */
uint8_t *buffer; /* raw data buffer */
size_t buffer_size;
size_t bytes_in_buffer;
int buffer_full; /* raw buffer has been filled */
int buffer_used; /* raw buffer has been fed to the decoder */
mpg123_handle *m; /* regular/single MPEG decoder */
mpg123_handle *m; /* MPEG decoder */
struct mpg123_frameinfo mi; /* start info, so it's available even when resetting */
/* for internal use, assumed to be constant for all frames */
int channels_per_frame;
@ -1128,6 +1148,12 @@ typedef struct {
#endif
#endif
typedef struct {
int pcm_blocks;
int utk_context_size;
void** utk_context;
} ea_mt_codec_data;
/* -------------------------------------------------------------------------*/
/* vgmstream "public" API */

View File

@ -64,6 +64,7 @@ int main(int argc, char ** argv) {
int i,j,k;
int opt;
/* config */
char * infilename = NULL;
char * outfilename = NULL;
char * outfilename_reset = NULL;
int ignore_loop = 0;
@ -156,6 +157,15 @@ int main(int argc, char ** argv) {
return 1;
}
infilename = argv[optind];
#ifdef WIN32
/* make stdout output work with windows */
if (play_sdtout) {
_setmode(fileno(stdout),_O_BINARY);
}
#endif
if (play_forever && !play_sdtout) {
fprintf(stderr,"A file of infinite size? Not likely.\n");
return 1;
@ -166,13 +176,6 @@ int main(int argc, char ** argv) {
return 1;
}
#ifdef WIN32
/* make stdout output work with windows */
if (play_sdtout) {
_setmode(fileno(stdout),_O_BINARY);
}
#endif
if (ignore_loop && force_loop) {
fprintf(stderr,"-e and -i are incompatible\n");
return 1;
@ -182,25 +185,27 @@ int main(int argc, char ** argv) {
return 1;
}
if (force_loop && really_force_loop) {
fprintf(stderr,"-E and -e are somewhat redundant, are you confused?\n");
fprintf(stderr,"-E and -e are incompatible\n");
return 1;
}
/* manually init streamfile to pass the stream index */
{
//s = init_vgmstream(argv[optind]);
STREAMFILE *streamFile = open_stdio_streamfile(argv[optind]);
if (streamFile) {
streamFile->stream_index = stream_index;
vgmstream = init_vgmstream_from_STREAMFILE(streamFile);
close_streamfile(streamFile);
//s = init_vgmstream(infilename);
STREAMFILE *streamFile = open_stdio_streamfile(infilename);
if (!streamFile) {
fprintf(stderr,"file %s not found\n",infilename);
return 1;
}
}
streamFile->stream_index = stream_index;
vgmstream = init_vgmstream_from_STREAMFILE(streamFile);
close_streamfile(streamFile);
if (!vgmstream) {
fprintf(stderr,"failed opening %s\n",argv[optind]);
return 1;
if (!vgmstream) {
fprintf(stderr,"failed opening %s\n",infilename);
return 1;
}
}
/* force only if there aren't already loop points */
@ -274,10 +279,10 @@ int main(int argc, char ** argv) {
printf("set loop=0\n");
}
else if (print_metaonly) {
printf("metadata for %s\n",argv[optind]);
printf("metadata for %s\n",infilename);
}
else {
printf("decoding %s\n",argv[optind]);
printf("decoding %s\n",infilename);
}
}
if (!play_sdtout && !print_adxencd && !print_oggenc && !print_batchvar) {