diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index 65679908e..1b177405e 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -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 = ""; }; 839E21DF1F2EDAF000EE54D7 /* vorbis_custom_utils_sk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_sk.c; sourceTree = ""; }; 839E21EA1F2EDB0500EE54D7 /* sk_aud.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = sk_aud.c; sourceTree = ""; }; + 839FBFF126C354CE0016A78A /* relic_decoder_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = relic_decoder_lib.h; sourceTree = ""; }; + 839FBFF526C354CE0016A78A /* relic_decoder_lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = relic_decoder_lib.c; sourceTree = ""; }; + 839FBFF826C354E60016A78A /* wxd_wxh.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = wxd_wxh.c; sourceTree = ""; }; + 839FBFF926C354E70016A78A /* mp4_faac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mp4_faac.c; sourceTree = ""; }; + 839FBFFA26C354E70016A78A /* bnk_relic.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bnk_relic.c; sourceTree = ""; }; 83A16D2722D2ADE700B90C4C /* awb.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = awb.c; sourceTree = ""; }; 83A21F7A201D895B000F04B9 /* blocked_xvag.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blocked_xvag.c; sourceTree = ""; }; 83A21F7C201D897F000F04B9 /* atx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = atx.c; sourceTree = ""; }; @@ -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 */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 2c40a2f34..ecd74e912 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -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 */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder.c index 63623e395..7b5968f18 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder.c @@ -1,72 +1,57 @@ -#include #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; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder_lib.c b/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder_lib.c new file mode 100644 index 000000000..c2f4ed47d --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder_lib.c @@ -0,0 +1,452 @@ +#include +#include +#include +#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; + } + } +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder_lib.h b/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder_lib.h new file mode 100644 index 000000000..b9fae8e4c --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/relic_decoder_lib.h @@ -0,0 +1,23 @@ +#ifndef _RELIC_DECODER_LIB_H_ +#define _RELIC_DECODER_LIB_H_ + +#include + +#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_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/decode.c b/Frameworks/vgmstream/vgmstream/src/decode.c index 9038086d9..c632aff23 100644 --- a/Frameworks/vgmstream/vgmstream/src/decode.c +++ b/Frameworks/vgmstream/vgmstream/src/decode.c @@ -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); diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 094cf344f..364807695 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -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) { diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_sns.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_sns.c index ba7a576be..9f9fbec58 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_sns.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_ea_sns.c @@ -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); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/akb.c b/Frameworks/vgmstream/vgmstream/src/meta/akb.c index e46471421..e320e91b9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/akb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/akb.c @@ -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 */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bnk_relic.c b/Frameworks/vgmstream/vgmstream/src/meta/bnk_relic.c new file mode 100644 index 000000000..38f176ab7 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/bnk_relic.c @@ -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; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c index 1abeca016..4a7fb3f0b 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_1snh.c @@ -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 */ +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c index 814a4473a..8aae825a5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ea_eaac.c @@ -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; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fda.c b/Frameworks/vgmstream/vgmstream/src/meta/fda.c index 57c9a0e19..36b671f3a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fda.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fda.c @@ -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; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca.c b/Frameworks/vgmstream/vgmstream/src/meta/hca.c index 26db6871b..75a172d53 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca.c @@ -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 +//#include + +/* 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 diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 6f0865220..9b31037f2 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -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*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mp4.c b/Frameworks/vgmstream/vgmstream/src/meta/mp4.c index b1850f2e8..288d80a61 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mp4.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mp4.c @@ -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; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mp4_faac.c b/Frameworks/vgmstream/vgmstream/src/meta/mp4_faac.c new file mode 100644 index 000000000..379b42321 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/mp4_faac.c @@ -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 diff --git a/Frameworks/vgmstream/vgmstream/src/meta/pc_adp.c b/Frameworks/vgmstream/vgmstream/src/meta/pc_adp.c index 4ff7c07ce..de7a56349 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/pc_adp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/pc_adp.c @@ -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; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/pc_adp_otns.c b/Frameworks/vgmstream/vgmstream/src/meta/pc_adp_otns.c index 6efc43d58..86d92e38c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/pc_adp_otns.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/pc_adp_otns.c @@ -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; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wii_sts.c b/Frameworks/vgmstream/vgmstream/src/meta/wii_sts.c index 328ddf8a9..b8f245f93 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wii_sts.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wii_sts.c @@ -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;ich[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; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wxd_wxh.c b/Frameworks/vgmstream/vgmstream/src/meta/wxd_wxh.c new file mode 100644 index 000000000..12a178c16 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/wxd_wxh.c @@ -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; +} diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 3153c49c4..85f5aba8b 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -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 */ diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 62e86f6d5..d76edf31a 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -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 {