Updated VGMStream to r1050-3899-g372cd750
parent
0891a20bba
commit
4298a232fc
|
@ -528,6 +528,11 @@
|
|||
839E21E81F2EDAF100EE54D7 /* mpeg_decoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 839E21DE1F2EDAF000EE54D7 /* mpeg_decoder.h */; };
|
||||
839E21E91F2EDAF100EE54D7 /* vorbis_custom_utils_sk.c in Sources */ = {isa = PBXBuildFile; fileRef = 839E21DF1F2EDAF000EE54D7 /* vorbis_custom_utils_sk.c */; };
|
||||
839E21EB1F2EDB0600EE54D7 /* sk_aud.c in Sources */ = {isa = PBXBuildFile; fileRef = 839E21EA1F2EDB0500EE54D7 /* sk_aud.c */; };
|
||||
839FBFF626C354CE0016A78A /* relic_decoder_lib.h in Headers */ = {isa = PBXBuildFile; fileRef = 839FBFF126C354CE0016A78A /* relic_decoder_lib.h */; };
|
||||
839FBFF726C354CE0016A78A /* relic_decoder_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 839FBFF526C354CE0016A78A /* relic_decoder_lib.c */; };
|
||||
839FBFFB26C354E70016A78A /* wxd_wxh.c in Sources */ = {isa = PBXBuildFile; fileRef = 839FBFF826C354E60016A78A /* wxd_wxh.c */; };
|
||||
839FBFFC26C354E70016A78A /* mp4_faac.c in Sources */ = {isa = PBXBuildFile; fileRef = 839FBFF926C354E70016A78A /* mp4_faac.c */; };
|
||||
839FBFFD26C354E70016A78A /* bnk_relic.c in Sources */ = {isa = PBXBuildFile; fileRef = 839FBFFA26C354E70016A78A /* bnk_relic.c */; };
|
||||
83A16D2B22D2ADE800B90C4C /* awb.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A16D2722D2ADE700B90C4C /* awb.c */; };
|
||||
83A21F7B201D895B000F04B9 /* blocked_xvag.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F7A201D895B000F04B9 /* blocked_xvag.c */; };
|
||||
83A21F85201D8981000F04B9 /* atx.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A21F7C201D897F000F04B9 /* atx.c */; };
|
||||
|
@ -1322,6 +1327,11 @@
|
|||
839E21DE1F2EDAF000EE54D7 /* mpeg_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mpeg_decoder.h; sourceTree = "<group>"; };
|
||||
839E21DF1F2EDAF000EE54D7 /* vorbis_custom_utils_sk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_sk.c; sourceTree = "<group>"; };
|
||||
839E21EA1F2EDB0500EE54D7 /* sk_aud.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sk_aud.c; sourceTree = "<group>"; };
|
||||
839FBFF126C354CE0016A78A /* relic_decoder_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = relic_decoder_lib.h; sourceTree = "<group>"; };
|
||||
839FBFF526C354CE0016A78A /* relic_decoder_lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = relic_decoder_lib.c; sourceTree = "<group>"; };
|
||||
839FBFF826C354E60016A78A /* wxd_wxh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wxd_wxh.c; sourceTree = "<group>"; };
|
||||
839FBFF926C354E70016A78A /* mp4_faac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mp4_faac.c; sourceTree = "<group>"; };
|
||||
839FBFFA26C354E70016A78A /* bnk_relic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bnk_relic.c; sourceTree = "<group>"; };
|
||||
83A16D2722D2ADE700B90C4C /* awb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = awb.c; sourceTree = "<group>"; };
|
||||
83A21F7A201D895B000F04B9 /* blocked_xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_xvag.c; sourceTree = "<group>"; };
|
||||
83A21F7C201D897F000F04B9 /* atx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = atx.c; sourceTree = "<group>"; };
|
||||
|
@ -1674,6 +1684,8 @@
|
|||
83709E0C1ECBC1C3005C03D3 /* psv_decoder.c */,
|
||||
836F6DFA18BDC2180095E648 /* psx_decoder.c */,
|
||||
837CEA7523487E2400E62A4A /* ptadpcm_decoder.c */,
|
||||
839FBFF526C354CE0016A78A /* relic_decoder_lib.c */,
|
||||
839FBFF126C354CE0016A78A /* relic_decoder_lib.h */,
|
||||
8373341223F60C7B00DE14DC /* relic_decoder_mixfft.c */,
|
||||
8373341423F60C7B00DE14DC /* relic_decoder.c */,
|
||||
836F6DFB18BDC2180095E648 /* SASSC_decoder.c */,
|
||||
|
@ -1810,6 +1822,7 @@
|
|||
83299FCE1E7660C7003A3242 /* bik.c */,
|
||||
83031ECF243C50DE00C3F3E0 /* bkhd.c */,
|
||||
837CEAD423487E8300E62A4A /* bmp_konami.c */,
|
||||
839FBFFA26C354E70016A78A /* bnk_relic.c */,
|
||||
834FE0CA215C79E7000A5D3D /* bnk_sony.c */,
|
||||
8373342523F60CDC00DE14DC /* bnsf_keys.h */,
|
||||
836F6E3918BDC2180095E648 /* bnsf.c */,
|
||||
|
@ -1925,6 +1938,7 @@
|
|||
8346D97725BF838C00D1A8B0 /* mjb_mjh.c */,
|
||||
836F6E5F18BDC2180095E648 /* mn_str.c */,
|
||||
8349A9031FE6258100E26435 /* mogg.c */,
|
||||
839FBFF926C354E70016A78A /* mp4_faac.c */,
|
||||
836F6E6018BDC2180095E648 /* mp4.c */,
|
||||
8306B0CB2098458E000302D4 /* msb_msh.c */,
|
||||
832BF81721E0514A006F50F1 /* msf_banpresto.c */,
|
||||
|
@ -2172,6 +2186,7 @@
|
|||
834FE0C9215C79E7000A5D3D /* wv6.c */,
|
||||
836F6F0918BDC2190095E648 /* wvs.c */,
|
||||
83FF0EBB1E93282100C58054 /* wwise.c */,
|
||||
839FBFF826C354E60016A78A /* wxd_wxh.c */,
|
||||
83AB8C741E8072A100086084 /* x360_ast.c */,
|
||||
831BA6151EAC61A500CF89B0 /* x360_cxs.c */,
|
||||
831BA6171EAC61A500CF89B0 /* x360_pasx.c */,
|
||||
|
@ -2327,6 +2342,7 @@
|
|||
83031ED9243C510500C3F3E0 /* ubi_lyn_streamfile.h in Headers */,
|
||||
8373341D23F60C7B00DE14DC /* g7221_decoder_lib.h in Headers */,
|
||||
8306B0E820984590000302D4 /* opus_interleave_streamfile.h in Headers */,
|
||||
839FBFF626C354CE0016A78A /* relic_decoder_lib.h in Headers */,
|
||||
83031EDD243C510500C3F3E0 /* xnb_streamfile.h in Headers */,
|
||||
83D2007B248DDB770048BD24 /* mups_streamfile.h in Headers */,
|
||||
83AA7F8B2519C076004C5298 /* render.h in Headers */,
|
||||
|
@ -2611,6 +2627,7 @@
|
|||
836F6F9418BDC2190095E648 /* ivb.c in Sources */,
|
||||
836F6F8D18BDC2190095E648 /* gsp_gsb.c in Sources */,
|
||||
836F704518BDC2190095E648 /* wvs.c in Sources */,
|
||||
839FBFFB26C354E70016A78A /* wxd_wxh.c in Sources */,
|
||||
8322ECE7240268BB009E9429 /* raw_al.c in Sources */,
|
||||
83EDE5D91A70951A005F5D84 /* btsnd.c in Sources */,
|
||||
8306B0E420984590000302D4 /* wave.c in Sources */,
|
||||
|
@ -2667,6 +2684,7 @@
|
|||
832BF80521E050DC006F50F1 /* blocked_mul.c in Sources */,
|
||||
83A8BADD256679C5000F5F3F /* wady_decoder.c in Sources */,
|
||||
8306B0D920984590000302D4 /* ngc_str_cauldron.c in Sources */,
|
||||
839FBFF726C354CE0016A78A /* relic_decoder_lib.c in Sources */,
|
||||
834FE0FB215C79ED000A5D3D /* xau_konami.c in Sources */,
|
||||
83F0AA6121E2028C004BBC04 /* vsv.c in Sources */,
|
||||
8351F32F2212B57000A606E4 /* dsf.c in Sources */,
|
||||
|
@ -2715,6 +2733,7 @@
|
|||
83AA7F852519C042004C5298 /* zwv.c in Sources */,
|
||||
83EED5D4203A8BC7008BEB45 /* aus.c in Sources */,
|
||||
836F6F7F18BDC2190095E648 /* dmsg_segh.c in Sources */,
|
||||
839FBFFC26C354E70016A78A /* mp4_faac.c in Sources */,
|
||||
832BF81D21E0514B006F50F1 /* msf_tamasoft.c in Sources */,
|
||||
832BF7FF21E050B7006F50F1 /* circus_decoder.c in Sources */,
|
||||
83709E071ECBC1A4005C03D3 /* mss.c in Sources */,
|
||||
|
@ -2992,6 +3011,7 @@
|
|||
836F6F7618BDC2190095E648 /* brstm.c in Sources */,
|
||||
836F700718BDC2190095E648 /* ps2_vgv.c in Sources */,
|
||||
836F704F18BDC2190095E648 /* xwb.c in Sources */,
|
||||
839FBFFD26C354E70016A78A /* bnk_relic.c in Sources */,
|
||||
8346D98525BF83B300D1A8B0 /* compresswave_decoder_lib.c in Sources */,
|
||||
8346D97D25BF838C00D1A8B0 /* compresswave.c in Sources */,
|
||||
834FBCEA26BBC7E50095647F /* piff_tpcm.c in Sources */,
|
||||
|
|
|
@ -310,6 +310,7 @@ void decode_relic(VGMSTREAMCHANNEL* stream, relic_codec_data* data, sample_t* ou
|
|||
void reset_relic(relic_codec_data* data);
|
||||
void seek_relic(relic_codec_data* data, int32_t num_sample);
|
||||
void free_relic(relic_codec_data* data);
|
||||
int32_t relic_bytes_to_samples(size_t bytes, int channels, int bitrate);
|
||||
|
||||
|
||||
/* hca_decoder */
|
||||
|
|
|
@ -1,72 +1,57 @@
|
|||
#include <math.h>
|
||||
#include "coding.h"
|
||||
#include "relic_decoder_lib.h"
|
||||
|
||||
/* Relic Codec decoder, a fairly simple mono-interleave DCT-based codec.
|
||||
*
|
||||
* Decompiled from Relic's dec.exe with some info from Homeworld source code .h/lib
|
||||
* files (released around 2003 through Relic Dev Network), accurate with minor +-1
|
||||
* samples due to double<>float ops or maybe original compiler (Intel's) diffs.
|
||||
*
|
||||
* TODO: clean API, improve validations (can segfault on bad data) and naming
|
||||
*/
|
||||
|
||||
/* mixfft.c */
|
||||
extern void fft(int n, float *xRe, float *xIm, float *yRe, float *yIm);
|
||||
|
||||
static relic_codec_data* init_codec(int channels, int bitrate, int codec_rate);
|
||||
static int decode_frame_next(VGMSTREAMCHANNEL* stream, relic_codec_data* data);
|
||||
static void copy_samples(relic_codec_data* data, sample_t* outbuf, int32_t samples_to_get);
|
||||
static void reset_codec(relic_codec_data* data);
|
||||
|
||||
#define RELIC_MAX_CHANNELS 2
|
||||
#define RELIC_MAX_SCALES 6
|
||||
#define RELIC_BASE_SCALE 10.0f
|
||||
#define RELIC_FREQUENCY_MASKING_FACTOR 1.0f
|
||||
#define RELIC_CRITICAL_BAND_COUNT 27
|
||||
#define RELIC_PI 3.14159265358979323846f
|
||||
#define RELIC_SIZE_LOW 128
|
||||
#define RELIC_SIZE_MID 256
|
||||
#define RELIC_SIZE_HIGH 512
|
||||
#define RELIC_MAX_SIZE RELIC_SIZE_HIGH
|
||||
#define RELIC_MAX_FREQ (RELIC_MAX_SIZE / 2)
|
||||
#define RELIC_MAX_FFT (RELIC_MAX_SIZE / 4)
|
||||
#define RELIC_BITRATE_22 256
|
||||
#define RELIC_BITRATE_44 512
|
||||
#define RELIC_BITRATE_88 1024
|
||||
#define RELIC_BITRATE_176 2048
|
||||
#define RELIC_MAX_FRAME_SIZE ((RELIC_BITRATE_176 / 8) + 0x04) /* extra 0x04 for the bitreader */
|
||||
|
||||
//TODO: fix looping
|
||||
|
||||
struct relic_codec_data {
|
||||
/* decoder info */
|
||||
relic_handle_t* handle;
|
||||
int channels;
|
||||
int frame_size;
|
||||
int wave_size;
|
||||
int freq_size;
|
||||
int dct_mode;
|
||||
int samples_mode;
|
||||
/* decoder init state */
|
||||
float scales[RELIC_MAX_SCALES]; /* quantization scales */
|
||||
float dct[RELIC_MAX_SIZE];
|
||||
float window[RELIC_MAX_SIZE];
|
||||
/* decoder frame state */
|
||||
uint8_t exponents[RELIC_MAX_CHANNELS][RELIC_MAX_FREQ]; /* quantization/scale indexes */
|
||||
float freq1[RELIC_MAX_FREQ]; /* dequantized spectrum */
|
||||
float freq2[RELIC_MAX_FREQ];
|
||||
float wave_cur[RELIC_MAX_CHANNELS][RELIC_MAX_SIZE]; /* current frame samples */
|
||||
float wave_prv[RELIC_MAX_CHANNELS][RELIC_MAX_SIZE]; /* previous frame samples */
|
||||
|
||||
/* sample state */
|
||||
int32_t samples_discard;
|
||||
int32_t samples_consumed;
|
||||
int32_t samples_filled;
|
||||
};
|
||||
|
||||
/* ************************************* */
|
||||
|
||||
|
||||
relic_codec_data* init_relic(int channels, int bitrate, int codec_rate) {
|
||||
return init_codec(channels, bitrate, codec_rate);
|
||||
relic_codec_data* data = NULL;
|
||||
|
||||
data = calloc(1, sizeof(relic_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->handle = relic_init(channels, bitrate, codec_rate);
|
||||
if (!data->handle) goto fail;
|
||||
|
||||
data->channels = channels;
|
||||
data->frame_size = relic_get_frame_size(data->handle);
|
||||
|
||||
return data;
|
||||
fail:
|
||||
free_relic(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int decode_frame_next(VGMSTREAMCHANNEL* stream, relic_codec_data* data) {
|
||||
int ch;
|
||||
int bytes;
|
||||
int ok;
|
||||
uint8_t buf[RELIC_BUFFER_SIZE];
|
||||
|
||||
for (ch = 0; ch < data->channels; ch++) {
|
||||
bytes = read_streamfile(buf, stream->offset, data->frame_size, stream->streamfile);
|
||||
if (bytes != data->frame_size) goto fail;
|
||||
stream->offset += data->frame_size;
|
||||
|
||||
ok = relic_decode_frame(data->handle, buf, ch);
|
||||
if (!ok) goto fail;
|
||||
}
|
||||
|
||||
data->samples_consumed = 0;
|
||||
data->samples_filled = RELIC_SAMPLES_PER_FRAME;
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
void decode_relic(VGMSTREAMCHANNEL* stream, relic_codec_data* data, sample_t* outbuf, int32_t samples_to_do) {
|
||||
|
@ -88,7 +73,7 @@ void decode_relic(VGMSTREAMCHANNEL* stream, relic_codec_data* data, sample_t* ou
|
|||
if (samples_to_get > samples_to_do)
|
||||
samples_to_get = samples_to_do;
|
||||
|
||||
copy_samples(data, outbuf, samples_to_get);
|
||||
relic_get_pcm16(data->handle, outbuf, samples_to_get, data->samples_consumed);
|
||||
|
||||
samples_to_do -= samples_to_get;
|
||||
outbuf += samples_to_get * data->channels;
|
||||
|
@ -113,7 +98,7 @@ decode_fail:
|
|||
void reset_relic(relic_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
reset_codec(data);
|
||||
relic_reset(data->handle);
|
||||
data->samples_filled = 0;
|
||||
data->samples_consumed = 0;
|
||||
data->samples_discard = 0;
|
||||
|
@ -129,378 +114,10 @@ void seek_relic(relic_codec_data* data, int32_t num_sample) {
|
|||
void free_relic(relic_codec_data* data) {
|
||||
if (!data) return;
|
||||
|
||||
relic_free(data->handle);
|
||||
free(data);
|
||||
}
|
||||
|
||||
/* ***************************************** */
|
||||
|
||||
static const int16_t critical_band_data[RELIC_CRITICAL_BAND_COUNT] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
9, 11, 13, 15, 17, 20, 23, 27,
|
||||
31, 37, 43, 51, 62, 74, 89, 110,
|
||||
139, 180, 256
|
||||
};
|
||||
|
||||
static void init_dct(float *dct, int dct_size) {
|
||||
int i;
|
||||
int dct_quarter = dct_size >> 2;
|
||||
|
||||
for (i = 0; i < dct_quarter; i++) {
|
||||
double temp = ((float)i + 0.125f) * (RELIC_PI * 2.0f) * (1.0f / (float)dct_size);
|
||||
dct[i] = sin(temp);
|
||||
dct[dct_quarter + i] = cos(temp);
|
||||
}
|
||||
}
|
||||
|
||||
static int apply_idct(const float *freq, float *wave, const float *dct, int dct_size) {
|
||||
int i;
|
||||
float factor;
|
||||
float out_re[RELIC_MAX_FFT];
|
||||
float out_im[RELIC_MAX_FFT];
|
||||
float in_re[RELIC_MAX_FFT];
|
||||
float in_im[RELIC_MAX_FFT];
|
||||
float wave_tmp[RELIC_MAX_SIZE];
|
||||
int dct_half = dct_size >> 1;
|
||||
int dct_quarter = dct_size >> 2;
|
||||
int dct_3quarter = 3 * (dct_size >> 2);
|
||||
|
||||
/* prerotation? */
|
||||
for (i = 0; i < dct_quarter; i++) {
|
||||
float coef1 = freq[2 * i] * 0.5f;
|
||||
float coef2 = freq[dct_half - 1 - 2 * i] * 0.5f;
|
||||
in_re[i] = coef1 * dct[dct_quarter + i] + coef2 * dct[i];
|
||||
in_im[i] = -coef1 * dct[i] + coef2 * dct[dct_quarter + i];
|
||||
}
|
||||
|
||||
/* main FFT */
|
||||
fft(dct_quarter, in_re, in_im, out_re, out_im);
|
||||
|
||||
/* postrotation, window and reorder? */
|
||||
factor = 8.0 / sqrt(dct_size);
|
||||
for (i = 0; i < dct_quarter; i++) {
|
||||
float out_re_i = out_re[i];
|
||||
out_re[i] = (out_re[i] * dct[dct_quarter + i] + out_im[i] * dct[i]) * factor;
|
||||
out_im[i] = (-out_re_i * dct[i] + out_im[i] * dct[dct_quarter + i]) * factor;
|
||||
wave_tmp[i * 2] = out_re[i];
|
||||
wave_tmp[i * 2 + dct_half] = out_im[i];
|
||||
}
|
||||
for (i = 1; i < dct_size; i += 2) {
|
||||
wave_tmp[i] = -wave_tmp[dct_size - 1 - i];
|
||||
}
|
||||
|
||||
/* wave mix thing? */
|
||||
for (i = 0; i < dct_3quarter; i++) {
|
||||
wave[i] = wave_tmp[dct_quarter + i];
|
||||
}
|
||||
for (i = dct_3quarter; i < dct_size; i++) {
|
||||
wave[i] = -wave_tmp[i - dct_3quarter];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void decode_frame(const float *freq1, const float *freq2, float *wave_cur, float *wave_prv, const float *dct, const float *window, int dct_size) {
|
||||
int i;
|
||||
float wave_tmp[RELIC_MAX_SIZE];
|
||||
int dct_half = dct_size >> 1;
|
||||
|
||||
/* copy for first half(?) */
|
||||
memcpy(wave_cur, wave_prv, RELIC_MAX_SIZE * sizeof(float));
|
||||
|
||||
/* transform frequency domain to time domain with DCT/FFT */
|
||||
apply_idct(freq1, wave_tmp, dct, dct_size);
|
||||
apply_idct(freq2, wave_prv, dct, dct_size);
|
||||
|
||||
/* overlap and apply window function to filter this block's beginning */
|
||||
for (i = 0; i < dct_half; i++) {
|
||||
wave_cur[dct_half + i] = wave_tmp[i] * window[i] + wave_cur[dct_half + i] * window[dct_half + i];
|
||||
wave_prv[i] = wave_prv[i] * window[i] + wave_tmp[dct_half + i] * window[dct_half + i];
|
||||
}
|
||||
}
|
||||
|
||||
static void init_window(float *window, int dct_size) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dct_size; i++) {
|
||||
window[i] = sin((float)i * (RELIC_PI / dct_size));
|
||||
}
|
||||
}
|
||||
|
||||
static void decode_frame_base(const float *freq1, const float *freq2, float *wave_cur, float *wave_prv, const float *dct, const float *window, int dct_mode, int samples_mode) {
|
||||
int i;
|
||||
float wave_tmp[RELIC_MAX_SIZE];
|
||||
|
||||
/* dec_relic only uses 512/512 mode, source references 256/256 (effects only?) too */
|
||||
|
||||
if (samples_mode == RELIC_SIZE_LOW) {
|
||||
{
|
||||
/* 128 DCT to 128 samples */
|
||||
decode_frame(freq1, freq2, wave_cur, wave_prv, dct, window, RELIC_SIZE_LOW);
|
||||
}
|
||||
}
|
||||
else if (samples_mode == RELIC_SIZE_MID) {
|
||||
if (dct_mode == RELIC_SIZE_LOW) {
|
||||
/* 128 DCT to 256 samples (repeat sample x2) */
|
||||
decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_LOW);
|
||||
for (i = 0; i < 256 - 1; i += 2) {
|
||||
wave_cur[i + 0] = wave_tmp[i >> 1];
|
||||
wave_cur[i + 1] = wave_tmp[i >> 1];
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* 256 DCT to 256 samples */
|
||||
decode_frame(freq1, freq2, wave_cur, wave_prv, dct, window, RELIC_SIZE_MID);
|
||||
}
|
||||
}
|
||||
else if (samples_mode == RELIC_SIZE_HIGH) {
|
||||
if (dct_mode == RELIC_SIZE_LOW) {
|
||||
/* 128 DCT to 512 samples (repeat sample x4) */
|
||||
decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_LOW);
|
||||
for (i = 0; i < 512 - 1; i += 4) {
|
||||
wave_cur[i + 0] = wave_tmp[i >> 2];
|
||||
wave_cur[i + 1] = wave_tmp[i >> 2];
|
||||
wave_cur[i + 2] = wave_tmp[i >> 2];
|
||||
wave_cur[i + 3] = wave_tmp[i >> 2];
|
||||
}
|
||||
}
|
||||
else if (dct_mode == RELIC_SIZE_MID) {
|
||||
/* 256 DCT to 512 samples (repeat sample x2) */
|
||||
decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_MID);
|
||||
for (i = 0; i < 512 - 1; i += 2) {
|
||||
wave_cur[i + 0] = wave_tmp[i >> 1];
|
||||
wave_cur[i + 1] = wave_tmp[i >> 1];
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* 512 DCT to 512 samples */
|
||||
decode_frame(freq1, freq2, wave_cur, wave_prv, dct, window, RELIC_SIZE_HIGH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* reads 32b max, packed in LSB order per byte (like Vorbis), ex.
|
||||
* with 0x45 6A=01000101 01101010 could read 4b=0101, 6b=100100, 3b=010 ...
|
||||
* assumes buf has enough extra bits to read 32b (size +0x04) */
|
||||
static uint32_t read_ubits(uint8_t bits, uint32_t offset, uint8_t *buf) {
|
||||
uint32_t shift, mask, pos, val;
|
||||
|
||||
shift = offset - 8 * (offset / 8);
|
||||
mask = (1 << bits) - 1;
|
||||
pos = offset / 8;
|
||||
val = (buf[pos+0]) | (buf[pos+1]<<8) | (buf[pos+2]<<16) | (buf[pos+3]<<24);
|
||||
return (val >> shift) & mask;
|
||||
}
|
||||
static int read_sbits(uint8_t bits, uint32_t offset, uint8_t *buf) {
|
||||
uint32_t val = read_ubits(bits, offset, buf);
|
||||
int outval;
|
||||
if (val >> (bits - 1) == 1) { /* upper bit = sign */
|
||||
uint32_t mask = (1 << (bits - 1)) - 1;
|
||||
outval = (int)(val & mask);
|
||||
outval = -outval;
|
||||
}
|
||||
else {
|
||||
outval = (int)val;
|
||||
}
|
||||
return outval;
|
||||
}
|
||||
|
||||
static void init_dequantization(float* scales) {
|
||||
int i;
|
||||
|
||||
scales[0] = RELIC_BASE_SCALE;
|
||||
for (i = 1; i < RELIC_MAX_SCALES; i++) {
|
||||
scales[i] = scales[i - 1] * scales[0];
|
||||
}
|
||||
for (i = 0; i < RELIC_MAX_SCALES; i++) {
|
||||
scales[i] = RELIC_FREQUENCY_MASKING_FACTOR / (double) ((1 << (i + 1)) - 1) * scales[i];
|
||||
}
|
||||
}
|
||||
|
||||
static void unpack_frame(uint8_t *buf, int buf_size, float *freq1, float *freq2, const float* scales, uint8_t *exponents, int freq_size) {
|
||||
uint8_t flags, cb_bits, ev_bits, ei_bits, qv_bits;
|
||||
int qv;
|
||||
uint8_t ev;
|
||||
uint8_t move, pos;
|
||||
uint32_t bit_offset;
|
||||
int i, j;
|
||||
int freq_half = freq_size >> 1;
|
||||
|
||||
|
||||
memset(freq1, 0, RELIC_MAX_FREQ * sizeof(float));
|
||||
memset(freq2, 0, RELIC_MAX_FREQ * sizeof(float));
|
||||
|
||||
flags = read_ubits(2u, 0u, buf);
|
||||
cb_bits = read_ubits(3u, 2u, buf);
|
||||
ev_bits = read_ubits(2u, 5u, buf);
|
||||
ei_bits = read_ubits(4u, 7u, buf);
|
||||
bit_offset = 11;
|
||||
|
||||
/* reset exponents indexes */
|
||||
if ((flags & 1) == 1) {
|
||||
memset(exponents, 0, RELIC_MAX_FREQ);
|
||||
}
|
||||
|
||||
/* read packed exponents indexes for all bands */
|
||||
if (cb_bits > 0 && ev_bits > 0) {
|
||||
pos = 0;
|
||||
for (i = 0; i < RELIC_CRITICAL_BAND_COUNT - 1; i++) {
|
||||
if (bit_offset >= 8u*buf_size)
|
||||
break;
|
||||
|
||||
move = read_ubits(cb_bits, bit_offset, buf);
|
||||
bit_offset += cb_bits;
|
||||
if (i > 0 && move == 0)
|
||||
break;
|
||||
pos += move;
|
||||
|
||||
ev = read_ubits(ev_bits, bit_offset, buf);
|
||||
bit_offset += ev_bits;
|
||||
for (j = critical_band_data[pos]; j < critical_band_data[pos + 1]; j++)
|
||||
exponents[j] = ev;
|
||||
}
|
||||
}
|
||||
|
||||
/* read quantized values */
|
||||
if (freq_half > 0 && ei_bits > 0) {
|
||||
|
||||
/* read first part */
|
||||
pos = 0;
|
||||
for (i = 0; i < RELIC_MAX_FREQ; i++) {
|
||||
if (bit_offset >= 8u*buf_size)
|
||||
break;
|
||||
|
||||
move = read_ubits(ei_bits, bit_offset, buf);
|
||||
bit_offset += ei_bits;
|
||||
if (i > 0 && move == 0)
|
||||
break;
|
||||
pos += move;
|
||||
|
||||
qv_bits = exponents[pos];
|
||||
qv = read_sbits(qv_bits + 2u, bit_offset, buf);
|
||||
bit_offset += qv_bits + 2u;
|
||||
|
||||
if (qv != 0 && pos < freq_half && qv_bits < 6)
|
||||
freq1[pos] = (float)qv * scales[qv_bits];
|
||||
}
|
||||
|
||||
/* read second part, or clone it */
|
||||
if ((flags & 2) == 2) {
|
||||
memcpy(freq2, freq1, RELIC_MAX_FREQ * sizeof(float));
|
||||
}
|
||||
else {
|
||||
pos = 0;
|
||||
for (i = 0; i < RELIC_MAX_FREQ; i++) {
|
||||
if (bit_offset >= 8u*buf_size)
|
||||
break;
|
||||
|
||||
move = read_ubits(ei_bits, bit_offset, buf);
|
||||
bit_offset += ei_bits;
|
||||
if (i > 0 && move == 0)
|
||||
break;
|
||||
pos += move;
|
||||
|
||||
qv_bits = exponents[pos];
|
||||
qv = read_sbits(qv_bits + 2u, bit_offset, buf);
|
||||
bit_offset += qv_bits + 2u;
|
||||
|
||||
if (qv != 0 && pos < freq_half && qv_bits < 6)
|
||||
freq2[pos] = (float)qv * scales[qv_bits];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int decode_frame_next(VGMSTREAMCHANNEL* stream, relic_codec_data* data) {
|
||||
int ch;
|
||||
int bytes;
|
||||
uint8_t buf[RELIC_MAX_FRAME_SIZE];
|
||||
|
||||
for (ch = 0; ch < data->channels; ch++) {
|
||||
/* clean extra bytes for bitreader */
|
||||
memset(buf + data->frame_size, 0, RELIC_MAX_FRAME_SIZE - data->frame_size);
|
||||
|
||||
bytes = read_streamfile(buf, stream->offset, data->frame_size, stream->streamfile);
|
||||
if (bytes != data->frame_size) goto fail;
|
||||
stream->offset += data->frame_size;
|
||||
|
||||
unpack_frame(buf, sizeof(buf), data->freq1, data->freq2, data->scales, data->exponents[ch], data->freq_size);
|
||||
|
||||
decode_frame_base(data->freq1, data->freq2, data->wave_cur[ch], data->wave_prv[ch], data->dct, data->window, data->dct_mode, data->samples_mode);
|
||||
}
|
||||
|
||||
data->samples_consumed = 0;
|
||||
data->samples_filled = data->wave_size;
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void copy_samples(relic_codec_data* data, sample_t* outbuf, int32_t samples) {
|
||||
int s, ch;
|
||||
int ichs = data->channels;
|
||||
int skip = data->samples_consumed;
|
||||
for (ch = 0; ch < ichs; ch++) {
|
||||
for (s = 0; s < samples; s++) {
|
||||
double d64_sample = data->wave_cur[ch][skip + s];
|
||||
int pcm_sample = clamp16((int32_t)d64_sample);
|
||||
|
||||
/* f32 in PCM 32767.0 .. -32768.0 format, original code
|
||||
* does some custom double-to-int rint() though */
|
||||
//FQ_BNUM ((float)(1<<26)*(1<<26)*1.5)
|
||||
//rint(x) ((d64 = (double)(x)+FQ_BNUM), *(int*)(&d64))
|
||||
|
||||
outbuf[s*ichs + ch] = pcm_sample;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static relic_codec_data* init_codec(int channels, int bitrate, int codec_rate) {
|
||||
relic_codec_data *data = NULL;
|
||||
|
||||
if (channels > RELIC_MAX_CHANNELS)
|
||||
goto fail;
|
||||
|
||||
data = calloc(1, sizeof(relic_codec_data));
|
||||
if (!data) goto fail;
|
||||
|
||||
data->channels = channels;
|
||||
|
||||
/* dequantized freq1+2 size (separate from DCT) */
|
||||
if (codec_rate < 22050) /* probably 11025 only */
|
||||
data->freq_size = RELIC_SIZE_LOW;
|
||||
if (codec_rate == 22050)
|
||||
data->freq_size = RELIC_SIZE_MID;
|
||||
if (codec_rate > 22050) /* probably 44100 only */
|
||||
data->freq_size = RELIC_SIZE_HIGH;
|
||||
|
||||
/* default for streams (only a few mode combos are valid, see decode) */
|
||||
data->wave_size = RELIC_SIZE_HIGH;
|
||||
data->dct_mode = RELIC_SIZE_HIGH;
|
||||
data->samples_mode = RELIC_SIZE_HIGH;
|
||||
|
||||
init_dct(data->dct, RELIC_SIZE_HIGH);
|
||||
init_window(data->window, RELIC_SIZE_HIGH);
|
||||
init_dequantization(data->scales);
|
||||
memset(data->wave_prv, 0, RELIC_MAX_CHANNELS * RELIC_MAX_SIZE * sizeof(float));
|
||||
|
||||
switch(bitrate) {
|
||||
case RELIC_BITRATE_22:
|
||||
case RELIC_BITRATE_44:
|
||||
case RELIC_BITRATE_88:
|
||||
case RELIC_BITRATE_176:
|
||||
data->frame_size = (bitrate / 8); /* 0x100 and 0x80 are common */
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
return data;
|
||||
fail:
|
||||
free_relic(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void reset_codec(relic_codec_data* data) {
|
||||
memset(data->wave_prv, 0, RELIC_MAX_CHANNELS * RELIC_MAX_SIZE * sizeof(float));
|
||||
int32_t relic_bytes_to_samples(size_t bytes, int channels, int bitrate) {
|
||||
return bytes / channels / (bitrate / 8) * RELIC_SAMPLES_PER_FRAME;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,452 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "relic_decoder_lib.h"
|
||||
|
||||
/* Relic Codec decoder, a fairly simple mono-interleave DCT-based codec.
|
||||
*
|
||||
* Decompiled from Relic's dec.exe with some info from Homeworld source code .h/lib
|
||||
* files (released around 2003 through Relic Dev Network), accurate with minor +-1
|
||||
* samples due to double<>float ops or maybe original compiler (Intel's) diffs.
|
||||
*/
|
||||
|
||||
/* mixfft.c */
|
||||
extern void fft(int n, float* xRe, float* xIm, float* yRe, float* yIm);
|
||||
|
||||
|
||||
#define RELIC_MAX_CHANNELS 2
|
||||
#define RELIC_MAX_SCALES 6
|
||||
#define RELIC_BASE_SCALE 10.0f
|
||||
#define RELIC_FREQUENCY_MASKING_FACTOR 1.0f
|
||||
#define RELIC_CRITICAL_BAND_COUNT 27
|
||||
#define RELIC_PI 3.14159265358979323846f
|
||||
#define RELIC_SIZE_LOW 128
|
||||
#define RELIC_SIZE_MID 256
|
||||
#define RELIC_SIZE_HIGH 512
|
||||
#define RELIC_MAX_SIZE RELIC_SIZE_HIGH
|
||||
#define RELIC_MAX_FREQ (RELIC_MAX_SIZE / 2)
|
||||
#define RELIC_MAX_FFT (RELIC_MAX_SIZE / 4)
|
||||
#define RELIC_MIN_BITRATE 256
|
||||
#define RELIC_MAX_BITRATE 2048
|
||||
//#define RELIC_MAX_FRAME_SIZE ((RELIC_MAX_BITRATE / 8) + 0x04) /* extra 0x04 for the bitreader */
|
||||
|
||||
|
||||
struct relic_handle_t {
|
||||
/* decoder info */
|
||||
int channels;
|
||||
int frame_size;
|
||||
int wave_size;
|
||||
int freq_size;
|
||||
int dct_mode;
|
||||
int samples_mode;
|
||||
/* decoder init state */
|
||||
float scales[RELIC_MAX_SCALES]; /* quantization scales */
|
||||
float dct[RELIC_MAX_SIZE];
|
||||
float window[RELIC_MAX_SIZE];
|
||||
/* decoder frame state */
|
||||
uint8_t exponents[RELIC_MAX_CHANNELS][RELIC_MAX_FREQ]; /* quantization/scale indexes */
|
||||
float freq1[RELIC_MAX_FREQ]; /* dequantized spectrum */
|
||||
float freq2[RELIC_MAX_FREQ];
|
||||
float wave_cur[RELIC_MAX_CHANNELS][RELIC_MAX_SIZE]; /* current frame samples */
|
||||
float wave_prv[RELIC_MAX_CHANNELS][RELIC_MAX_SIZE]; /* previous frame samples */
|
||||
};
|
||||
|
||||
/* ************************************* */
|
||||
|
||||
static const int16_t critical_band_data[RELIC_CRITICAL_BAND_COUNT] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
9, 11, 13, 15, 17, 20, 23, 27,
|
||||
31, 37, 43, 51, 62, 74, 89, 110,
|
||||
139, 180, 256
|
||||
};
|
||||
|
||||
static void init_dct(float* dct, int dct_size) {
|
||||
int i;
|
||||
int dct_quarter = dct_size >> 2;
|
||||
|
||||
for (i = 0; i < dct_quarter; i++) {
|
||||
double temp = ((float)i + 0.125f) * (RELIC_PI * 2.0f) * (1.0f / (float)dct_size);
|
||||
dct[i] = sin(temp);
|
||||
dct[dct_quarter + i] = cos(temp);
|
||||
}
|
||||
}
|
||||
|
||||
static int apply_idct(const float* freq, float* wave, const float* dct, int dct_size) {
|
||||
int i;
|
||||
float factor;
|
||||
float out_re[RELIC_MAX_FFT];
|
||||
float out_im[RELIC_MAX_FFT];
|
||||
float in_re[RELIC_MAX_FFT];
|
||||
float in_im[RELIC_MAX_FFT];
|
||||
float wave_tmp[RELIC_MAX_SIZE];
|
||||
int dct_half = dct_size >> 1;
|
||||
int dct_quarter = dct_size >> 2;
|
||||
int dct_3quarter = 3 * (dct_size >> 2);
|
||||
|
||||
/* prerotation? */
|
||||
for (i = 0; i < dct_quarter; i++) {
|
||||
float coef1 = freq[2 * i] * 0.5f;
|
||||
float coef2 = freq[dct_half - 1 - 2 * i] * 0.5f;
|
||||
in_re[i] = coef1 * dct[dct_quarter + i] + coef2 * dct[i];
|
||||
in_im[i] = -coef1 * dct[i] + coef2 * dct[dct_quarter + i];
|
||||
}
|
||||
|
||||
/* main FFT */
|
||||
fft(dct_quarter, in_re, in_im, out_re, out_im);
|
||||
|
||||
/* postrotation, window and reorder? */
|
||||
factor = 8.0 / sqrt(dct_size);
|
||||
for (i = 0; i < dct_quarter; i++) {
|
||||
float out_re_i = out_re[i];
|
||||
out_re[i] = (out_re[i] * dct[dct_quarter + i] + out_im[i] * dct[i]) * factor;
|
||||
out_im[i] = (-out_re_i * dct[i] + out_im[i] * dct[dct_quarter + i]) * factor;
|
||||
wave_tmp[i * 2] = out_re[i];
|
||||
wave_tmp[i * 2 + dct_half] = out_im[i];
|
||||
}
|
||||
for (i = 1; i < dct_size; i += 2) {
|
||||
wave_tmp[i] = -wave_tmp[dct_size - 1 - i];
|
||||
}
|
||||
|
||||
/* wave mix thing? */
|
||||
for (i = 0; i < dct_3quarter; i++) {
|
||||
wave[i] = wave_tmp[dct_quarter + i];
|
||||
}
|
||||
for (i = dct_3quarter; i < dct_size; i++) {
|
||||
wave[i] = -wave_tmp[i - dct_3quarter];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void decode_frame(const float* freq1, const float* freq2, float* wave_cur, float* wave_prv, const float* dct, const float* window, int dct_size) {
|
||||
int i;
|
||||
float wave_tmp[RELIC_MAX_SIZE];
|
||||
int dct_half = dct_size >> 1;
|
||||
|
||||
/* copy for first half(?) */
|
||||
memcpy(wave_cur, wave_prv, RELIC_MAX_SIZE * sizeof(float));
|
||||
|
||||
/* transform frequency domain to time domain with DCT/FFT */
|
||||
apply_idct(freq1, wave_tmp, dct, dct_size);
|
||||
apply_idct(freq2, wave_prv, dct, dct_size);
|
||||
|
||||
/* overlap and apply window function to filter this block's beginning */
|
||||
for (i = 0; i < dct_half; i++) {
|
||||
wave_cur[dct_half + i] = wave_tmp[i] * window[i] + wave_cur[dct_half + i] * window[dct_half + i];
|
||||
wave_prv[i] = wave_prv[i] * window[i] + wave_tmp[dct_half + i] * window[dct_half + i];
|
||||
}
|
||||
}
|
||||
|
||||
static void init_window(float *window, int dct_size) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dct_size; i++) {
|
||||
window[i] = sin((float)i * (RELIC_PI / dct_size));
|
||||
}
|
||||
}
|
||||
|
||||
static void decode_frame_base(const float* freq1, const float* freq2, float* wave_cur, float* wave_prv, const float* dct, const float* window, int dct_mode, int samples_mode) {
|
||||
int i;
|
||||
float wave_tmp[RELIC_MAX_SIZE];
|
||||
|
||||
/* dec_relic only uses 512/512 mode, source references 256/256 (effects only?) too */
|
||||
|
||||
if (samples_mode == RELIC_SIZE_LOW) {
|
||||
{
|
||||
/* 128 DCT to 128 samples */
|
||||
decode_frame(freq1, freq2, wave_cur, wave_prv, dct, window, RELIC_SIZE_LOW);
|
||||
}
|
||||
}
|
||||
else if (samples_mode == RELIC_SIZE_MID) {
|
||||
if (dct_mode == RELIC_SIZE_LOW) {
|
||||
/* 128 DCT to 256 samples (repeat sample x2) */
|
||||
decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_LOW);
|
||||
for (i = 0; i < 256 - 1; i += 2) {
|
||||
wave_cur[i + 0] = wave_tmp[i >> 1];
|
||||
wave_cur[i + 1] = wave_tmp[i >> 1];
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* 256 DCT to 256 samples */
|
||||
decode_frame(freq1, freq2, wave_cur, wave_prv, dct, window, RELIC_SIZE_MID);
|
||||
}
|
||||
}
|
||||
else if (samples_mode == RELIC_SIZE_HIGH) {
|
||||
if (dct_mode == RELIC_SIZE_LOW) {
|
||||
/* 128 DCT to 512 samples (repeat sample x4) */
|
||||
decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_LOW);
|
||||
for (i = 0; i < 512 - 1; i += 4) {
|
||||
wave_cur[i + 0] = wave_tmp[i >> 2];
|
||||
wave_cur[i + 1] = wave_tmp[i >> 2];
|
||||
wave_cur[i + 2] = wave_tmp[i >> 2];
|
||||
wave_cur[i + 3] = wave_tmp[i >> 2];
|
||||
}
|
||||
}
|
||||
else if (dct_mode == RELIC_SIZE_MID) {
|
||||
/* 256 DCT to 512 samples (repeat sample x2) */
|
||||
decode_frame(freq1, freq2, wave_tmp, wave_prv, dct, window, RELIC_SIZE_MID);
|
||||
for (i = 0; i < 512 - 1; i += 2) {
|
||||
wave_cur[i + 0] = wave_tmp[i >> 1];
|
||||
wave_cur[i + 1] = wave_tmp[i >> 1];
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* 512 DCT to 512 samples */
|
||||
decode_frame(freq1, freq2, wave_cur, wave_prv, dct, window, RELIC_SIZE_HIGH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* reads 32b max, packed in LSB order per byte (like Vorbis), ex.
|
||||
* with 0x45 6A=01000101 01101010 could read 4b=0101, 6b=100100, 3b=010 ...
|
||||
* assumes buf has enough extra bits to read 32b (size +0x04) */
|
||||
static uint32_t read_ubits(uint8_t bits, uint32_t offset, uint8_t* buf) {
|
||||
uint32_t shift, mask, pos, val;
|
||||
|
||||
shift = offset - 8 * (offset / 8);
|
||||
mask = (1 << bits) - 1;
|
||||
pos = offset / 8;
|
||||
val = (buf[pos+0]) | (buf[pos+1]<<8) | (buf[pos+2]<<16) | (buf[pos+3]<<24);
|
||||
return (val >> shift) & mask;
|
||||
}
|
||||
|
||||
static int read_sbits(uint8_t bits, uint32_t offset, uint8_t* buf) {
|
||||
uint32_t val = read_ubits(bits, offset, buf);
|
||||
int outval;
|
||||
if (val >> (bits - 1) == 1) { /* upper bit = sign */
|
||||
uint32_t mask = (1 << (bits - 1)) - 1;
|
||||
outval = (int)(val & mask);
|
||||
outval = -outval;
|
||||
}
|
||||
else {
|
||||
outval = (int)val;
|
||||
}
|
||||
return outval;
|
||||
}
|
||||
|
||||
static void init_dequantization(float* scales) {
|
||||
int i;
|
||||
|
||||
scales[0] = RELIC_BASE_SCALE;
|
||||
for (i = 1; i < RELIC_MAX_SCALES; i++) {
|
||||
scales[i] = scales[i - 1] * scales[0];
|
||||
}
|
||||
for (i = 0; i < RELIC_MAX_SCALES; i++) {
|
||||
scales[i] = RELIC_FREQUENCY_MASKING_FACTOR / (double) ((1 << (i + 1)) - 1) * scales[i];
|
||||
}
|
||||
}
|
||||
|
||||
static int unpack_frame(uint8_t* buf, int buf_size, float* freq1, float* freq2, const float* scales, uint8_t* exponents, int freq_size) {
|
||||
uint8_t flags, cb_bits, ev_bits, ei_bits, qv_bits;
|
||||
int qv;
|
||||
uint8_t ev;
|
||||
uint8_t move, pos;
|
||||
uint32_t bit_offset, max_offset;
|
||||
int i, j;
|
||||
int freq_half = freq_size >> 1;
|
||||
|
||||
|
||||
memset(freq1, 0, RELIC_MAX_FREQ * sizeof(float));
|
||||
memset(freq2, 0, RELIC_MAX_FREQ * sizeof(float));
|
||||
|
||||
flags = read_ubits(2u, 0u, buf);
|
||||
cb_bits = read_ubits(3u, 2u, buf);
|
||||
ev_bits = read_ubits(2u, 5u, buf);
|
||||
ei_bits = read_ubits(4u, 7u, buf);
|
||||
bit_offset = 11;
|
||||
max_offset = buf_size * 8u;
|
||||
|
||||
/* reset exponents indexes */
|
||||
if ((flags & 1) == 1) {
|
||||
memset(exponents, 0, RELIC_MAX_FREQ);
|
||||
}
|
||||
|
||||
/* read packed exponents indexes for all bands */
|
||||
if (cb_bits > 0 && ev_bits > 0) {
|
||||
pos = 0;
|
||||
for (i = 0; i < RELIC_CRITICAL_BAND_COUNT - 1; i++) {
|
||||
if (bit_offset + cb_bits > max_offset)
|
||||
goto fail;
|
||||
move = read_ubits(cb_bits, bit_offset, buf);
|
||||
bit_offset += cb_bits;
|
||||
|
||||
if (i > 0 && move == 0)
|
||||
break;
|
||||
pos += move;
|
||||
|
||||
if (bit_offset + ev_bits > max_offset)
|
||||
goto fail;
|
||||
ev = read_ubits(ev_bits, bit_offset, buf);
|
||||
bit_offset += ev_bits;
|
||||
|
||||
if (pos + 1 >= sizeof(critical_band_data))
|
||||
goto fail;
|
||||
for (j = critical_band_data[pos]; j < critical_band_data[pos + 1]; j++) {
|
||||
exponents[j] = ev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* read quantized values */
|
||||
if (freq_half > 0 && ei_bits > 0) {
|
||||
|
||||
/* read first part */
|
||||
pos = 0;
|
||||
for (i = 0; i < RELIC_MAX_FREQ; i++) {
|
||||
if (bit_offset + ei_bits > max_offset)
|
||||
goto fail;
|
||||
move = read_ubits(ei_bits, bit_offset, buf);
|
||||
bit_offset += ei_bits;
|
||||
|
||||
if (i > 0 && move == 0)
|
||||
break;
|
||||
pos += move;
|
||||
|
||||
if (pos >= RELIC_MAX_FREQ)
|
||||
goto fail;
|
||||
qv_bits = exponents[pos];
|
||||
|
||||
if (bit_offset + qv_bits + 2u > max_offset)
|
||||
goto fail;
|
||||
qv = read_sbits(qv_bits + 2u, bit_offset, buf);
|
||||
bit_offset += qv_bits + 2u;
|
||||
|
||||
if (qv != 0 && pos < freq_half && qv_bits < 6) {
|
||||
freq1[pos] = (float)qv * scales[qv_bits];
|
||||
}
|
||||
}
|
||||
|
||||
/* read second part, or clone it */
|
||||
if ((flags & 2) == 2) {
|
||||
memcpy(freq2, freq1, RELIC_MAX_FREQ * sizeof(float));
|
||||
}
|
||||
else {
|
||||
pos = 0;
|
||||
for (i = 0; i < RELIC_MAX_FREQ; i++) {
|
||||
if (bit_offset + ei_bits > max_offset)
|
||||
goto fail;
|
||||
move = read_ubits(ei_bits, bit_offset, buf);
|
||||
bit_offset += ei_bits;
|
||||
|
||||
if (i > 0 && move == 0)
|
||||
break;
|
||||
pos += move;
|
||||
|
||||
if (pos >= RELIC_MAX_FREQ)
|
||||
goto fail;
|
||||
qv_bits = exponents[pos];
|
||||
|
||||
if (bit_offset + qv_bits + 2u > max_offset)
|
||||
goto fail;
|
||||
qv = read_sbits(qv_bits + 2u, bit_offset, buf);
|
||||
bit_offset += qv_bits + 2u;
|
||||
|
||||
if (qv != 0 && pos < freq_half && qv_bits < 6) {
|
||||
freq2[pos] = (float)qv * scales[qv_bits];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0; /* original code doesn't check bad sizes so no return errcode */
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
relic_handle_t* relic_init(int channels, int bitrate, int codec_rate) {
|
||||
relic_handle_t* handle = NULL;
|
||||
|
||||
if (channels < 0 || channels > RELIC_MAX_CHANNELS)
|
||||
goto fail;
|
||||
|
||||
handle = calloc(1, sizeof(relic_handle_t));
|
||||
if (!handle) goto fail;
|
||||
|
||||
handle->channels = channels;
|
||||
|
||||
/* dequantized freq1+2 size (separate from DCT) */
|
||||
if (codec_rate < 22050) /* probably 11025 only */
|
||||
handle->freq_size = RELIC_SIZE_LOW;
|
||||
else if (codec_rate == 22050)
|
||||
handle->freq_size = RELIC_SIZE_MID;
|
||||
else if (codec_rate > 22050) /* probably 44100 only */
|
||||
handle->freq_size = RELIC_SIZE_HIGH;
|
||||
|
||||
/* default for streams (only a few mode combos are valid, see decode) */
|
||||
handle->wave_size = RELIC_SIZE_HIGH;
|
||||
handle->dct_mode = RELIC_SIZE_HIGH;
|
||||
handle->samples_mode = RELIC_SIZE_HIGH;
|
||||
|
||||
init_dct(handle->dct, RELIC_SIZE_HIGH);
|
||||
init_window(handle->window, RELIC_SIZE_HIGH);
|
||||
init_dequantization(handle->scales);
|
||||
memset(handle->wave_prv, 0, RELIC_MAX_CHANNELS * RELIC_MAX_SIZE * sizeof(float));
|
||||
|
||||
/* known bitrates: 0x100, 0x180, 0x200, 0x280, 0x300, 0x380, 0x400, 0x800
|
||||
* dec.exe doesn't validate this, so there may be more */
|
||||
if (bitrate < RELIC_MIN_BITRATE || bitrate > RELIC_MAX_BITRATE)
|
||||
goto fail;
|
||||
handle->frame_size = (bitrate / 8); /* 0x100 and 0x80 are common */
|
||||
|
||||
|
||||
return handle;
|
||||
fail:
|
||||
relic_free(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void relic_free(relic_handle_t* handle) {
|
||||
if (!handle) return;
|
||||
free(handle);
|
||||
}
|
||||
|
||||
void relic_reset(relic_handle_t* handle) {
|
||||
if (!handle) return;
|
||||
memset(handle->wave_prv, 0, RELIC_MAX_CHANNELS * RELIC_MAX_SIZE * sizeof(float));
|
||||
}
|
||||
|
||||
int relic_get_frame_size(relic_handle_t* handle) {
|
||||
if (!handle) return 0;
|
||||
return handle->frame_size;
|
||||
}
|
||||
|
||||
int relic_decode_frame(relic_handle_t* handle, uint8_t* buf, int channel) {
|
||||
int ok;
|
||||
|
||||
/* clean extra bytes for bitreader (due to a quirk in the original code it may read outside max frame size) */
|
||||
memset(buf + handle->frame_size, 0, RELIC_BUFFER_SIZE - handle->frame_size);
|
||||
|
||||
ok = unpack_frame(buf, RELIC_BUFFER_SIZE, handle->freq1, handle->freq2, handle->scales, handle->exponents[channel], handle->freq_size);
|
||||
if (!ok) return ok;
|
||||
|
||||
decode_frame_base(handle->freq1, handle->freq2, handle->wave_cur[channel], handle->wave_prv[channel], handle->dct, handle->window, handle->dct_mode, handle->samples_mode);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int clamp16(int32_t val) {
|
||||
if (val > 32767) return 32767;
|
||||
else if (val < -32768) return -32768;
|
||||
else return val;
|
||||
}
|
||||
|
||||
void relic_get_pcm16(relic_handle_t* handle, int16_t* outbuf, int32_t samples, int32_t skip) {
|
||||
int s, ch;
|
||||
int ichs = handle->channels;
|
||||
|
||||
for (ch = 0; ch < ichs; ch++) {
|
||||
for (s = 0; s < samples; s++) {
|
||||
double d64_sample = handle->wave_cur[ch][skip + s];
|
||||
int pcm_sample = clamp16((int32_t)d64_sample);
|
||||
|
||||
/* f32 in PCM 32767.0 .. -32768.0 format, original code
|
||||
* does some custom double-to-int rint() though */
|
||||
//FQ_BNUM ((float)(1<<26)*(1<<26)*1.5)
|
||||
//rint(x) ((d64 = (double)(x)+FQ_BNUM), *(int*)(&d64))
|
||||
|
||||
outbuf[s*ichs + ch] = pcm_sample;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef _RELIC_DECODER_LIB_H_
|
||||
#define _RELIC_DECODER_LIB_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define RELIC_BUFFER_SIZE 0x104
|
||||
#define RELIC_SAMPLES_PER_FRAME 512
|
||||
|
||||
typedef struct relic_handle_t relic_handle_t;
|
||||
|
||||
relic_handle_t* relic_init(int channels, int bitrate, int codec_rate);
|
||||
|
||||
void relic_free(relic_handle_t* handle);
|
||||
|
||||
void relic_reset(relic_handle_t* handle);
|
||||
|
||||
int relic_get_frame_size(relic_handle_t* handle);
|
||||
|
||||
int relic_decode_frame(relic_handle_t* handle, uint8_t* buf, int channel);
|
||||
|
||||
void relic_get_pcm16(relic_handle_t* handle, int16_t* outbuf, int32_t samples, int32_t skip);
|
||||
|
||||
#endif/*_RELIC_DECODER_LIB_H_ */
|
|
@ -395,7 +395,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) {
|
|||
case coding_IMA:
|
||||
case coding_DVI_IMA:
|
||||
case coding_SNDS_IMA:
|
||||
case coding_OTNS_IMA:
|
||||
case coding_QD_IMA:
|
||||
case coding_UBI_IMA:
|
||||
case coding_UBI_SCE_IMA:
|
||||
case coding_OKI16:
|
||||
|
@ -631,7 +631,7 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) {
|
|||
case coding_RAD_IMA_mono:
|
||||
return 0x14;
|
||||
case coding_SNDS_IMA:
|
||||
case coding_OTNS_IMA:
|
||||
case coding_QD_IMA:
|
||||
return 0; //todo: 0x01?
|
||||
case coding_UBI_IMA: /* variable (PCM then IMA) */
|
||||
return 0;
|
||||
|
@ -1173,7 +1173,7 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
|
|||
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch);
|
||||
}
|
||||
break;
|
||||
case coding_OTNS_IMA:
|
||||
case coding_QD_IMA:
|
||||
for (ch = 0; ch < vgmstream->channels; ch++) {
|
||||
decode_otns_ima(vgmstream, &vgmstream->ch[ch], buffer+ch,
|
||||
vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch);
|
||||
|
|
|
@ -594,6 +594,7 @@ static const char* extension_list[] = {
|
|||
"wve",
|
||||
"wvs",
|
||||
"wvx",
|
||||
"wxd",
|
||||
|
||||
"x",
|
||||
"x360audio", //fake extension for Unreal Engine 3 XMA (real extension unknown)
|
||||
|
@ -746,7 +747,7 @@ static const coding_info coding_info_list[] = {
|
|||
{coding_DVI_IMA_int, "Intel DVI 4-bit IMA ADPCM (mono/interleave)"},
|
||||
{coding_3DS_IMA, "3DS IMA 4-bit ADPCM"},
|
||||
{coding_SNDS_IMA, "Heavy Iron .snds 4-bit IMA ADPCM"},
|
||||
{coding_OTNS_IMA, "Omikron: The Nomad Soul 4-bit IMA ADPCM"},
|
||||
{coding_QD_IMA, "Quantic Dream 4-bit IMA ADPCM"},
|
||||
{coding_WV6_IMA, "Gorilla Systems WV6 4-bit IMA ADPCM"},
|
||||
{coding_ALP_IMA, "High Voltage ALP 4-bit IMA ADPCM"},
|
||||
{coding_FFTA2_IMA, "Final Fantasy Tactics A2 4-bit IMA ADPCM"},
|
||||
|
@ -1068,7 +1069,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_SMP, "Infernal Engine .smp header"},
|
||||
{meta_MUL, "Crystal Dynamics .MUL header"},
|
||||
{meta_THP, "Nintendo THP header"},
|
||||
{meta_STS_WII, "Shikigami no Shiro (WII) Header"},
|
||||
{meta_STS, "Alfa System .STS header"},
|
||||
{meta_PS2_P2BT, "Pop'n'Music 7 Header"},
|
||||
{meta_PS2_GBTS, "Pop'n'Music 9 Header"},
|
||||
{meta_NGC_DSP_IADP, "IADP Header"},
|
||||
|
@ -1166,7 +1167,7 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_HYPERSCAN_KVAG, "Mattel Hyperscan KVAG"},
|
||||
{meta_IOS_PSND, "PSND Header"},
|
||||
{meta_BOS_ADP, "ADP! header"},
|
||||
{meta_OTNS_ADP, "Omikron: The Nomad Soul ADP header"},
|
||||
{meta_QD_ADP, "Quantic Dream .ADP header"},
|
||||
{meta_EB_SFX, "Excitebots .sfx header"},
|
||||
{meta_EB_SF0, "assumed Excitebots .sf0 by extension"},
|
||||
{meta_MTAF, "Konami MTAF header"},
|
||||
|
@ -1353,6 +1354,8 @@ static const meta_info meta_info_list[] = {
|
|||
{meta_DSP_KWA, "Kuju London .KWA header"},
|
||||
{meta_OGV_3RDEYE, "3rdEye .OGV header"},
|
||||
{meta_PIFF_TPCM, "Tantalus PIFF TPCM header"},
|
||||
{meta_WXD_WXH, "Relic WXD+WXH header"},
|
||||
{meta_BNK_RELIC, "Relic BNK header"},
|
||||
};
|
||||
|
||||
void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) {
|
||||
|
|
|
@ -42,6 +42,11 @@ void block_update_ea_sns(off_t block_offset, VGMSTREAM* vgmstream) {
|
|||
return;
|
||||
|
||||
switch (vgmstream->coding_type) {
|
||||
case coding_PCM16_int:
|
||||
channel_start = 0x00;
|
||||
channel_interleave = 0x02;
|
||||
break;
|
||||
|
||||
case coding_NGC_DSP:
|
||||
/* 0x04: unknown (0x00/02), 0x08: some size?, 0x34: null? */
|
||||
channel_start = read_32bitBE(block_offset + 0x08 + 0x00, sf);
|
||||
|
|
|
@ -43,8 +43,7 @@ VGMSTREAM * init_vgmstream_akb(STREAMFILE *sf) {
|
|||
|
||||
|
||||
/* checks */
|
||||
/* .akb.bytes is the usual extension in later games */
|
||||
if ( !check_extensions(sf, "akb,bytes") )
|
||||
if ( !check_extensions(sf, "akb") )
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00,sf) != 0x414B4220) /* "AKB " */
|
||||
goto fail;
|
||||
|
@ -188,8 +187,7 @@ VGMSTREAM * init_vgmstream_akb2(STREAMFILE *sf) {
|
|||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
|
||||
/* check extensions */
|
||||
/* .akb.bytes is the usual extension in later games */
|
||||
if ( !check_extensions(sf, "akb,bytes") )
|
||||
if ( !check_extensions(sf, "akb") )
|
||||
goto fail;
|
||||
|
||||
/* checks */
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* BNK - sfx container from Relic's earlier games [Homeworld (PC), Homeworld Cataclysm (PC)] */
|
||||
VGMSTREAM* init_vgmstream_bnk_relic(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
uint32_t start_offset, offset, data_size, loop_start, loop_end;
|
||||
int loop_flag, channels, bitrate, internal_rate, sample_rate;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "bnk"))
|
||||
goto fail;
|
||||
if (!is_id32be(0x00,sf, "BNK0"))
|
||||
goto fail;
|
||||
/* 0x04: flags? */
|
||||
|
||||
total_subsongs = read_u32le(0x08,sf);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
|
||||
offset = 0x0c + (target_subsong-1) * 0x38;
|
||||
if (!is_id32be(offset + 0x00,sf, "PCH0"))
|
||||
goto fail;
|
||||
/* 0x04: null */
|
||||
/* 0x08: 0/+-number? */
|
||||
start_offset = read_u32le(offset + 0x0c,sf);
|
||||
data_size = read_u32le(offset + 0x10,sf);
|
||||
loop_start = read_u32le(offset + 0x14,sf); /* 0x14: 0/offset? */
|
||||
loop_end = read_u32le(offset + 0x18,sf); /* 0x18: 0/offset? */
|
||||
bitrate = read_u16le(offset + 0x1c,sf);
|
||||
loop_flag = read_u16le(offset + 0x1e,sf);
|
||||
/* 0x20: volume? */
|
||||
/* 0x22: -1 */
|
||||
/* 0x24: fmt pcm codec 1 */
|
||||
channels = read_u16le(offset + 0x26,sf);
|
||||
sample_rate = read_u32le(offset + 0x28,sf);
|
||||
/* 0x2c: bitrate */
|
||||
/* 0x30: pcm block size */
|
||||
/* 0x32: pcm bps */
|
||||
/* 0x34: 0 */
|
||||
/* 0x36: -1 */
|
||||
|
||||
internal_rate = 44100;
|
||||
|
||||
loop_flag = 0; //todo clicks on loop, wrong calcs?
|
||||
|
||||
/* stream info */
|
||||
if (!is_id32be(start_offset - 0x04,sf, "DATA"))
|
||||
goto fail;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_BNK_RELIC;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
vgmstream->num_samples = relic_bytes_to_samples(data_size, channels, bitrate);
|
||||
vgmstream->loop_start_sample = relic_bytes_to_samples(loop_start, channels, bitrate);
|
||||
vgmstream->loop_end_sample = relic_bytes_to_samples(loop_end, channels, bitrate);
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = data_size;
|
||||
|
||||
vgmstream->codec_data = init_relic(channels, bitrate, internal_rate);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_RELIC;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -1,354 +1,354 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../layout/layout.h"
|
||||
|
||||
#define EA_CODEC_PCM 0x00
|
||||
#define EA_CODEC_ULAW 0x01
|
||||
#define EA_CODEC_IMA 0x02
|
||||
#define EA_CODEC_PSX 0xFF //fake value
|
||||
|
||||
typedef struct {
|
||||
int32_t sample_rate;
|
||||
uint8_t bits;
|
||||
uint8_t channels;
|
||||
uint8_t codec;
|
||||
uint8_t type;
|
||||
int32_t num_samples;
|
||||
int32_t loop_start;
|
||||
int32_t loop_end;
|
||||
int32_t loop_start_offset;
|
||||
int32_t data_offset;
|
||||
|
||||
int big_endian;
|
||||
int loop_flag;
|
||||
int is_sead;
|
||||
int codec_config;
|
||||
int is_bank;
|
||||
int total_subsongs;
|
||||
} eacs_header;
|
||||
|
||||
static int parse_header(STREAMFILE* streamFile, eacs_header* ea, off_t begin_offset);
|
||||
static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, eacs_header* ea);
|
||||
|
||||
static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE *streamFile, eacs_header *ea, int find_loop);
|
||||
static int get_ea_1snh_ima_version(STREAMFILE* streamFile, off_t start_offset, const eacs_header* ea);
|
||||
|
||||
/* EA 1SNh - from early EA games, stream (~1996, ex. Need for Speed) */
|
||||
VGMSTREAM * init_vgmstream_ea_1snh(STREAMFILE *streamFile) {
|
||||
eacs_header ea = { 0 };
|
||||
off_t offset, eacs_offset;
|
||||
VGMSTREAM *vgmstream = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .asf/as4: common,
|
||||
* .lasf: fake for plugins
|
||||
* .cnk: some PS1 games
|
||||
* .sng: fake for plugins (to mimic EA SCHl's common extension)
|
||||
* .uv/tgq: some SAT games (video only?)
|
||||
* .tgv: videos
|
||||
* (extensionless): Need for Speed (SAT) (videos) */
|
||||
if (!check_extensions(streamFile, "asf,lasf,as4,cnk,sng,uv,tgq,tgv,"))
|
||||
goto fail;
|
||||
|
||||
offset = 0x00;
|
||||
|
||||
/* in TGV videos, either TGVk or 1SNh block comes first */
|
||||
if (read_32bitBE(0x00, streamFile) == 0x5447566B) { /* "TGVk" */
|
||||
offset = read_32bitBE(0x04, streamFile);
|
||||
} else if (read_32bitLE(0x00, streamFile) == 0x5447566B) { /* "kVGT" */
|
||||
offset = read_32bitLE(0x04, streamFile);
|
||||
}
|
||||
|
||||
if (read_32bitBE(offset + 0x00, streamFile) != 0x31534E68 && /* "1SNh" */
|
||||
read_32bitBE(offset + 0x00, streamFile) != 0x53454144) /* "SEAD" */
|
||||
goto fail;
|
||||
|
||||
/* stream is divided into blocks/chunks: 1SNh=audio header, 1SNd=data xN, 1SNl=loop end, 1SNe=end.
|
||||
* Video uses various blocks (TGVk/TGVf/MUVf/etc) and sometimes alt audio blocks (SEAD/SNDC/SEND). */
|
||||
ea.is_sead = read_32bitBE(offset + 0x00, streamFile) == 0x53454144;
|
||||
|
||||
eacs_offset = offset + 0x08; /* after 1SNh block id+size */
|
||||
|
||||
if (!parse_header(streamFile, &ea, eacs_offset))
|
||||
goto fail;
|
||||
vgmstream = init_vgmstream_main(streamFile, &ea);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
if (ea.num_samples == 0) {
|
||||
/* header does not specify number of samples, need to calculate it manually */
|
||||
/* HACK: we need vgmstream object to use blocked layout so we're doing this calc after creating it */
|
||||
set_ea_1snh_num_samples(vgmstream, streamFile, &ea, 0);
|
||||
|
||||
/* update samples and loop state */
|
||||
vgmstream->num_samples = ea.num_samples;
|
||||
vgmstream_force_loop(vgmstream, ea.loop_flag, ea.loop_start, ea.loop_end);
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* EA EACS - from early EA games, bank (~1996, ex. Need for Speed) */
|
||||
VGMSTREAM * init_vgmstream_ea_eacs(STREAMFILE *streamFile) {
|
||||
eacs_header ea = {0};
|
||||
off_t eacs_offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .eas: single bank [Need for Speed (PC)]
|
||||
* .bnk: multi bank [Need for Speed (PC)] */
|
||||
if (!check_extensions(streamFile,"eas,bnk"))
|
||||
goto fail;
|
||||
|
||||
/* plain data without blocks, can contain N*(EACS header) + N*(data), or N (EACS header + data) */
|
||||
ea.is_bank = 1;
|
||||
|
||||
/* use ??? as endian marker (Saturn = BE) */
|
||||
//ea.big_endian = guess_endianness32bit(0x04,streamFile);
|
||||
|
||||
if (read_32bitBE(0x00,streamFile) == 0x45414353) { /* "EACS" */
|
||||
/* single bank variant */
|
||||
eacs_offset = 0x00;
|
||||
}
|
||||
else if (read_32bitBE(0x00,streamFile) == 0x00) {
|
||||
/* multi bank variant */
|
||||
int i;
|
||||
int target_subsong = streamFile->stream_index;
|
||||
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0) goto fail;
|
||||
|
||||
/* offsets to EACSs are scattered in the first 0x200
|
||||
* this looks dumb but seems like the only way */
|
||||
eacs_offset = 0;
|
||||
for (i = 0x00; i < 0x200; i += 0x04) {
|
||||
off_t bank_offset = read_32bitLE(i, streamFile);
|
||||
if (bank_offset == 0)
|
||||
continue;
|
||||
|
||||
ea.total_subsongs++;
|
||||
|
||||
/* parse mini bank header */
|
||||
if (ea.total_subsongs == target_subsong) {
|
||||
/* 0x00: some id or flags? */
|
||||
eacs_offset = read_32bitLE(bank_offset + 0x04, streamFile);
|
||||
if (read_32bitBE(eacs_offset, streamFile) != 0x45414353)
|
||||
goto fail;
|
||||
/* rest: not sure if part of this header */
|
||||
}
|
||||
}
|
||||
|
||||
if (eacs_offset == 0)
|
||||
goto fail;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!parse_header(streamFile,&ea, eacs_offset))
|
||||
goto fail;
|
||||
return init_vgmstream_main(streamFile, &ea);
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static VGMSTREAM * init_vgmstream_main(STREAMFILE *streamFile, eacs_header* ea) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(ea->channels, ea->loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = ea->sample_rate;
|
||||
vgmstream->num_samples = ea->num_samples;
|
||||
vgmstream->loop_start_sample = ea->loop_start;
|
||||
vgmstream->loop_end_sample = ea->loop_end;
|
||||
|
||||
vgmstream->codec_endian = ea->big_endian;
|
||||
vgmstream->layout_type = ea->is_bank ? layout_none : layout_blocked_ea_1snh;
|
||||
vgmstream->meta_type = ea->is_bank ? meta_EA_EACS : meta_EA_1SNH;
|
||||
vgmstream->num_streams = ea->total_subsongs;
|
||||
|
||||
switch (ea->codec) {
|
||||
case EA_CODEC_PCM: /* Need for Speed (PC) */
|
||||
vgmstream->coding_type = ea->bits==1 ? coding_PCM8_int : coding_PCM16_int;
|
||||
break;
|
||||
|
||||
case EA_CODEC_ULAW: /* Crusader: No Remorse movies (SAT), FIFA 96 movies (SAT) */
|
||||
if (ea->bits && ea->bits != 2) goto fail; /* only set in EACS */
|
||||
vgmstream->coding_type = coding_ULAW_int;
|
||||
break;
|
||||
|
||||
case EA_CODEC_IMA: /* Need for Speed (PC) */
|
||||
if (ea->bits && ea->bits != 2) goto fail; /* only in EACS */
|
||||
vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */
|
||||
vgmstream->codec_config = ea->codec_config;
|
||||
break;
|
||||
|
||||
case EA_CODEC_PSX: /* Need for Speed (PS1) */
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("EA EACS: unknown codec 0x%02x\n", ea->codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream,streamFile,ea->data_offset))
|
||||
goto fail;
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int parse_header(STREAMFILE* streamFile, eacs_header* ea, off_t offset) {
|
||||
/* audio header endianness doesn't always match block headers, use sample rate to detect */
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*);
|
||||
|
||||
if (read_32bitBE(offset+0x00, streamFile) == 0x45414353) { /* "EACS" */
|
||||
/* EACS subheader (PC, SAT) */
|
||||
ea->big_endian = guess_endianness32bit(offset + 0x04, streamFile);
|
||||
read_32bit = ea->big_endian ? read_32bitBE : read_32bitLE;
|
||||
|
||||
ea->sample_rate = read_32bit(offset+0x04, streamFile);
|
||||
ea->bits = read_8bit(offset+0x08, streamFile);
|
||||
ea->channels = read_8bit(offset+0x09, streamFile);
|
||||
ea->codec = read_8bit(offset+0x0a, streamFile);
|
||||
ea->type = read_8bit(offset+0x0b, streamFile); /* block type? 0=1SNh, -1=bank */
|
||||
ea->num_samples = read_32bit(offset+0x0c, streamFile);
|
||||
ea->loop_start = read_32bit(offset+0x10, streamFile);
|
||||
ea->loop_end = read_32bit(offset+0x14, streamFile) + ea->loop_start; /* loop length */
|
||||
ea->data_offset = read_32bit(offset+0x18, streamFile); /* 0 when blocked */
|
||||
/* 0x1c: pan/volume/etc? (0x7F)
|
||||
* rest may be padding/garbage */
|
||||
//VGM_ASSERT(ea->type != 0, "EA EACS: unknown type %i\n", ea->type);
|
||||
|
||||
if (ea->codec == EA_CODEC_IMA)
|
||||
ea->codec_config = get_ea_1snh_ima_version(streamFile, 0x00, ea);
|
||||
/* EACS banks with empty values exist but will be rejected later */
|
||||
}
|
||||
else if (read_32bitBE(offset + 0x00, streamFile) == 0x00) {
|
||||
/* found in early videos, similar to EACS */
|
||||
ea->big_endian = guess_endianness32bit(offset + 0x04, streamFile);
|
||||
read_32bit = ea->big_endian ? read_32bitBE : read_32bitLE;
|
||||
|
||||
ea->sample_rate = read_32bit(offset + 0x04, streamFile);
|
||||
ea->bits = read_8bit(offset + 0x08, streamFile);
|
||||
ea->channels = read_8bit(offset + 0x09, streamFile);
|
||||
ea->codec = read_8bit(offset + 0x0a, streamFile);
|
||||
ea->type = read_8bit(offset + 0x0b, streamFile); /* block type? 0=1SNh, -1=bank */
|
||||
|
||||
if (ea->codec == EA_CODEC_IMA)
|
||||
ea->codec_config = get_ea_1snh_ima_version(streamFile, 0x00, ea);
|
||||
}
|
||||
else if (ea->is_sead) {
|
||||
/* alt subheader (found in some PC videos) */
|
||||
ea->big_endian = guess_endianness32bit(offset + 0x00, streamFile);
|
||||
read_32bit = ea->big_endian ? read_32bitBE : read_32bitLE;
|
||||
|
||||
ea->sample_rate = read_32bit(offset+0x00, streamFile);
|
||||
ea->channels = read_32bit(offset+0x04, streamFile);
|
||||
ea->codec = read_32bit(offset+0x08, streamFile);
|
||||
|
||||
if (ea->codec == EA_CODEC_IMA)
|
||||
ea->codec_config = get_ea_1snh_ima_version(streamFile, 0x00, ea);
|
||||
}
|
||||
else {
|
||||
/* alt subheader (PS1) */
|
||||
ea->big_endian = guess_endianness32bit(offset + 0x00, streamFile);
|
||||
read_32bit = ea->big_endian ? read_32bitBE : read_32bitLE;
|
||||
|
||||
ea->sample_rate = read_32bit(offset+0x00, streamFile);
|
||||
ea->channels = read_8bit(offset+0x18, streamFile);
|
||||
ea->codec = EA_CODEC_PSX;
|
||||
}
|
||||
|
||||
ea->loop_flag = (ea->loop_end > 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* get total samples by parsing block headers, needed when EACS isn't present */
|
||||
static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE *streamFile, eacs_header *ea, int find_loop) {
|
||||
int32_t num_samples = 0, block_id;
|
||||
size_t file_size;
|
||||
int32_t(*read_32bit)(off_t, STREAMFILE *) = ea->big_endian ? read_32bitBE : read_32bitLE;
|
||||
int loop_end_found = 0;
|
||||
|
||||
file_size = get_streamfile_size(streamFile);
|
||||
vgmstream->next_block_offset = ea->data_offset;
|
||||
|
||||
while (vgmstream->next_block_offset < file_size) {
|
||||
block_update_ea_1snh(vgmstream->next_block_offset, vgmstream);
|
||||
if (vgmstream->current_block_samples < 0)
|
||||
break;
|
||||
|
||||
block_id = read_32bitBE(vgmstream->current_block_offset, streamFile);
|
||||
if (find_loop) {
|
||||
if (vgmstream->current_block_offset == ea->loop_start_offset) {
|
||||
ea->loop_start = num_samples;
|
||||
ea->loop_flag = 1;
|
||||
block_update_ea_1snh(ea->data_offset, vgmstream);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (block_id == 0x31534E6C) { /* "1SNl" loop point found */
|
||||
ea->loop_start_offset = read_32bit(vgmstream->current_block_offset + 0x08, streamFile);
|
||||
ea->loop_end = num_samples;
|
||||
loop_end_found = 1;
|
||||
}
|
||||
}
|
||||
|
||||
num_samples += vgmstream->current_block_samples;
|
||||
}
|
||||
|
||||
ea->num_samples = num_samples;
|
||||
|
||||
/* reset once we're done */
|
||||
block_update_ea_1snh(ea->data_offset, vgmstream);
|
||||
|
||||
if (loop_end_found) {
|
||||
/* recurse to find loop start sample */
|
||||
set_ea_1snh_num_samples(vgmstream, streamFile, ea, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* find codec version used, with or without ADPCM hist per block */
|
||||
static int get_ea_1snh_ima_version(STREAMFILE* streamFile, off_t start_offset, const eacs_header* ea) {
|
||||
off_t block_offset = start_offset;
|
||||
size_t file_size = get_streamfile_size(streamFile);
|
||||
int32_t (*read_32bit)(off_t,STREAMFILE*) = ea->big_endian ? read_32bitBE : read_32bitLE;
|
||||
|
||||
while (block_offset < file_size) {
|
||||
uint32_t id = read_32bitBE(block_offset+0x00,streamFile);
|
||||
size_t block_size;
|
||||
|
||||
/* BE in SAT, but one file may have both BE and LE chunks */
|
||||
if (guess_endianness32bit(block_offset + 0x04, streamFile))
|
||||
block_size = read_32bitBE(block_offset + 0x04, streamFile);
|
||||
else
|
||||
block_size = read_32bitLE(block_offset + 0x04, streamFile);
|
||||
|
||||
if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */
|
||||
size_t ima_samples = read_32bit(block_offset + 0x08, streamFile);
|
||||
size_t expected_samples = (block_size - 0x08 - 0x04 - 0x08*ea->channels) * 2 / ea->channels;
|
||||
|
||||
if (ima_samples == expected_samples) {
|
||||
return 1; /* has ADPCM hist (hopefully) */
|
||||
}
|
||||
}
|
||||
|
||||
block_offset += block_size;
|
||||
}
|
||||
|
||||
return 0; /* no ADPCM hist */
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
#include "../layout/layout.h"
|
||||
|
||||
#define EA_CODEC_PCM 0x00
|
||||
#define EA_CODEC_ULAW 0x01
|
||||
#define EA_CODEC_IMA 0x02
|
||||
#define EA_CODEC_PSX 0xFF //fake value
|
||||
|
||||
typedef struct {
|
||||
int32_t sample_rate;
|
||||
uint8_t bits;
|
||||
uint8_t channels;
|
||||
uint8_t codec;
|
||||
uint8_t type;
|
||||
int32_t num_samples;
|
||||
int32_t loop_start;
|
||||
int32_t loop_end;
|
||||
int32_t loop_start_offset;
|
||||
int32_t data_offset;
|
||||
|
||||
int big_endian;
|
||||
int loop_flag;
|
||||
int is_sead;
|
||||
int codec_config;
|
||||
int is_bank;
|
||||
int total_subsongs;
|
||||
} eacs_header;
|
||||
|
||||
static int parse_header(STREAMFILE* sf, eacs_header* ea, off_t begin_offset);
|
||||
static VGMSTREAM* init_vgmstream_main(STREAMFILE* sf, eacs_header* ea);
|
||||
|
||||
static void set_ea_1snh_num_samples(VGMSTREAM* vgmstream, STREAMFILE* sf, eacs_header* ea, int find_loop);
|
||||
static int get_ea_1snh_ima_version(STREAMFILE* sf, off_t start_offset, const eacs_header* ea);
|
||||
|
||||
/* EA 1SNh - from early EA games, stream (~1996, ex. Need for Speed) */
|
||||
VGMSTREAM* init_vgmstream_ea_1snh(STREAMFILE* sf) {
|
||||
eacs_header ea = { 0 };
|
||||
off_t offset, eacs_offset;
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .asf/as4: common,
|
||||
* .lasf: fake for plugins
|
||||
* .sng: fake for plugins (for .asf issues)
|
||||
* .cnk: some PS1 games
|
||||
* .uv/tgq: some SAT videos
|
||||
* .tgv: videos
|
||||
* (extensionless): Need for Speed (SAT) videos */
|
||||
if (!check_extensions(sf, "asf,lasf,sng,as4,cnk,uv,tgq,tgv,"))
|
||||
goto fail;
|
||||
|
||||
offset = 0x00;
|
||||
|
||||
/* in TGV videos, either TGVk or 1SNh block comes first */
|
||||
if (is_id32be(0x00, sf, "TGVk")) {
|
||||
offset = read_u32be(0x04, sf);
|
||||
} else if (is_id32be(0x00, sf, "kVGT")) {
|
||||
offset = read_u32le(0x04, sf);
|
||||
}
|
||||
|
||||
if (!is_id32be(offset + 0x00, sf, "1SNh") &&
|
||||
!is_id32be(offset + 0x00, sf, "SEAD"))
|
||||
goto fail;
|
||||
|
||||
/* stream is divided into blocks/chunks: 1SNh=audio header, 1SNd=data xN, 1SNl=loop end, 1SNe=end.
|
||||
* Video uses various blocks (TGVk/TGVf/MUVf/etc) and sometimes alt audio blocks (SEAD/SNDC/SEND). */
|
||||
ea.is_sead = is_id32be(offset + 0x00, sf, "SEAD");
|
||||
|
||||
eacs_offset = offset + 0x08; /* after 1SNh block id+size */
|
||||
|
||||
if (!parse_header(sf, &ea, eacs_offset))
|
||||
goto fail;
|
||||
vgmstream = init_vgmstream_main(sf, &ea);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
if (ea.num_samples == 0) {
|
||||
/* header does not specify number of samples, need to calculate it manually */
|
||||
/* HACK: we need vgmstream object to use blocked layout so we're doing this calc after creating it */
|
||||
set_ea_1snh_num_samples(vgmstream, sf, &ea, 0);
|
||||
|
||||
/* update samples and loop state */
|
||||
vgmstream->num_samples = ea.num_samples;
|
||||
vgmstream_force_loop(vgmstream, ea.loop_flag, ea.loop_start, ea.loop_end);
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* EA EACS - from early EA games, bank (~1996, ex. Need for Speed) */
|
||||
VGMSTREAM* init_vgmstream_ea_eacs(STREAMFILE* sf) {
|
||||
eacs_header ea = {0};
|
||||
off_t eacs_offset;
|
||||
|
||||
|
||||
/* checks */
|
||||
/* .eas: single bank [Need for Speed (PC)]
|
||||
* .bnk: multi bank [Need for Speed (PC)] */
|
||||
if (!check_extensions(sf,"eas,bnk"))
|
||||
goto fail;
|
||||
|
||||
/* plain data without blocks, can contain N*(EACS header) + N*(data), or N (EACS header + data) */
|
||||
ea.is_bank = 1;
|
||||
|
||||
/* use ??? as endian marker (Saturn = BE) */
|
||||
//ea.big_endian = guess_endianness32bit(0x04,sf);
|
||||
|
||||
if (is_id32be(0x00,sf, "EACS")) {
|
||||
/* single bank variant */
|
||||
eacs_offset = 0x00;
|
||||
}
|
||||
else if (read_u32be(0x00,sf) == 0x00) {
|
||||
/* multi bank variant */
|
||||
int i;
|
||||
int target_subsong = sf->stream_index;
|
||||
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0) goto fail;
|
||||
|
||||
/* offsets to EACSs are scattered in the first 0x200
|
||||
* this looks dumb but seems like the only way */
|
||||
eacs_offset = 0;
|
||||
for (i = 0x00; i < 0x200; i += 0x04) {
|
||||
off_t bank_offset = read_u32le(i, sf);
|
||||
if (bank_offset == 0)
|
||||
continue;
|
||||
|
||||
ea.total_subsongs++;
|
||||
|
||||
/* parse mini bank header */
|
||||
if (ea.total_subsongs == target_subsong) {
|
||||
/* 0x00: some id or flags? */
|
||||
eacs_offset = read_u32le(bank_offset + 0x04, sf);
|
||||
if (!is_id32be(eacs_offset, sf, "EACS"))
|
||||
goto fail;
|
||||
/* rest: not sure if part of this header */
|
||||
}
|
||||
}
|
||||
|
||||
if (eacs_offset == 0)
|
||||
goto fail;
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!parse_header(sf,&ea, eacs_offset))
|
||||
goto fail;
|
||||
return init_vgmstream_main(sf, &ea);
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static VGMSTREAM* init_vgmstream_main(STREAMFILE* sf, eacs_header* ea) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(ea->channels, ea->loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->sample_rate = ea->sample_rate;
|
||||
vgmstream->num_samples = ea->num_samples;
|
||||
vgmstream->loop_start_sample = ea->loop_start;
|
||||
vgmstream->loop_end_sample = ea->loop_end;
|
||||
|
||||
vgmstream->codec_endian = ea->big_endian;
|
||||
vgmstream->layout_type = ea->is_bank ? layout_none : layout_blocked_ea_1snh;
|
||||
vgmstream->meta_type = ea->is_bank ? meta_EA_EACS : meta_EA_1SNH;
|
||||
vgmstream->num_streams = ea->total_subsongs;
|
||||
|
||||
switch (ea->codec) {
|
||||
case EA_CODEC_PCM: /* Need for Speed (PC) */
|
||||
vgmstream->coding_type = ea->bits==1 ? coding_PCM8_int : coding_PCM16_int;
|
||||
break;
|
||||
|
||||
case EA_CODEC_ULAW: /* Crusader: No Remorse movies (SAT), FIFA 96 movies (SAT) */
|
||||
if (ea->bits && ea->bits != 2) goto fail; /* only set in EACS */
|
||||
vgmstream->coding_type = coding_ULAW_int;
|
||||
break;
|
||||
|
||||
case EA_CODEC_IMA: /* Need for Speed (PC) */
|
||||
if (ea->bits && ea->bits != 2) goto fail; /* only in EACS */
|
||||
vgmstream->coding_type = coding_DVI_IMA; /* stereo/mono, high nibble first */
|
||||
vgmstream->codec_config = ea->codec_config;
|
||||
break;
|
||||
|
||||
case EA_CODEC_PSX: /* Need for Speed (PS1) */
|
||||
vgmstream->coding_type = coding_PSX;
|
||||
break;
|
||||
|
||||
default:
|
||||
VGM_LOG("EA EACS: unknown codec 0x%02x\n", ea->codec);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, ea->data_offset))
|
||||
goto fail;
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int parse_header(STREAMFILE* sf, eacs_header* ea, off_t offset) {
|
||||
/* audio header endianness doesn't always match block headers, use sample rate to detect */
|
||||
int32_t (*read_s32)(off_t,STREAMFILE*);
|
||||
|
||||
if (is_id32be(offset+0x00,sf, "EACS")) {
|
||||
/* EACS subheader (PC, SAT) */
|
||||
ea->big_endian = guess_endianness32bit(offset + 0x04, sf);
|
||||
read_s32 = ea->big_endian ? read_s32be : read_s32le;
|
||||
|
||||
ea->sample_rate = read_s32(offset+0x04, sf);
|
||||
ea->bits = read_u8(offset+0x08, sf);
|
||||
ea->channels = read_u8(offset+0x09, sf);
|
||||
ea->codec = read_u8(offset+0x0a, sf);
|
||||
ea->type = read_u8(offset+0x0b, sf); /* block type? 0=1SNh, -1=bank */
|
||||
ea->num_samples = read_s32(offset+0x0c, sf);
|
||||
ea->loop_start = read_s32(offset+0x10, sf);
|
||||
ea->loop_end = read_s32(offset+0x14, sf) + ea->loop_start; /* loop length */
|
||||
ea->data_offset = read_s32(offset+0x18, sf); /* 0 when blocked */
|
||||
/* 0x1c: pan/volume/etc? (0x7F)
|
||||
* rest may be padding/garbage */
|
||||
//VGM_ASSERT(ea->type != 0, "EA EACS: unknown type %i\n", ea->type);
|
||||
|
||||
if (ea->codec == EA_CODEC_IMA)
|
||||
ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea);
|
||||
/* EACS banks with empty values exist but will be rejected later */
|
||||
}
|
||||
else if (read_u32be(offset + 0x00, sf) == 0x00) {
|
||||
/* found in early videos, similar to EACS */
|
||||
ea->big_endian = guess_endianness32bit(offset + 0x04, sf);
|
||||
read_s32 = ea->big_endian ? read_s32be : read_s32le;
|
||||
|
||||
ea->sample_rate = read_s32(offset + 0x04, sf);
|
||||
ea->bits = read_u8(offset + 0x08, sf);
|
||||
ea->channels = read_u8(offset + 0x09, sf);
|
||||
ea->codec = read_u8(offset + 0x0a, sf);
|
||||
ea->type = read_u8(offset + 0x0b, sf); /* block type? 0=1SNh, -1=bank */
|
||||
|
||||
if (ea->codec == EA_CODEC_IMA)
|
||||
ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea);
|
||||
}
|
||||
else if (ea->is_sead) {
|
||||
/* alt subheader (found in some PC videos) */
|
||||
ea->big_endian = guess_endianness32bit(offset + 0x00, sf);
|
||||
read_s32 = ea->big_endian ? read_s32be : read_s32le;
|
||||
|
||||
ea->sample_rate = read_s32(offset+0x00, sf);
|
||||
ea->channels = read_s32(offset+0x04, sf);
|
||||
ea->codec = read_s32(offset+0x08, sf);
|
||||
|
||||
if (ea->codec == EA_CODEC_IMA)
|
||||
ea->codec_config = get_ea_1snh_ima_version(sf, 0x00, ea);
|
||||
}
|
||||
else {
|
||||
/* alt subheader (PS1) */
|
||||
ea->big_endian = guess_endianness32bit(offset + 0x00, sf);
|
||||
read_s32 = ea->big_endian ? read_s32be : read_s32le;
|
||||
|
||||
ea->sample_rate = read_s32(offset+0x00, sf);
|
||||
ea->channels = read_u8(offset+0x18, sf);
|
||||
ea->codec = EA_CODEC_PSX;
|
||||
}
|
||||
|
||||
ea->loop_flag = (ea->loop_end > 0);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* get total samples by parsing block headers, needed when EACS isn't present */
|
||||
static void set_ea_1snh_num_samples(VGMSTREAM *vgmstream, STREAMFILE* sf, eacs_header* ea, int find_loop) {
|
||||
int32_t num_samples = 0, block_id;
|
||||
size_t file_size;
|
||||
int32_t(*read_s32)(off_t, STREAMFILE *) = ea->big_endian ? read_s32be : read_s32le;
|
||||
int loop_end_found = 0;
|
||||
|
||||
file_size = get_streamfile_size(sf);
|
||||
vgmstream->next_block_offset = ea->data_offset;
|
||||
|
||||
while (vgmstream->next_block_offset < file_size) {
|
||||
block_update_ea_1snh(vgmstream->next_block_offset, vgmstream);
|
||||
if (vgmstream->current_block_samples < 0)
|
||||
break;
|
||||
|
||||
block_id = read_u32be(vgmstream->current_block_offset, sf);
|
||||
if (find_loop) {
|
||||
if (vgmstream->current_block_offset == ea->loop_start_offset) {
|
||||
ea->loop_start = num_samples;
|
||||
ea->loop_flag = 1;
|
||||
block_update_ea_1snh(ea->data_offset, vgmstream);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (block_id == get_id32be("1SNl") ) { /* loop point found */
|
||||
ea->loop_start_offset = read_s32(vgmstream->current_block_offset + 0x08, sf);
|
||||
ea->loop_end = num_samples;
|
||||
loop_end_found = 1;
|
||||
}
|
||||
}
|
||||
|
||||
num_samples += vgmstream->current_block_samples;
|
||||
}
|
||||
|
||||
ea->num_samples = num_samples;
|
||||
|
||||
/* reset once we're done */
|
||||
block_update_ea_1snh(ea->data_offset, vgmstream);
|
||||
|
||||
if (loop_end_found) {
|
||||
/* recurse to find loop start sample */
|
||||
set_ea_1snh_num_samples(vgmstream, sf, ea, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* find codec version used, with or without ADPCM hist per block */
|
||||
static int get_ea_1snh_ima_version(STREAMFILE* sf, off_t start_offset, const eacs_header* ea) {
|
||||
off_t block_offset = start_offset;
|
||||
size_t file_size = get_streamfile_size(sf);
|
||||
int32_t (*read_s32)(off_t,STREAMFILE*) = ea->big_endian ? read_s32be : read_s32le;
|
||||
|
||||
while (block_offset < file_size) {
|
||||
uint32_t id = read_u32be(block_offset+0x00,sf);
|
||||
size_t block_size;
|
||||
|
||||
/* BE in SAT, but one file may have both BE and LE chunks */
|
||||
if (guess_endianness32bit(block_offset + 0x04, sf))
|
||||
block_size = read_u32be(block_offset + 0x04, sf);
|
||||
else
|
||||
block_size = read_u32le(block_offset + 0x04, sf);
|
||||
|
||||
if (id == 0x31534E64 || id == 0x534E4443) { /* "1SNd" "SNDC" audio data */
|
||||
size_t ima_samples = read_s32(block_offset + 0x08, sf);
|
||||
size_t expected_samples = (block_size - 0x08 - 0x04 - 0x08*ea->channels) * 2 / ea->channels;
|
||||
|
||||
if (ima_samples == expected_samples) {
|
||||
return 1; /* has ADPCM hist (hopefully) */
|
||||
}
|
||||
}
|
||||
|
||||
block_offset += block_size;
|
||||
}
|
||||
|
||||
return 0; /* no ADPCM hist */
|
||||
}
|
||||
|
|
|
@ -809,10 +809,11 @@ fail:
|
|||
|
||||
/* EA Harmony Sample Bank - used in 8th gen EA Sports games */
|
||||
VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
|
||||
uint64_t set_sounds, base_offset, sound_offset;
|
||||
uint32_t chunk_id, data_offset, table_offset, dset_offset, sound_table_offset;
|
||||
uint64_t base_offset, sound_offset, offset, prev_offset;
|
||||
uint32_t chunk_id, data_offset, table_offset, dset_offset, set_values, set_sounds, sound_table_offset;
|
||||
int32_t flag;
|
||||
uint16_t num_dsets;
|
||||
uint8_t set_type, flag, offset_size;
|
||||
uint8_t set_type, offset_size;
|
||||
uint32_t i, j;
|
||||
char sound_name[STREAM_NAME_SIZE];
|
||||
STREAMFILE *sf_sbs = NULL, *sf_data = NULL;
|
||||
|
@ -826,11 +827,11 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
|
|||
goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_u32be(0x00, sf) == 0x53426C65) { /* "SBle" */
|
||||
if (is_id32be(0x00, sf, "SBle")) {
|
||||
read_u64 = read_u64le;
|
||||
read_u32 = read_u32le;
|
||||
read_u16 = read_u16le;
|
||||
} else if (read_u32be(0x00, sf) == 0x53426265) { /* "SBbe" */
|
||||
} else if (is_id32be(0x00, sf, "SBbe")) {
|
||||
read_u64 = read_u64be;
|
||||
read_u32 = read_u32be;
|
||||
read_u16 = read_u16be;
|
||||
|
@ -839,8 +840,8 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
|
|||
}
|
||||
|
||||
num_dsets = read_u16(0x0a, sf);
|
||||
table_offset = read_u32(0x18, sf);
|
||||
data_offset = read_u32(0x20, sf);
|
||||
table_offset = read_u32(0x24, sf);
|
||||
|
||||
if (target_stream == 0) target_stream = 1;
|
||||
if (target_stream < 0)
|
||||
|
@ -849,14 +850,14 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
|
|||
total_sounds = 0;
|
||||
sound_offset = 0;
|
||||
|
||||
/* The bank is split into DSET sections each of which references one or multiple sounds. */
|
||||
/* Each set can contain RAM sounds (stored in SBR in data section) or streamed sounds (stored separately in SBS file). */
|
||||
/* the bank is split into DSET sections each of which references one or multiple sounds */
|
||||
/* each set can contain RAM sounds (stored in SBR in data section) or streamed sounds (stored separately in SBS file) */
|
||||
for (i = 0; i < num_dsets; i++) {
|
||||
dset_offset = read_u32(table_offset + 0x08 * i, sf);
|
||||
if (read_u32(dset_offset, sf) != 0x44534554) /* "DSET" */
|
||||
goto fail;
|
||||
|
||||
set_sounds = read_u32(dset_offset + 0x38, sf);
|
||||
set_values = read_u32(dset_offset + 0x38, sf);
|
||||
local_target = target_stream - total_sounds - 1;
|
||||
dset_offset += 0x48;
|
||||
|
||||
|
@ -877,50 +878,60 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
|
|||
}
|
||||
}
|
||||
|
||||
/* Different set types store offsets differently */
|
||||
/* different set types store offsets differently */
|
||||
set_type = read_u8(dset_offset + 0x05, sf);
|
||||
|
||||
/* data sets often contain duplicate offets, need to filter them out however we can */
|
||||
/* offsets are stored in ascending order which makes things easier */
|
||||
if (set_type == 0x00) {
|
||||
total_sounds++;
|
||||
set_sounds = 1;
|
||||
total_sounds += set_sounds;
|
||||
if (local_target < 0 || local_target > 0)
|
||||
continue;
|
||||
|
||||
sound_offset = read_u64(dset_offset + 0x08, sf);
|
||||
} else if (set_type == 0x01) {
|
||||
total_sounds += 2;
|
||||
if (local_target < 0 || local_target > 1)
|
||||
continue;
|
||||
|
||||
flag = (int16_t)read_u16(dset_offset + 0x06, sf);
|
||||
base_offset = read_u64(dset_offset + 0x08, sf);
|
||||
|
||||
if (local_target == 0) {
|
||||
sound_offset = base_offset;
|
||||
} else {
|
||||
sound_offset = base_offset + read_u16(dset_offset + 0x06, sf);
|
||||
}
|
||||
set_sounds = set_values;
|
||||
total_sounds += set_sounds;
|
||||
if (local_target < 0 || local_target >= set_sounds)
|
||||
continue;
|
||||
|
||||
sound_offset = base_offset + flag * local_target;
|
||||
} else if (set_type == 0x02) {
|
||||
flag = (read_u16(dset_offset + 0x06, sf) >> 0) & 0xFF;
|
||||
offset_size = (read_u16(dset_offset + 0x06, sf) >> 8) & 0xFF;
|
||||
base_offset = read_u64(dset_offset + 0x08, sf);
|
||||
sound_table_offset = read_u32(dset_offset + 0x10, sf);
|
||||
|
||||
set_sounds = 0;
|
||||
prev_offset = UINT64_MAX;
|
||||
for (j = 0; j < set_values; j++) {
|
||||
if (offset_size == 0x01) {
|
||||
offset = read_u8(sound_table_offset + 0x01 * j, sf);
|
||||
} else if (offset_size == 0x02) {
|
||||
offset = read_u16(sound_table_offset + 0x02 * j, sf);
|
||||
} else if (offset_size == 0x04) {
|
||||
offset = read_u32(sound_table_offset + 0x04 * j, sf);
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
offset <<= flag;
|
||||
offset += base_offset;
|
||||
|
||||
if (offset != prev_offset) {
|
||||
if (set_sounds == local_target)
|
||||
sound_offset = offset;
|
||||
set_sounds++;
|
||||
}
|
||||
prev_offset = offset;
|
||||
}
|
||||
|
||||
total_sounds += set_sounds;
|
||||
if (local_target < 0 || local_target >= set_sounds)
|
||||
continue;
|
||||
|
||||
if (offset_size == 0x01) {
|
||||
sound_offset = read_u8(sound_table_offset + 0x01 * local_target, sf);
|
||||
for (j = 0; j < flag; j++) sound_offset *= 2;
|
||||
} else if (offset_size == 0x02) {
|
||||
sound_offset = read_u16(sound_table_offset + 0x02 * local_target, sf);
|
||||
for (j = 0; j < flag; j++) sound_offset *= 2;
|
||||
} else if (offset_size == 0x04) {
|
||||
sound_offset = read_u32(sound_table_offset + 0x04 * local_target, sf);
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
sound_offset += base_offset;
|
||||
} else if (set_type == 0x03) {
|
||||
offset_size = (read_u16(dset_offset + 0x06, sf) >> 8) & 0xFF;
|
||||
set_sounds = read_u64(dset_offset + 0x08, sf);
|
||||
|
@ -940,12 +951,24 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
|
|||
goto fail;
|
||||
}
|
||||
} else if (set_type == 0x04) {
|
||||
sound_table_offset = read_u32(dset_offset + 0x10, sf);
|
||||
|
||||
set_sounds = 0;
|
||||
prev_offset = UINT64_MAX;
|
||||
for (j = 0; j < set_values; j++) {
|
||||
offset = read_u64(sound_table_offset + 0x08 * j, sf);
|
||||
|
||||
if (sound_offset != prev_offset) {
|
||||
if (set_sounds == local_target)
|
||||
sound_offset = offset;
|
||||
set_sounds++;
|
||||
}
|
||||
prev_offset = offset;
|
||||
}
|
||||
|
||||
total_sounds += set_sounds;
|
||||
if (local_target < 0 || local_target >= set_sounds)
|
||||
continue;
|
||||
|
||||
sound_table_offset = read_u32(dset_offset + 0x10, sf);
|
||||
sound_offset = read_u32(sound_table_offset + 0x08 * local_target, sf);
|
||||
} else {
|
||||
goto fail;
|
||||
}
|
||||
|
@ -964,8 +987,8 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
|
|||
|
||||
if (!is_streamed) {
|
||||
/* RAM asset */
|
||||
if (read_u32be(data_offset, sf) != 0x64617461 && /* "data" */
|
||||
read_u32be(data_offset, sf) != 0x44415441) /* "DATA" */
|
||||
if (!is_id32be(data_offset, sf, "data") &&
|
||||
!is_id32be(data_offset, sf, "DATA"))
|
||||
goto fail;
|
||||
|
||||
sf_data = sf;
|
||||
|
@ -976,13 +999,13 @@ VGMSTREAM* init_vgmstream_ea_sbr_harmony(STREAMFILE* sf) {
|
|||
if (!sf_sbs)
|
||||
goto fail;
|
||||
|
||||
if (read_u32be(0x00, sf_sbs) != 0x64617461 && /* "data" */
|
||||
read_u32be(0x00, sf_sbs) != 0x44415441) /* "DATA" */
|
||||
if (!is_id32be(0x00, sf_sbs, "data") &&
|
||||
!is_id32be(0x00, sf_sbs, "DATA"))
|
||||
goto fail;
|
||||
|
||||
sf_data = sf_sbs;
|
||||
|
||||
if (read_u32be(sound_offset, sf_data) == 0x736C6F74) {
|
||||
if (is_id32be(sound_offset, sf_data, "slot")) {
|
||||
/* skip "slot" section */
|
||||
sound_offset += 0x30;
|
||||
}
|
||||
|
|
|
@ -1,22 +1,19 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* FDA - from Relic Entertainment games [Warhammer 4000: Dawn of War (PC)] */
|
||||
VGMSTREAM * init_vgmstream_fda(STREAMFILE *sf) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
VGMSTREAM* init_vgmstream_fda(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag, channel_count, bitrate, sample_rate, num_samples;
|
||||
int loop_flag, channels, bitrate, sample_rate, num_samples;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "fda"))
|
||||
goto fail;
|
||||
|
||||
if (read_u32be(0x00, sf) != 0x52656C69 || /* "Reli" */
|
||||
read_u32be(0x04, sf) != 0x63204368 || /* "c Ch" */
|
||||
read_u32be(0x08, sf) != 0x756E6B79 || /* "unky" */
|
||||
read_u32be(0x0c, sf) != 0x0D0A1A00) /* "\r\n\1a\00"*/
|
||||
if (!is_id64be(0x00, sf, "Relic Ch") ||
|
||||
!is_id64be(0x08, sf, "unky\r\n\x1a\x00"))
|
||||
goto fail;
|
||||
|
||||
/* version? (later .fda change this) */
|
||||
|
@ -38,18 +35,18 @@ VGMSTREAM * init_vgmstream_fda(STREAMFILE *sf) {
|
|||
offset += 0x14 + name_size + chunk_size;
|
||||
|
||||
/* FOLD-FDA (folder of chunks) */
|
||||
if (read_u32be(offset + 0x04, sf) != 0x46444120) /* "FDA " */
|
||||
if (!is_id32be(offset + 0x04, sf, "FDA "))
|
||||
goto fail;
|
||||
offset += 0x14;
|
||||
|
||||
/* DATA-INFO (header) */
|
||||
if (read_u32be(offset + 0x04, sf) != 0x494E464F) /* "INFO" */
|
||||
if (!is_id32be(offset + 0x04, sf, "INFO"))
|
||||
goto fail;
|
||||
chunk_size = read_u32le(offset + 0x0c, sf);
|
||||
name_size = read_u32le(offset + 0x10, sf);
|
||||
offset += 0x14 + name_size;
|
||||
|
||||
channel_count = read_s32le(offset + 0x00, sf);
|
||||
channels = read_s32le(offset + 0x00, sf);
|
||||
/* 0x04: bps */
|
||||
bitrate = read_s32le(offset + 0x08, sf);
|
||||
sample_rate = read_s32le(offset + 0x0c, sf);
|
||||
|
@ -60,28 +57,28 @@ VGMSTREAM * init_vgmstream_fda(STREAMFILE *sf) {
|
|||
offset += chunk_size;
|
||||
|
||||
/* DATA-DATA (data) */
|
||||
if (read_u32be(offset + 0x04, sf) != 0x44415441) /* "DATA" */
|
||||
if (!is_id32be(offset + 0x04, sf, "DATA"))
|
||||
goto fail;
|
||||
chunk_size = read_u32le(offset + 0x0c, sf);
|
||||
name_size = read_u32le(offset + 0x10, sf);
|
||||
offset += 0x14 + name_size;
|
||||
|
||||
data_size = read_s32le(offset + 0x00, sf);
|
||||
data_size = read_u32le(offset + 0x00, sf);
|
||||
|
||||
start_offset = offset + 0x04;
|
||||
num_samples = data_size / channel_count / (bitrate / 8) * 512;
|
||||
num_samples = relic_bytes_to_samples(data_size, channels, bitrate);
|
||||
}
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_FDA;
|
||||
vgmstream->sample_rate = 44100; /* fixed output */
|
||||
vgmstream->num_samples = num_samples;
|
||||
|
||||
vgmstream->codec_data = init_relic(channel_count, bitrate, sample_rate);
|
||||
vgmstream->codec_data = init_relic(channels, bitrate, sample_rate);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_RELIC;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
//#define HCA_BRUTEFORCE
|
||||
#ifdef HCA_BRUTEFORCE
|
||||
static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* out_keycode, uint16_t subkey);
|
||||
static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey);
|
||||
#endif
|
||||
static void find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t subkey);
|
||||
|
||||
|
@ -191,7 +191,7 @@ done:
|
|||
/* Bruteforce binary keys in executables and similar files, mainly for some mobile games.
|
||||
* Kinda slow but acceptable for ~20MB exes, not very optimized. Unity usually has keys
|
||||
* in plaintext (inside levelX or other base files) instead though. */
|
||||
static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* out_keycode, uint16_t subkey) {
|
||||
static void bruteforce_hca_key_bin(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
|
||||
STREAMFILE* sf_keys = NULL;
|
||||
uint8_t* buf = NULL;
|
||||
int best_score = 0xFFFFFF, cur_score;
|
||||
|
@ -200,9 +200,9 @@ static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigne
|
|||
uint64_t old_key = 0;
|
||||
|
||||
|
||||
VGM_LOG("HCA: test keys\n");
|
||||
VGM_LOG("HCA: test keys.bin\n");
|
||||
|
||||
*out_keycode = 0;
|
||||
*p_keycode = 0;
|
||||
|
||||
/* load whole file in memory for performance (exes with keys shouldn't be too big) */
|
||||
sf_keys = open_streamfile_by_filename(sf, "keys.bin");
|
||||
|
@ -232,14 +232,15 @@ static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigne
|
|||
//key = ((uint64_t)get_u32be(buf + pos + 0x00) << 0 ) | 0; /* upper bytes not set, ex. P5 */
|
||||
|
||||
/* observed files have aligned keys, change if needed */
|
||||
pos += 0x04; //pos++;
|
||||
pos += 0x04;
|
||||
//pos++;
|
||||
|
||||
if (key == 0 || key == old_key)
|
||||
continue;
|
||||
old_key = key;
|
||||
|
||||
cur_score = 0;
|
||||
test_key(hca_data, key, subkey, &cur_score, out_keycode);
|
||||
test_key(hca_data, key, subkey, &cur_score, p_keycode);
|
||||
if (cur_score == 1)
|
||||
goto done;
|
||||
|
||||
|
@ -253,10 +254,98 @@ static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigne
|
|||
|
||||
done:
|
||||
VGM_ASSERT(best_score > 0, "HCA: best key=%08x%08x (score=%i)\n",
|
||||
(uint32_t)((*out_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*out_keycode & 0xFFFFFFFF), best_score);
|
||||
(uint32_t)((*p_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*p_keycode & 0xFFFFFFFF), best_score);
|
||||
VGM_ASSERT(best_score < 0, "HCA: key not found\n");
|
||||
if (best_score < 0 || best_score > 10000)
|
||||
*p_keycode = 0;
|
||||
|
||||
close_streamfile(sf_keys);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
#include <inttypes.h>
|
||||
//#include <stdio.h>
|
||||
|
||||
/* same as the above but for txt lines. */
|
||||
static void bruteforce_hca_key_txt(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
|
||||
STREAMFILE* sf_keys = NULL;
|
||||
uint8_t* buf = NULL;
|
||||
int best_score = 0xFFFFFF, cur_score;
|
||||
off_t keys_size, bytes;
|
||||
int i = 0, pos;
|
||||
char line[1024];
|
||||
|
||||
|
||||
VGM_LOG("HCA: test keys.txt\n");
|
||||
|
||||
*p_keycode = 0;
|
||||
|
||||
/* load whole file in memory for performance (exes with keys shouldn't be too big) */
|
||||
sf_keys = open_streamfile_by_filename(sf, "keys.txt");
|
||||
if (!sf_keys) goto done;
|
||||
|
||||
keys_size = get_streamfile_size(sf_keys);
|
||||
|
||||
buf = malloc(keys_size);
|
||||
if (!buf) goto done;
|
||||
|
||||
bytes = read_streamfile(buf, 0, keys_size, sf_keys);
|
||||
if (bytes != keys_size) goto done;
|
||||
|
||||
VGM_LOG("HCA: start\n");
|
||||
|
||||
pos = 0;
|
||||
while (pos < keys_size) {
|
||||
int bytes_read, line_ok, count;
|
||||
uint64_t key = 0;
|
||||
|
||||
bytes_read = read_line(line, sizeof(line), pos, sf_keys, &line_ok);
|
||||
if (!line_ok) continue; //???
|
||||
|
||||
pos += bytes_read;
|
||||
|
||||
count = sscanf(line, "%" SCNd64, &key);
|
||||
if (count != 1) continue;
|
||||
|
||||
VGM_ASSERT(pos % 100000 == 0, "HCA: count %i...\n", i);
|
||||
|
||||
if (key == 0)
|
||||
continue;
|
||||
i++;
|
||||
|
||||
cur_score = 0;
|
||||
test_key(hca_data, key, subkey, &cur_score, p_keycode);
|
||||
if (cur_score == 1)
|
||||
goto done;
|
||||
|
||||
if (cur_score > 0 && cur_score <= 500) {
|
||||
VGM_LOG("HCA: possible key=%08x%08x (score=%i) at %x\n",
|
||||
(uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), cur_score, pos-0x04);
|
||||
if (best_score > cur_score)
|
||||
best_score = cur_score;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
VGM_LOG("HCA: done %i keys.txt\n", i);
|
||||
VGM_ASSERT(best_score > 0, "HCA: best key=%08x%08x (score=%i)\n",
|
||||
(uint32_t)((*p_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*p_keycode & 0xFFFFFFFF), best_score);
|
||||
VGM_ASSERT(best_score < 0, "HCA: key not found\n");
|
||||
if (best_score < 0 || best_score > 10000)
|
||||
*p_keycode = 0;
|
||||
|
||||
close_streamfile(sf_keys);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void bruteforce_hca_key(STREAMFILE* sf, hca_codec_data* hca_data, unsigned long long* p_keycode, uint16_t subkey) {
|
||||
bruteforce_hca_key_bin(sf, hca_data, p_keycode, subkey);
|
||||
if (*p_keycode != 0)
|
||||
return;
|
||||
|
||||
bruteforce_hca_key_txt(sf, hca_data, p_keycode, subkey);
|
||||
if (*p_keycode != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -359,7 +359,7 @@ VGMSTREAM * init_vgmstream_mul(STREAMFILE * streamFile);
|
|||
|
||||
VGMSTREAM * init_vgmstream_thp(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_wii_sts(STREAMFILE *streamFile);
|
||||
VGMSTREAM* init_vgmstream_sts(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_ps2_p2bt(STREAMFILE *streamFile);
|
||||
|
||||
|
@ -533,9 +533,9 @@ VGMSTREAM * init_vgmstream_hyperscan_kvag(STREAMFILE* streamFile);
|
|||
|
||||
VGMSTREAM * init_vgmstream_ios_psnd(STREAMFILE* streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_pc_adp_bos(STREAMFILE* streamFile);
|
||||
VGMSTREAM* init_vgmstream_adp_bos(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_pc_adp_otns(STREAMFILE* streamFile);
|
||||
VGMSTREAM* init_vgmstream_adp_qd(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM * init_vgmstream_eb_sfx(STREAMFILE* streamFile);
|
||||
VGMSTREAM * init_vgmstream_eb_sf0(STREAMFILE* streamFile);
|
||||
|
@ -955,4 +955,8 @@ VGMSTREAM* init_vgmstream_sspr(STREAMFILE* sf);
|
|||
|
||||
VGMSTREAM* init_vgmstream_piff_tpcm(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_wxd_wxh(STREAMFILE* sf);
|
||||
|
||||
VGMSTREAM* init_vgmstream_bnk_relic(STREAMFILE* sf);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
|
|
@ -1,167 +1,7 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
#ifdef VGM_USE_MP4V2
|
||||
void* mp4_file_open( const char* name, MP4FileMode mode )
|
||||
{
|
||||
char * endptr;
|
||||
#ifdef _MSC_VER
|
||||
unsigned __int64 ptr = _strtoui64( name, &endptr, 16 );
|
||||
#else
|
||||
unsigned long ptr = strtoul( name, &endptr, 16 );
|
||||
#endif
|
||||
return (void*) ptr;
|
||||
}
|
||||
|
||||
int mp4_file_seek( void* handle, int64_t pos )
|
||||
{
|
||||
mp4_streamfile * file = ( mp4_streamfile * ) handle;
|
||||
if ( pos > file->size ) pos = file->size;
|
||||
pos += file->start;
|
||||
file->offset = pos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mp4_file_get_size( void* handle, int64_t* size )
|
||||
{
|
||||
mp4_streamfile * file = ( mp4_streamfile * ) handle;
|
||||
*size = file->size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mp4_file_read( void* handle, void* buffer, int64_t size, int64_t* nin, int64_t maxChunkSize )
|
||||
{
|
||||
mp4_streamfile * file = ( mp4_streamfile * ) handle;
|
||||
int64_t max_size = file->size - file->offset - file->start;
|
||||
if ( size > max_size ) size = max_size;
|
||||
if ( size > 0 )
|
||||
{
|
||||
*nin = read_streamfile( (uint8_t *) buffer, file->offset, size, file->streamfile );
|
||||
file->offset += *nin;
|
||||
}
|
||||
else
|
||||
{
|
||||
*nin = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mp4_file_write( void* handle, const void* buffer, int64_t size, int64_t* nout, int64_t maxChunkSize )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mp4_file_close( void* handle )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
MP4FileProvider mp4_file_provider = { mp4_file_open, mp4_file_seek, mp4_file_read, mp4_file_write, mp4_file_close, mp4_file_get_size };
|
||||
|
||||
#ifdef VGM_USE_FDKAAC
|
||||
VGMSTREAM * init_vgmstream_mp4_aac_offset(STREAMFILE *sf, uint64_t start, uint64_t size);
|
||||
|
||||
VGMSTREAM * init_vgmstream_mp4_aac(STREAMFILE *sf) {
|
||||
return init_vgmstream_mp4_aac_offset( sf, 0, sf->get_size(sf) );
|
||||
}
|
||||
|
||||
VGMSTREAM * init_vgmstream_mp4_aac_offset(STREAMFILE *sf, uint64_t start, uint64_t size) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
mp4_aac_codec_data * aac_file = ( mp4_aac_codec_data * ) calloc(1, sizeof(mp4_aac_codec_data));
|
||||
|
||||
CStreamInfo * stream_info;
|
||||
|
||||
uint8_t * buffer = NULL;
|
||||
uint32_t buffer_size;
|
||||
UINT ubuffer_size, bytes_valid;
|
||||
|
||||
if ( !aac_file ) goto fail;
|
||||
|
||||
aac_file->if_file.streamfile = sf;
|
||||
aac_file->if_file.start = start;
|
||||
aac_file->if_file.offset = start;
|
||||
aac_file->if_file.size = size;
|
||||
|
||||
/* Big ol' kludge! */
|
||||
sprintf( filename, "%p", &aac_file->if_file );
|
||||
aac_file->h_mp4file = MP4ReadProvider( filename, &mp4_file_provider );
|
||||
if ( !aac_file->h_mp4file ) goto fail;
|
||||
|
||||
if ( MP4GetNumberOfTracks(aac_file->h_mp4file, MP4_AUDIO_TRACK_TYPE, '\000') != 1 ) goto fail;
|
||||
|
||||
aac_file->track_id = MP4FindTrackId( aac_file->h_mp4file, 0, MP4_AUDIO_TRACK_TYPE, '\000' );
|
||||
|
||||
aac_file->h_aacdecoder = aacDecoder_Open( TT_MP4_RAW, 1 );
|
||||
if ( !aac_file->h_aacdecoder ) goto fail;
|
||||
|
||||
MP4GetTrackESConfiguration( aac_file->h_mp4file, aac_file->track_id, (uint8_t**)(&buffer), (uint32_t*)(&buffer_size));
|
||||
|
||||
ubuffer_size = buffer_size;
|
||||
if ( aacDecoder_ConfigRaw( aac_file->h_aacdecoder, &buffer, &ubuffer_size ) ) goto fail;
|
||||
|
||||
free( buffer ); buffer = NULL;
|
||||
|
||||
aac_file->sampleId = 1;
|
||||
aac_file->numSamples = MP4GetTrackNumberOfSamples( aac_file->h_mp4file, aac_file->track_id );
|
||||
|
||||
if (!MP4ReadSample(aac_file->h_mp4file, aac_file->track_id, aac_file->sampleId, (uint8_t**)(&buffer), (uint32_t*)(&buffer_size), 0, 0, 0, 0)) goto fail;
|
||||
|
||||
ubuffer_size = buffer_size;
|
||||
bytes_valid = buffer_size;
|
||||
if ( aacDecoder_Fill( aac_file->h_aacdecoder, &buffer, &ubuffer_size, &bytes_valid ) || bytes_valid ) goto fail;
|
||||
if ( aacDecoder_DecodeFrame( aac_file->h_aacdecoder, aac_file->sample_buffer, ( (6) * (2048)*4 ), 0 ) ) goto fail;
|
||||
|
||||
free( buffer ); buffer = NULL;
|
||||
|
||||
aac_file->sample_ptr = 0;
|
||||
|
||||
stream_info = aacDecoder_GetStreamInfo( aac_file->h_aacdecoder );
|
||||
|
||||
aac_file->samples_per_frame = stream_info->frameSize;
|
||||
aac_file->samples_discard = 0;
|
||||
|
||||
sf->get_name( sf, filename, sizeof(filename) );
|
||||
|
||||
aac_file->if_file.streamfile = sf->open(sf, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!aac_file->if_file.streamfile) goto fail;
|
||||
|
||||
vgmstream = allocate_vgmstream( stream_info->numChannels, 1 );
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->loop_flag = 0;
|
||||
|
||||
vgmstream->codec_data = aac_file;
|
||||
|
||||
vgmstream->channels = stream_info->numChannels;
|
||||
vgmstream->sample_rate = stream_info->sampleRate;
|
||||
|
||||
vgmstream->num_samples = stream_info->frameSize * aac_file->numSamples;
|
||||
|
||||
vgmstream->coding_type = coding_MP4_AAC;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_MP4;
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
if ( buffer ) free( buffer );
|
||||
if ( aac_file ) {
|
||||
if ( aac_file->h_aacdecoder ) aacDecoder_Close( aac_file->h_aacdecoder );
|
||||
if ( aac_file->h_mp4file ) MP4Close( aac_file->h_mp4file, 0 );
|
||||
free( aac_file );
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
|
||||
typedef struct {
|
||||
int channels;
|
||||
int sample_rate;
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||
// VGM_USE_MP4V2
|
||||
void* mp4_file_open( const char* name, MP4FileMode mode )
|
||||
{
|
||||
char * endptr;
|
||||
#ifdef _MSC_VER
|
||||
unsigned __int64 ptr = _strtoui64( name, &endptr, 16 );
|
||||
#else
|
||||
unsigned long ptr = strtoul( name, &endptr, 16 );
|
||||
#endif
|
||||
return (void*) ptr;
|
||||
}
|
||||
|
||||
int mp4_file_seek( void* handle, int64_t pos )
|
||||
{
|
||||
mp4_streamfile * file = ( mp4_streamfile * ) handle;
|
||||
if ( pos > file->size ) pos = file->size;
|
||||
pos += file->start;
|
||||
file->offset = pos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mp4_file_get_size( void* handle, int64_t* size )
|
||||
{
|
||||
mp4_streamfile * file = ( mp4_streamfile * ) handle;
|
||||
*size = file->size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mp4_file_read( void* handle, void* buffer, int64_t size, int64_t* nin, int64_t maxChunkSize )
|
||||
{
|
||||
mp4_streamfile * file = ( mp4_streamfile * ) handle;
|
||||
int64_t max_size = file->size - file->offset - file->start;
|
||||
if ( size > max_size ) size = max_size;
|
||||
if ( size > 0 )
|
||||
{
|
||||
*nin = read_streamfile( (uint8_t *) buffer, file->offset, size, file->streamfile );
|
||||
file->offset += *nin;
|
||||
}
|
||||
else
|
||||
{
|
||||
*nin = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mp4_file_write( void* handle, const void* buffer, int64_t size, int64_t* nout, int64_t maxChunkSize )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mp4_file_close( void* handle )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
MP4FileProvider mp4_file_provider = { mp4_file_open, mp4_file_seek, mp4_file_read, mp4_file_write, mp4_file_close, mp4_file_get_size };
|
||||
|
||||
// VGM_USE_FDKAAC
|
||||
VGMSTREAM * init_vgmstream_mp4_aac_offset(STREAMFILE *sf, uint64_t start, uint64_t size);
|
||||
|
||||
VGMSTREAM * init_vgmstream_mp4_aac(STREAMFILE *sf) {
|
||||
return init_vgmstream_mp4_aac_offset( sf, 0, sf->get_size(sf) );
|
||||
}
|
||||
|
||||
VGMSTREAM * init_vgmstream_mp4_aac_offset(STREAMFILE *sf, uint64_t start, uint64_t size) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
mp4_aac_codec_data * aac_file = ( mp4_aac_codec_data * ) calloc(1, sizeof(mp4_aac_codec_data));
|
||||
|
||||
CStreamInfo * stream_info;
|
||||
|
||||
uint8_t * buffer = NULL;
|
||||
uint32_t buffer_size;
|
||||
UINT ubuffer_size, bytes_valid;
|
||||
|
||||
if ( !aac_file ) goto fail;
|
||||
|
||||
aac_file->if_file.streamfile = sf;
|
||||
aac_file->if_file.start = start;
|
||||
aac_file->if_file.offset = start;
|
||||
aac_file->if_file.size = size;
|
||||
|
||||
/* Big ol' kludge! */
|
||||
sprintf( filename, "%p", &aac_file->if_file );
|
||||
aac_file->h_mp4file = MP4ReadProvider( filename, &mp4_file_provider );
|
||||
if ( !aac_file->h_mp4file ) goto fail;
|
||||
|
||||
if ( MP4GetNumberOfTracks(aac_file->h_mp4file, MP4_AUDIO_TRACK_TYPE, '\000') != 1 ) goto fail;
|
||||
|
||||
aac_file->track_id = MP4FindTrackId( aac_file->h_mp4file, 0, MP4_AUDIO_TRACK_TYPE, '\000' );
|
||||
|
||||
aac_file->h_aacdecoder = aacDecoder_Open( TT_MP4_RAW, 1 );
|
||||
if ( !aac_file->h_aacdecoder ) goto fail;
|
||||
|
||||
MP4GetTrackESConfiguration( aac_file->h_mp4file, aac_file->track_id, (uint8_t**)(&buffer), (uint32_t*)(&buffer_size));
|
||||
|
||||
ubuffer_size = buffer_size;
|
||||
if ( aacDecoder_ConfigRaw( aac_file->h_aacdecoder, &buffer, &ubuffer_size ) ) goto fail;
|
||||
|
||||
free( buffer ); buffer = NULL;
|
||||
|
||||
aac_file->sampleId = 1;
|
||||
aac_file->numSamples = MP4GetTrackNumberOfSamples( aac_file->h_mp4file, aac_file->track_id );
|
||||
|
||||
if (!MP4ReadSample(aac_file->h_mp4file, aac_file->track_id, aac_file->sampleId, (uint8_t**)(&buffer), (uint32_t*)(&buffer_size), 0, 0, 0, 0)) goto fail;
|
||||
|
||||
ubuffer_size = buffer_size;
|
||||
bytes_valid = buffer_size;
|
||||
if ( aacDecoder_Fill( aac_file->h_aacdecoder, &buffer, &ubuffer_size, &bytes_valid ) || bytes_valid ) goto fail;
|
||||
if ( aacDecoder_DecodeFrame( aac_file->h_aacdecoder, aac_file->sample_buffer, ( (6) * (2048)*4 ), 0 ) ) goto fail;
|
||||
|
||||
free( buffer ); buffer = NULL;
|
||||
|
||||
aac_file->sample_ptr = 0;
|
||||
|
||||
stream_info = aacDecoder_GetStreamInfo( aac_file->h_aacdecoder );
|
||||
|
||||
aac_file->samples_per_frame = stream_info->frameSize;
|
||||
aac_file->samples_discard = 0;
|
||||
|
||||
sf->get_name( sf, filename, sizeof(filename) );
|
||||
|
||||
aac_file->if_file.streamfile = sf->open(sf, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
if (!aac_file->if_file.streamfile) goto fail;
|
||||
|
||||
vgmstream = allocate_vgmstream( stream_info->numChannels, 1 );
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->loop_flag = 0;
|
||||
|
||||
vgmstream->codec_data = aac_file;
|
||||
|
||||
vgmstream->channels = stream_info->numChannels;
|
||||
vgmstream->sample_rate = stream_info->sampleRate;
|
||||
|
||||
vgmstream->num_samples = stream_info->frameSize * aac_file->numSamples;
|
||||
|
||||
vgmstream->coding_type = coding_MP4_AAC;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_MP4;
|
||||
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
if ( buffer ) free( buffer );
|
||||
if ( aac_file ) {
|
||||
if ( aac_file->h_aacdecoder ) aacDecoder_Close( aac_file->h_aacdecoder );
|
||||
if ( aac_file->h_mp4file ) MP4Close( aac_file->h_mp4file, 0 );
|
||||
free( aac_file );
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
|
@ -1,45 +1,43 @@
|
|||
#include "meta.h"
|
||||
|
||||
/* ADP - from Balls of Steel */
|
||||
VGMSTREAM * init_vgmstream_pc_adp_bos(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
VGMSTREAM* init_vgmstream_adp_bos(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag = 0;
|
||||
int channel_count;
|
||||
int channels;
|
||||
|
||||
if (!check_extensions(streamFile,"adp")) goto fail;
|
||||
|
||||
/* check header */
|
||||
if (read_32bitBE(0x00,streamFile) != 0x41445021) /* "ADP!" */
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"adp"))
|
||||
goto fail;
|
||||
|
||||
loop_flag = (-1 != read_32bitLE(0x08,streamFile));
|
||||
channel_count = 1;
|
||||
|
||||
if (!is_id32be(0x00,sf, "ADP!"))
|
||||
goto fail;
|
||||
|
||||
loop_flag = (-1 != read_s32le(0x08,sf));
|
||||
channels = 1;
|
||||
start_offset = 0x18;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
start_offset = 0x18;
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = read_32bitLE(0x0C,streamFile);
|
||||
vgmstream->num_samples = read_32bitLE(0x04,streamFile);
|
||||
if (loop_flag) {
|
||||
vgmstream->loop_start_sample = read_32bitLE(0x08,streamFile);
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
}
|
||||
vgmstream->sample_rate = read_s32le(0x0C,sf);
|
||||
vgmstream->num_samples = read_s32le(0x04,sf);
|
||||
vgmstream->loop_start_sample = read_s32le(0x08,sf);
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
|
||||
vgmstream->coding_type = coding_DVI_IMA_int;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_BOS_ADP;
|
||||
|
||||
// 0x10, 0x12 - both initial history?
|
||||
//vgmstream->ch[0].adpcm_history1_32 = read_16bitLE(0x10,streamFile);
|
||||
//vgmstream->ch[0].adpcm_history1_32 = read_16bitLE(0x10,sf);
|
||||
// 0x14 - initial step index?
|
||||
//vgmstream->ch[0].adpcm_step_index = read_32bitLE(0x14,streamFile);
|
||||
//vgmstream->ch[0].adpcm_step_index = read_32bitLE(0x14,sf);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
|
|
|
@ -1,43 +1,46 @@
|
|||
#include "meta.h"
|
||||
|
||||
/* ADP - from Omikron: The Nomad Soul (PC/DC) */
|
||||
VGMSTREAM * init_vgmstream_pc_adp_otns(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset, datasize;
|
||||
int loop_flag = 0, channel_count, stereo_flag;
|
||||
|
||||
if (!check_extensions(streamFile,"adp")) goto fail;
|
||||
|
||||
/* no ID, only a basic 0x10 header with filesize and nulls; do some extra checks */
|
||||
datasize = read_32bitLE(0x00,streamFile) & 0x00FFFFFF; /*24 bit*/
|
||||
if (datasize + 0x10 != streamFile->get_size(streamFile)
|
||||
|| read_32bitLE(0x04,streamFile) != 0
|
||||
|| read_32bitLE(0x08,streamFile) != 0
|
||||
|| read_32bitLE(0x0c,streamFile) != 0)
|
||||
goto fail;
|
||||
|
||||
stereo_flag = read_8bit(0x03, streamFile);
|
||||
if (stereo_flag > 1 || stereo_flag < 0) goto fail;
|
||||
channel_count = stereo_flag ? 2 : 1;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
start_offset = 0x10;
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = 22050;
|
||||
vgmstream->num_samples = channel_count== 1 ? datasize*2 : datasize;
|
||||
|
||||
vgmstream->coding_type = coding_OTNS_IMA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_OTNS_ADP;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
|
||||
/* ADP - from Omikron: The Nomad Soul (PC/DC) */
|
||||
VGMSTREAM* init_vgmstream_adp_qd(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
off_t start_offset, data_size;
|
||||
int loop_flag = 0, channels, stereo_flag;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf,"adp"))
|
||||
goto fail;
|
||||
|
||||
/* no ID, only a basic 0x10 header with filesize and nulls; do some extra checks */
|
||||
data_size = read_u32le(0x00,sf) & 0x00FFFFFF; /*24 bit*/
|
||||
if (data_size + 0x10 != sf->get_size(sf)
|
||||
|| read_u32le(0x04,sf) != 0
|
||||
|| read_u32le(0x08,sf) != 0
|
||||
|| read_u32le(0x0c,sf) != 0)
|
||||
goto fail;
|
||||
|
||||
stereo_flag = read_u8(0x03, sf);
|
||||
if (stereo_flag > 1 || stereo_flag < 0) goto fail;
|
||||
channels = stereo_flag ? 2 : 1;
|
||||
start_offset = 0x10;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_QD_ADP;
|
||||
vgmstream->sample_rate = 22050;
|
||||
vgmstream->num_samples = data_size * 2 / channels;
|
||||
|
||||
vgmstream->coding_type = coding_QD_IMA;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -1,87 +1,55 @@
|
|||
#include "meta.h"
|
||||
#include "../util.h"
|
||||
|
||||
/* STS
|
||||
|
||||
STS (found in Shikigami No Shiro 3)
|
||||
Don't confuse with ps2 .STS (EXST) format, this one is for WII
|
||||
*/
|
||||
|
||||
VGMSTREAM * init_vgmstream_wii_sts(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
char filename[PATH_LIMIT];
|
||||
|
||||
int loop_flag=0;
|
||||
int channel_count;
|
||||
int i,j;
|
||||
off_t start_offset;
|
||||
|
||||
/* check extension, case insensitive */
|
||||
streamFile->get_name(streamFile,filename,sizeof(filename));
|
||||
if (strcasecmp("sts",filename_extension(filename))) goto fail;
|
||||
|
||||
/* First bytes contain the size of the file (-4) */
|
||||
if(read_32bitBE(0x0,streamFile)!=get_streamfile_size(streamFile)-4)
|
||||
goto fail;
|
||||
|
||||
loop_flag = (read_32bitLE(0x4C,streamFile)!=0xFFFFFFFF);
|
||||
channel_count=read_8bit(0x8,streamFile)+1;
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
/* fill in the vital statistics */
|
||||
vgmstream->channels = channel_count;
|
||||
vgmstream->sample_rate = read_32bitBE(0x0A,streamFile);
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
|
||||
if(vgmstream->channels==1)
|
||||
vgmstream->num_samples = (read_32bitBE(0x0,streamFile)+4-0x70)/8*14;
|
||||
else
|
||||
vgmstream->num_samples = (read_32bitBE(0x0,streamFile)+4-0x50-0x26)/8*14/2;
|
||||
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->meta_type = meta_STS_WII;
|
||||
|
||||
if(loop_flag) {
|
||||
vgmstream->loop_start_sample=read_32bitLE(0x24,streamFile);
|
||||
vgmstream->loop_end_sample=vgmstream->num_samples;
|
||||
}
|
||||
|
||||
/* setting coef tables */
|
||||
if(vgmstream->channels==1)
|
||||
start_offset = 0x70;
|
||||
else
|
||||
start_offset = 0x50;
|
||||
|
||||
// First channel
|
||||
for(j=0;j<16;j++) {
|
||||
vgmstream->ch[0].adpcm_coef[j]=read_16bitBE(0x1E + (j*2),streamFile);
|
||||
}
|
||||
|
||||
// Second channel ?
|
||||
if(vgmstream->channels==2) {
|
||||
start_offset+=read_32bitBE(0x1a,streamFile);
|
||||
for(j=0;j<16;j++) {
|
||||
vgmstream->ch[1].adpcm_coef[j]=read_16bitBE(start_offset+(j*2),streamFile);
|
||||
}
|
||||
}
|
||||
|
||||
/* open the file for reading by each channel */
|
||||
{
|
||||
for (i=0;i<channel_count;i++) {
|
||||
vgmstream->ch[i].streamfile = streamFile->open(streamFile,filename,STREAMFILE_DEFAULT_BUFFER_SIZE);
|
||||
vgmstream->ch[i].offset = 0x50+(i*(start_offset+0x26-0x50));
|
||||
|
||||
if (!vgmstream->ch[i].streamfile) goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return vgmstream;
|
||||
|
||||
/* clean up anything we may have opened */
|
||||
fail:
|
||||
if (vgmstream) close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* .STS - from Alfa System games [Shikigami no Shiro 3 (Wii)] */
|
||||
VGMSTREAM* init_vgmstream_sts(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
uint32_t start_offset, data_size, channel_size;
|
||||
int loop_flag, channels, sample_rate;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "sts"))
|
||||
goto fail;
|
||||
|
||||
data_size = read_u32be(0x00,sf);
|
||||
if (data_size + 0x04 != get_streamfile_size(sf))
|
||||
goto fail;
|
||||
|
||||
channels = read_u8(0x08,sf) + 1;
|
||||
sample_rate = read_u16be(0x0c,sf);
|
||||
/* 0x10: dsp related? */
|
||||
/* 0x16: usable size */
|
||||
channel_size = read_u32be(0x1a,sf);
|
||||
|
||||
loop_flag = 0; //(read_s32be(0x4C,sf) != -1); /* not seen */
|
||||
|
||||
start_offset = (channels == 1) ? 0x70 : 0x50;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_STS;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
|
||||
vgmstream->num_samples = dsp_bytes_to_samples(channel_size, 1);
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
|
||||
vgmstream->coding_type = coding_NGC_DSP;
|
||||
vgmstream->layout_type = layout_interleave;
|
||||
vgmstream->interleave_block_size = channel_size + 0x2e;
|
||||
|
||||
dsp_read_coefs_be(vgmstream, sf, 0x1e, start_offset - 0x1e + channel_size);
|
||||
dsp_read_hist_be(vgmstream, sf, 0x1e + 0x24, start_offset - 0x1e + channel_size);
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
/* WXD+WXH - stream container from Relic's earlier games [Homeworld (PC), Homeworld Cataclysm (PC)] */
|
||||
VGMSTREAM* init_vgmstream_wxd_wxh(STREAMFILE* sf) {
|
||||
VGMSTREAM* vgmstream = NULL;
|
||||
STREAMFILE* sh = NULL;
|
||||
uint32_t start_offset, offset, data_size;
|
||||
int loop_flag, channels, bitrate, internal_rate;
|
||||
int total_subsongs, target_subsong = sf->stream_index;
|
||||
|
||||
|
||||
/* checks */
|
||||
if (!check_extensions(sf, "wxd"))
|
||||
goto fail;
|
||||
if (!is_id32be(0x00,sf, "WXD1"))
|
||||
goto fail;
|
||||
/* 0x04: crc? */
|
||||
|
||||
/* companion .wxh found in .big bigfiles, must extract and put together */
|
||||
sh = open_streamfile_by_ext(sf,"wxh");
|
||||
if (!sh) goto fail;
|
||||
|
||||
if (!is_id32be(0x00,sh, "WXH1"))
|
||||
goto fail;
|
||||
/* 0x04: crc? */
|
||||
|
||||
total_subsongs = read_u32le(0x08,sh);
|
||||
if (target_subsong == 0) target_subsong = 1;
|
||||
if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail;
|
||||
|
||||
offset = 0x0c + (target_subsong-1) * 0x0c;
|
||||
start_offset = read_u32le(offset + 0x00,sh);
|
||||
loop_flag = read_u16le(offset + 0x04,sh);
|
||||
bitrate = read_u16le(offset + 0x06,sh);
|
||||
/* 0x08: volume (255=max) */
|
||||
channels = read_u8 (offset + 0x09,sh);
|
||||
/* 0x0a: unused (-1) */
|
||||
internal_rate = 44100;
|
||||
|
||||
/* stream info */
|
||||
if (!is_id32be(start_offset + 0x00,sf, "DATA"))
|
||||
goto fail;
|
||||
data_size = read_u32le(start_offset + 0x04,sf);
|
||||
start_offset += 0x08;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channels, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_WXD_WXH;
|
||||
vgmstream->sample_rate = 44100;
|
||||
|
||||
vgmstream->num_samples = relic_bytes_to_samples(data_size, channels, bitrate);
|
||||
vgmstream->loop_start_sample = 0;
|
||||
vgmstream->loop_end_sample = vgmstream->num_samples;
|
||||
|
||||
vgmstream->num_streams = total_subsongs;
|
||||
vgmstream->stream_size = data_size;
|
||||
|
||||
vgmstream->codec_data = init_relic(channels, bitrate, internal_rate);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_RELIC;
|
||||
vgmstream->layout_type = layout_none;
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, sf, start_offset))
|
||||
goto fail;
|
||||
close_streamfile(sh);
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_streamfile(sh);
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
|
@ -179,7 +179,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
|||
init_vgmstream_dcs_wav,
|
||||
init_vgmstream_mul,
|
||||
init_vgmstream_thp,
|
||||
init_vgmstream_wii_sts,
|
||||
init_vgmstream_sts,
|
||||
init_vgmstream_ps2_p2bt,
|
||||
init_vgmstream_ps2_gbts,
|
||||
init_vgmstream_wii_sng,
|
||||
|
@ -275,8 +275,8 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
|||
init_vgmstream_ps2_wmus,
|
||||
init_vgmstream_hyperscan_kvag,
|
||||
init_vgmstream_ios_psnd,
|
||||
init_vgmstream_pc_adp_bos,
|
||||
init_vgmstream_pc_adp_otns,
|
||||
init_vgmstream_adp_bos,
|
||||
init_vgmstream_adp_qd,
|
||||
init_vgmstream_eb_sfx,
|
||||
init_vgmstream_eb_sf0,
|
||||
init_vgmstream_mtaf,
|
||||
|
@ -526,6 +526,8 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = {
|
|||
init_vgmstream_ogv_3rdeye,
|
||||
init_vgmstream_sspr,
|
||||
init_vgmstream_piff_tpcm,
|
||||
init_vgmstream_wxd_wxh,
|
||||
init_vgmstream_bnk_relic,
|
||||
|
||||
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
|
||||
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
|
||||
|
|
|
@ -6,13 +6,15 @@
|
|||
#define _VGMSTREAM_H
|
||||
|
||||
/* reasonable limits */
|
||||
enum { PATH_LIMIT = 32768 };
|
||||
enum { STREAM_NAME_SIZE = 255 };
|
||||
enum { VGMSTREAM_MAX_CHANNELS = 64 };
|
||||
enum { VGMSTREAM_MIN_SAMPLE_RATE = 300 }; /* 300 is Wwise min */
|
||||
enum { VGMSTREAM_MAX_SAMPLE_RATE = 192000 }; /* found in some FSB5 */
|
||||
enum { VGMSTREAM_MAX_SUBSONGS = 65535 };
|
||||
enum { VGMSTREAM_MAX_NUM_SAMPLES = 1000000000 }; /* no ~5h vgm hopefully */
|
||||
enum {
|
||||
PATH_LIMIT = 32768,
|
||||
STREAM_NAME_SIZE = 255,
|
||||
VGMSTREAM_MAX_CHANNELS = 64,
|
||||
VGMSTREAM_MIN_SAMPLE_RATE = 300, /* 300 is Wwise min */
|
||||
VGMSTREAM_MAX_SAMPLE_RATE = 192000, /* found in some FSB5 */
|
||||
VGMSTREAM_MAX_SUBSONGS = 65535, /* +20000 isn't that uncommon */
|
||||
VGMSTREAM_MAX_NUM_SAMPLES = 1000000000, /* no ~5h vgm hopefully */
|
||||
};
|
||||
|
||||
#include "streamfile.h"
|
||||
|
||||
|
@ -108,7 +110,7 @@ typedef enum {
|
|||
coding_DVI_IMA_int, /* DVI IMA ADPCM (mono/interleave, high nibble first) */
|
||||
coding_3DS_IMA, /* 3DS IMA ADPCM */
|
||||
coding_SNDS_IMA, /* Heavy Iron Studios .snds IMA ADPCM */
|
||||
coding_OTNS_IMA, /* Omikron The Nomad Soul IMA ADPCM */
|
||||
coding_QD_IMA,
|
||||
coding_WV6_IMA, /* Gorilla Systems WV6 4-bit IMA ADPCM */
|
||||
coding_ALP_IMA, /* High Voltage ALP 4-bit IMA ADPCM */
|
||||
coding_FFTA2_IMA, /* Final Fantasy Tactics A2 4-bit IMA ADPCM */
|
||||
|
@ -485,7 +487,7 @@ typedef enum {
|
|||
meta_VS, /* Men in Black .vs */
|
||||
meta_FFXI_BGW, /* FFXI (PC) BGW */
|
||||
meta_FFXI_SPW, /* FFXI (PC) SPW */
|
||||
meta_STS_WII, /* Shikigami No Shiro 3 STS Audio File */
|
||||
meta_STS,
|
||||
meta_PS2_P2BT, /* Pop'n'Music 7 Audio File */
|
||||
meta_PS2_GBTS, /* Pop'n'Music 9 Audio File */
|
||||
meta_NGC_DSP_IADP, /* Gamecube Interleave DSP */
|
||||
|
@ -560,8 +562,8 @@ typedef enum {
|
|||
meta_PS2_WMUS, /* The Warriors (PS2) */
|
||||
meta_HYPERSCAN_KVAG, /* Hyperscan KVAG/BVG */
|
||||
meta_IOS_PSND, /* Crash Bandicoot Nitro Kart 2 (iOS) */
|
||||
meta_BOS_ADP, /* ADP! (Balls of Steel, PC) */
|
||||
meta_OTNS_ADP, /* Omikron: The Nomad Soul .adp (PC/DC) */
|
||||
meta_BOS_ADP,
|
||||
meta_QD_ADP,
|
||||
meta_EB_SFX, /* Excitebots .sfx */
|
||||
meta_EB_SF0, /* Excitebots .sf0 */
|
||||
meta_MTAF,
|
||||
|
@ -745,6 +747,8 @@ typedef enum {
|
|||
meta_DSP_KWA,
|
||||
meta_OGV_3RDEYE,
|
||||
meta_PIFF_TPCM,
|
||||
meta_WXD_WXH,
|
||||
meta_BNK_RELIC,
|
||||
} meta_t;
|
||||
|
||||
/* standard WAVEFORMATEXTENSIBLE speaker positions */
|
||||
|
@ -1029,7 +1033,7 @@ typedef struct {
|
|||
} acm_codec_data;
|
||||
|
||||
|
||||
#ifdef VGM_USE_MP4V2
|
||||
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
|
||||
typedef struct {
|
||||
STREAMFILE* streamfile;
|
||||
uint64_t start;
|
||||
|
@ -1037,7 +1041,6 @@ typedef struct {
|
|||
uint64_t size;
|
||||
} mp4_streamfile;
|
||||
|
||||
#ifdef VGM_USE_FDKAAC
|
||||
typedef struct {
|
||||
mp4_streamfile if_file;
|
||||
MP4FileHandle h_mp4file;
|
||||
|
@ -1049,7 +1052,6 @@ typedef struct {
|
|||
INT_PCM sample_buffer[( (6) * (2048)*4 )];
|
||||
} mp4_aac_codec_data;
|
||||
#endif
|
||||
#endif //VGM_USE_MP4V2
|
||||
|
||||
// VGMStream description in structure format
|
||||
typedef struct {
|
||||
|
|
Loading…
Reference in New Issue