Updated VGMStream to r1050-3899-g372cd750

CQTexperiment
Christopher Snowhill 2021-08-10 17:56:02 -07:00
parent 0891a20bba
commit 4298a232fc
23 changed files with 1551 additions and 1190 deletions

View File

@ -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 */,

View File

@ -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 */

View File

@ -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;
}

View File

@ -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;
}
}
}

View File

@ -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_ */

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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 */

View File

@ -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;
}

View File

@ -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 */
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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

View File

@ -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*/

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 */

View File

@ -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 {