mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-15 02:57:38 +01:00
commit
1c68e8cb7b
@ -162,6 +162,7 @@ a companion file:
|
||||
- .adx: .adxkey (derived 6 byte key, in start/mult/add format)
|
||||
- .ahx: .ahxkey (derived 6 byte key, in start/mult/add format)
|
||||
- .hca: .hcakey (8 byte decryption key, a 64-bit number)
|
||||
- May be followed by 2 byte AWB derivation value for newer HCA
|
||||
- .fsb: .fsbkey (decryption key, in hex)
|
||||
|
||||
The key file can be ".(ext)key" (for the whole folder), or "(name).(ext)key"
|
||||
@ -240,7 +241,7 @@ are used in few games.
|
||||
- AAC
|
||||
- Bink
|
||||
- AC3/SPDIF
|
||||
- Xiph Opus (Ogg, Switch)
|
||||
- Xiph Opus (Ogg, Switch, EA, UE4)
|
||||
- Xiph CELT (FSB)
|
||||
- Musepack
|
||||
- FLAC
|
||||
|
@ -27,7 +27,7 @@ void clHCA_delete(clHCA *);
|
||||
* the header length with clHCA_isOurFile, then read data and call this.
|
||||
* May be called multiple times to reset decoder state.
|
||||
* Returns 0 on success, <0 on failure. */
|
||||
int clHCA_DecodeHeader(clHCA *, void *data, unsigned int size);
|
||||
int clHCA_DecodeHeader(clHCA *, const void *data, unsigned int size);
|
||||
|
||||
typedef struct clHCA_stInfo {
|
||||
unsigned int version;
|
||||
@ -61,6 +61,7 @@ int clHCA_getInfo(clHCA *, clHCA_stInfo *out);
|
||||
|
||||
/* Decodes a single frame, from data after headerSize. Should be called after
|
||||
* clHCA_DecodeHeader and size must be at least blockSize long.
|
||||
* Data may be modified if encrypted.
|
||||
* Returns 0 on success, <0 on failure. */
|
||||
int clHCA_DecodeBlock(clHCA *, void *data, unsigned int size);
|
||||
|
||||
@ -81,6 +82,10 @@ void clHCA_SetKey(clHCA *, unsigned long long keycode);
|
||||
* and select the key with scores closer to 1. */
|
||||
int clHCA_TestBlock(clHCA *hca, void *data, unsigned int size);
|
||||
|
||||
/* Resets the internal decode state, used when restarting to decode the file from the beginning.
|
||||
* Without it there are minor differences, mainly useful when testing a new key. */
|
||||
void clHCA_DecodeReset(clHCA * hca);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
172
ext_libs/clHCA.c
172
ext_libs/clHCA.c
@ -26,7 +26,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <memory.h>
|
||||
|
||||
#define HCA_MASK 0x7F7F7F7F /* chunk obfuscation when the HCA is encrypted (ciph type > 0) */
|
||||
#define HCA_MASK 0x7F7F7F7F /* chunk obfuscation when the HCA is encrypted with key */
|
||||
#define HCA_SUBFRAMES_PER_FRAME 8
|
||||
#define HCA_SAMPLES_PER_SUBFRAME 128
|
||||
#define HCA_SAMPLES_PER_FRAME (HCA_SUBFRAMES_PER_FRAME*HCA_SAMPLES_PER_SUBFRAME)
|
||||
@ -34,6 +34,13 @@
|
||||
|
||||
#define HCA_MAX_CHANNELS 16 /* internal max? in practice only 8 can be encoded */
|
||||
|
||||
#define HCA_ERROR_PARAMS -1
|
||||
#define HCA_ERROR_HEADER -2
|
||||
#define HCA_ERROR_CHECKSUM -3
|
||||
#define HCA_ERROR_SYNC -4
|
||||
#define HCA_ERROR_UNPACK -5
|
||||
#define HCA_ERROR_BITREADER -6
|
||||
|
||||
//--------------------------------------------------
|
||||
// Decoder config/state
|
||||
//--------------------------------------------------
|
||||
@ -239,7 +246,7 @@ int clHCA_isOurFile(const void *data, unsigned int size) {
|
||||
unsigned int header_size = 0;
|
||||
|
||||
if (!data || size < 0x08)
|
||||
return -1;
|
||||
return HCA_ERROR_PARAMS;
|
||||
|
||||
bitreader_init(&br, data, 8);
|
||||
if ((bitreader_peek(&br, 32) & HCA_MASK) == 0x48434100) {/*'HCA\0'*/
|
||||
@ -248,13 +255,13 @@ int clHCA_isOurFile(const void *data, unsigned int size) {
|
||||
}
|
||||
|
||||
if (header_size == 0)
|
||||
return -1;
|
||||
return HCA_ERROR_HEADER;
|
||||
return header_size;
|
||||
}
|
||||
|
||||
int clHCA_getInfo(clHCA *hca, clHCA_stInfo *info) {
|
||||
if (!hca || !info || !hca->is_valid)
|
||||
return -1;
|
||||
return HCA_ERROR_PARAMS;
|
||||
|
||||
info->version = hca->version;
|
||||
info->headerSize = hca->header_size;
|
||||
@ -286,10 +293,10 @@ void clHCA_ReadSamples16(clHCA *hca, signed short *samples) {
|
||||
for (k = 0; k < hca->channels; k++) {
|
||||
f = hca->channel[k].wave[i][j];
|
||||
//f = f * hca->rva_volume; /* rare, won't apply for now */
|
||||
if (f > 1) {
|
||||
f = 1;
|
||||
} else if (f < -1) {
|
||||
f = -1;
|
||||
if (f > 1.0f) {
|
||||
f = 1.0f;
|
||||
} else if (f < -1.0f) {
|
||||
f = -1.0f;
|
||||
}
|
||||
s = (signed int) (f * scale);
|
||||
if ((unsigned) (s + 0x8000) & 0xFFFF0000)
|
||||
@ -424,7 +431,7 @@ static int ath_init(unsigned char *ath_curve, int type, unsigned int sample_rate
|
||||
ath_init1(ath_curve, sample_rate);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
return HCA_ERROR_HEADER;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -455,7 +462,7 @@ static void cipher_init1(unsigned char *cipher_table) {
|
||||
const int add = 11;
|
||||
unsigned int i, v = 0;
|
||||
|
||||
/* keyless encryption (unused?) */
|
||||
/* keyless encryption (rare) */
|
||||
for (i = 1; i < 256 - 1; i++) {
|
||||
v = (v * mul + add) & 0xFF;
|
||||
if (v == 0 || v == 0xFF)
|
||||
@ -542,7 +549,7 @@ static void cipher_init56(unsigned char *cipher_table, unsigned long long keycod
|
||||
}
|
||||
|
||||
static int cipher_init(unsigned char *cipher_table, int type, unsigned long long keycode) {
|
||||
if (!(keycode))
|
||||
if (type == 56 && !(keycode))
|
||||
type = 0;
|
||||
|
||||
switch (type) {
|
||||
@ -556,7 +563,7 @@ static int cipher_init(unsigned char *cipher_table, int type, unsigned long long
|
||||
cipher_init56(cipher_table, keycode);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
return HCA_ERROR_HEADER;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -568,16 +575,17 @@ static unsigned int header_ceil2(unsigned int a, unsigned int b) {
|
||||
return (b > 0) ? (a / b + ((a % b) ? 1 : 0)) : 0;
|
||||
}
|
||||
|
||||
int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
|
||||
int clHCA_DecodeHeader(clHCA *hca, const void *data, unsigned int size) {
|
||||
clData br;
|
||||
int res;
|
||||
|
||||
if (!hca || !data )
|
||||
return -1;
|
||||
if (!hca || !data)
|
||||
return HCA_ERROR_PARAMS;
|
||||
|
||||
hca->is_valid = 0;
|
||||
|
||||
if (size < 0x08)
|
||||
return -1;
|
||||
return HCA_ERROR_PARAMS;
|
||||
|
||||
bitreader_init(&br, data, size);
|
||||
|
||||
@ -594,18 +602,18 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
|
||||
hca->version != 0x0102 &&
|
||||
hca->version != 0x0103 &&
|
||||
hca->version != 0x0200)
|
||||
return -1;
|
||||
return HCA_ERROR_HEADER;
|
||||
#endif
|
||||
if (size < hca->header_size)
|
||||
return -1;
|
||||
return HCA_ERROR_PARAMS;
|
||||
|
||||
if (crc16_checksum(data,hca->header_size))
|
||||
return -1;
|
||||
return HCA_ERROR_CHECKSUM;
|
||||
|
||||
size -= 0x08;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
return HCA_ERROR_HEADER;
|
||||
}
|
||||
|
||||
/* format info */
|
||||
@ -618,18 +626,18 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
|
||||
hca->encoder_padding = bitreader_read(&br, 16);
|
||||
|
||||
if (!(hca->channels >= 1 && hca->channels <= HCA_MAX_CHANNELS))
|
||||
return -1;
|
||||
return HCA_ERROR_HEADER;
|
||||
|
||||
if (hca->frame_count == 0)
|
||||
return -1;
|
||||
return HCA_ERROR_HEADER;
|
||||
|
||||
if (!(hca->sample_rate >= 1 && hca->sample_rate <= 0x7FFFFF)) /* encoder max seems 48000 */
|
||||
return -1;
|
||||
return HCA_ERROR_HEADER;
|
||||
|
||||
size -= 0x10;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
return HCA_ERROR_HEADER;
|
||||
}
|
||||
|
||||
/* compression (v2.0) or decode (v1.x) info */
|
||||
@ -668,7 +676,7 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
|
||||
size -= 0x0c;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
return HCA_ERROR_HEADER;
|
||||
}
|
||||
|
||||
/* VBR (variable bit rate) info */
|
||||
@ -678,7 +686,7 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
|
||||
hca->vbr_noise_Level = bitreader_read(&br, 16);
|
||||
|
||||
if (!(hca->frame_size == 0 && hca->vbr_max_frame_size > 8 && hca->vbr_max_frame_size <= 0x1FF))
|
||||
return -1;
|
||||
return HCA_ERROR_HEADER;
|
||||
|
||||
size -= 0x08;
|
||||
}
|
||||
@ -710,7 +718,7 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
|
||||
|
||||
if (!(hca->loop_start_frame >= 0 && hca->loop_start_frame <= hca->loop_end_frame
|
||||
&& hca->loop_end_frame < hca->frame_count))
|
||||
return -1;
|
||||
return HCA_ERROR_HEADER;
|
||||
|
||||
size -= 0x10;
|
||||
}
|
||||
@ -729,7 +737,7 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
|
||||
hca->ciph_type = bitreader_read(&br, 16);
|
||||
|
||||
if (!(hca->ciph_type == 0 || hca->ciph_type == 1 || hca->ciph_type == 56))
|
||||
return -1;
|
||||
return HCA_ERROR_HEADER;
|
||||
|
||||
size -= 0x06;
|
||||
}
|
||||
@ -760,11 +768,11 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
|
||||
hca->comment_len = bitreader_read(&br, 8);
|
||||
|
||||
if (hca->comment_len > size)
|
||||
return -1;
|
||||
return HCA_ERROR_HEADER;
|
||||
|
||||
temp = realloc(hca->comment, hca->comment_len + 1);
|
||||
if (!temp)
|
||||
return -1;
|
||||
return HCA_ERROR_HEADER;
|
||||
hca->comment = temp;
|
||||
for (i = 0; i < hca->comment_len; ++i)
|
||||
hca->comment[i] = bitreader_read(&br, 8);
|
||||
@ -782,17 +790,17 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
|
||||
size -= (size - 0x02); /* fills up to header_size, sans checksum */
|
||||
}
|
||||
|
||||
/* should be fully read, but allow data buffer may be bigger than header_size */
|
||||
/* should be fully read, but allow as data buffer may be bigger than header_size */
|
||||
//if (size != 0x02)
|
||||
// return -1;
|
||||
// return HCA_ERROR_HEADER;
|
||||
|
||||
|
||||
/* extra validations */
|
||||
if (!(hca->frame_size >= 0x08 && hca->frame_size <= 0xFFFF)) /* actual max seems 0x155*channels */
|
||||
return -1; /* theoretically can be 0 if VBR (not seen) */
|
||||
return HCA_ERROR_HEADER; /* theoretically can be 0 if VBR (not seen) */
|
||||
|
||||
if (!(hca->min_resolution == 1 && hca->max_resolution == 15))
|
||||
return -1;
|
||||
return HCA_ERROR_HEADER;
|
||||
|
||||
|
||||
/* inits state */
|
||||
@ -803,10 +811,12 @@ int clHCA_DecodeHeader(clHCA *hca, void *data, unsigned int size) {
|
||||
hca->total_band_count - hca->base_band_count - hca->stereo_band_count,
|
||||
hca->bands_per_hfr_group);
|
||||
|
||||
if (ath_init(hca->ath_curve, hca->ath_type, hca->sample_rate) < 0)
|
||||
return -1;
|
||||
if (cipher_init(hca->cipher_table, hca->ciph_type, hca->keycode) < 0)
|
||||
return -1;
|
||||
res = ath_init(hca->ath_curve, hca->ath_type, hca->sample_rate);
|
||||
if (res < 0)
|
||||
return res;
|
||||
res = cipher_init(hca->cipher_table, hca->ciph_type, hca->keycode);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
|
||||
/* init channels */
|
||||
@ -919,43 +929,62 @@ int clHCA_TestBlock(clHCA *hca, void *data, unsigned int size) {
|
||||
unsigned int ch, sf, s;
|
||||
int status;
|
||||
int clips = 0, blanks = 0;
|
||||
float fsample;
|
||||
signed int psample;
|
||||
|
||||
/* return if decode fails (happens sometimes with wrong keys) */
|
||||
|
||||
/* first blocks can be empty/silent, check all bytes but sync/crc */
|
||||
{
|
||||
int i;
|
||||
int is_empty = 1;
|
||||
const unsigned char *buf = data;
|
||||
|
||||
for (i = 2; i < size - 0x02; i++) {
|
||||
if (buf[i] != 0) {
|
||||
is_empty = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_empty) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* return if decode fails (happens often with wrong keys due to bad bitstream values) */
|
||||
status = clHCA_DecodeBlock(hca, data, size);
|
||||
if (status < 0)
|
||||
return -1;
|
||||
|
||||
/* check decode results */
|
||||
/* check decode results as bad keys may still get here */
|
||||
for (ch = 0; ch < hca->channels; ch++) {
|
||||
for (sf = 0; sf < HCA_SUBFRAMES_PER_FRAME; sf++) {
|
||||
for (s = 0; s < HCA_SAMPLES_PER_SUBFRAME; s++) {
|
||||
fsample = hca->channel[ch].wave[sf][s];
|
||||
psample = (signed int) (fsample * scale);
|
||||
if (fsample > 1.0f || fsample < -1.0f)
|
||||
float fsample = hca->channel[ch].wave[sf][s];
|
||||
|
||||
if (fsample > 1.0f || fsample < -1.0f) { //improve?
|
||||
clips++;
|
||||
else if (psample == 0 || psample == -1)
|
||||
blanks++;
|
||||
}
|
||||
else {
|
||||
signed int psample = (signed int) (fsample * scale);
|
||||
if (psample == 0 || psample == -1)
|
||||
blanks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* the more clips the less likely block was correctly decrypted */
|
||||
if (clips > 0)
|
||||
if (clips == 1)
|
||||
clips++;
|
||||
if (clips > 1)
|
||||
return clips;
|
||||
/* if block is silent result is not useful */
|
||||
if (blanks == hca->channels * HCA_SUBFRAMES_PER_FRAME * HCA_SAMPLES_PER_SUBFRAME)
|
||||
return 0;
|
||||
|
||||
/* block may be correct (but wrong keys can get this too) */
|
||||
/* block may be correct (but wrong keys can get this too and should test more blocks) */
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
// it'd seem like resetting IMDCT (others get overwritten) would matter when restarting the
|
||||
// stream from 0, but doesn't seem any different, maybe because the first frame acts as setup/empty
|
||||
void clHCA_DecodeReset(clHCA * hca) {
|
||||
unsigned int i;
|
||||
|
||||
@ -965,18 +994,18 @@ void clHCA_DecodeReset(clHCA * hca) {
|
||||
for (i = 0; i < hca->channels; i++) {
|
||||
stChannel *ch = &hca->channel[i];
|
||||
|
||||
memset(ch->intensity, 0, sizeof(ch->intensity[0]) * HCA_SUBFRAMES_PER_FRAME);
|
||||
memset(ch->scalefactors, 0, sizeof(ch->scalefactors[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
memset(ch->resolution, 0, sizeof(ch->resolution[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
memset(ch->gain, 0, sizeof(ch->gain[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
memset(ch->spectra, 0, sizeof(ch->spectra[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
memset(ch->temp, 0, sizeof(ch->temp[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
memset(ch->dct, 0, sizeof(ch->dct[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
/* most values get overwritten during decode */
|
||||
//memset(ch->intensity, 0, sizeof(ch->intensity[0]) * HCA_SUBFRAMES_PER_FRAME);
|
||||
//memset(ch->scalefactors, 0, sizeof(ch->scalefactors[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
//memset(ch->resolution, 0, sizeof(ch->resolution[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
//memset(ch->gain, 0, sizeof(ch->gain[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
//memset(ch->spectra, 0, sizeof(ch->spectra[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
//memset(ch->temp, 0, sizeof(ch->temp[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
//memset(ch->dct, 0, sizeof(ch->dct[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
memset(ch->imdct_previous, 0, sizeof(ch->imdct_previous[0]) * HCA_SAMPLES_PER_SUBFRAME);
|
||||
memset(ch->wave, 0, sizeof(ch->wave[0][0]) * HCA_SUBFRAMES_PER_FRAME * HCA_SUBFRAMES_PER_FRAME);
|
||||
//memset(ch->wave, 0, sizeof(ch->wave[0][0]) * HCA_SUBFRAMES_PER_FRAME * HCA_SUBFRAMES_PER_FRAME);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------
|
||||
// Decode
|
||||
@ -1002,19 +1031,19 @@ int clHCA_DecodeBlock(clHCA *hca, void *data, unsigned int size) {
|
||||
unsigned int subframe, ch;
|
||||
|
||||
if (!data || !hca || !hca->is_valid)
|
||||
return -1;
|
||||
return HCA_ERROR_PARAMS;
|
||||
if (size < hca->frame_size)
|
||||
return -1;
|
||||
return HCA_ERROR_PARAMS;
|
||||
|
||||
bitreader_init(&br, data, hca->frame_size);
|
||||
|
||||
/* test sync (not encrypted) */
|
||||
sync = bitreader_read(&br, 16);
|
||||
if (sync != 0xFFFF)
|
||||
return -1;
|
||||
return HCA_ERROR_SYNC;
|
||||
|
||||
if (crc16_checksum(data, hca->frame_size))
|
||||
return -1;
|
||||
return HCA_ERROR_CHECKSUM;
|
||||
|
||||
cipher_decrypt(hca->cipher_table, data, hca->frame_size);
|
||||
|
||||
@ -1029,7 +1058,7 @@ int clHCA_DecodeBlock(clHCA *hca, void *data, unsigned int size) {
|
||||
int unpack = decode1_unpack_channel(&hca->channel[ch], &br,
|
||||
hca->hfr_group_count, packed_noise_level, hca->ath_curve);
|
||||
if (unpack < 0)
|
||||
return -1;
|
||||
return unpack;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1060,9 +1089,8 @@ int clHCA_DecodeBlock(clHCA *hca, void *data, unsigned int size) {
|
||||
}
|
||||
|
||||
/* should read all frame sans checksum at most */
|
||||
if (br.bit > br.size - 16) {
|
||||
return -1;
|
||||
}
|
||||
if (br.bit > br.size - 16)
|
||||
return HCA_ERROR_BITREADER;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1132,7 +1160,7 @@ static int decode1_unpack_channel(stChannel *ch, clData *br,
|
||||
/* may happen with bad keycodes, scalefactors must be 6b indexes */
|
||||
int scalefactor_test = (int)scalefactor_prev + ((int)delta - (int)extra_delta);
|
||||
if (scalefactor_test < 0 || scalefactor_test > 64) {
|
||||
return -1;
|
||||
return HCA_ERROR_UNPACK;
|
||||
}
|
||||
|
||||
scalefactor_prev += delta - extra_delta;
|
||||
@ -1160,7 +1188,7 @@ static int decode1_unpack_channel(stChannel *ch, clData *br,
|
||||
}
|
||||
/* 15 may be an invalid value? */
|
||||
//else {
|
||||
// return -1;
|
||||
// return HCA_ERROR_INSENSITY;
|
||||
//}
|
||||
}
|
||||
else {
|
||||
|
@ -281,12 +281,14 @@ void ffmpeg_set_skip_samples(ffmpeg_codec_data * data, int skip_samples);
|
||||
ffmpeg_codec_data * init_ffmpeg_switch_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
|
||||
ffmpeg_codec_data * init_ffmpeg_ue4_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
|
||||
ffmpeg_codec_data * init_ffmpeg_ea_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
|
||||
ffmpeg_codec_data * init_ffmpeg_x_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate);
|
||||
|
||||
size_t switch_opus_get_samples(off_t offset, size_t data_size, STREAMFILE *streamFile);
|
||||
|
||||
size_t switch_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile);
|
||||
size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile);
|
||||
size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile);
|
||||
size_t x_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -16,8 +16,9 @@
|
||||
static size_t make_oggs_first(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate);
|
||||
static size_t make_oggs_page(uint8_t * buf, int buf_size, size_t data_size, int page_sequence, int granule);
|
||||
static size_t opus_get_packet_samples(const uint8_t * buf, int len);
|
||||
static size_t get_xopus_packet_size(int packet, STREAMFILE * streamfile);
|
||||
|
||||
typedef enum { OPUS_SWITCH, OPUS_UE4, OPUS_EA } opus_type_t;
|
||||
typedef enum { OPUS_SWITCH, OPUS_UE4, OPUS_EA, OPUS_X } opus_type_t;
|
||||
typedef struct {
|
||||
/* config */
|
||||
opus_type_t type;
|
||||
@ -104,6 +105,10 @@ static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
|
||||
data_size = (uint16_t)read_16bitBE(data->physical_offset, streamfile);
|
||||
skip_size = 0x02;
|
||||
break;
|
||||
case OPUS_X:
|
||||
data_size = get_xopus_packet_size(data->sequence - 2, streamfile);
|
||||
skip_size = 0;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -162,6 +167,7 @@ static size_t opus_io_read(STREAMFILE *streamfile, uint8_t *dest, off_t offset,
|
||||
static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) {
|
||||
off_t physical_offset, max_physical_offset;
|
||||
size_t logical_size = 0;
|
||||
int packet = 0;
|
||||
|
||||
if (data->logical_size)
|
||||
return data->logical_size;
|
||||
@ -192,6 +198,10 @@ static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) {
|
||||
data_size = (uint16_t)read_16bitBE(physical_offset, streamfile);
|
||||
skip_size = 0x02;
|
||||
break;
|
||||
case OPUS_X:
|
||||
data_size = get_xopus_packet_size(packet, streamfile);
|
||||
skip_size = 0x00;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -199,6 +209,7 @@ static size_t opus_io_size(STREAMFILE *streamfile, opus_io_data* data) {
|
||||
|
||||
physical_offset += data_size + skip_size;
|
||||
logical_size += oggs_size + data_size;
|
||||
packet++;
|
||||
}
|
||||
|
||||
/* logical size can be bigger though */
|
||||
@ -390,6 +401,24 @@ fail:
|
||||
|
||||
static size_t make_opus_header(uint8_t * buf, int buf_size, int channels, int skip, int sample_rate) {
|
||||
size_t header_size = 0x13;
|
||||
int mapping_family = 0; /* channel config: 0=standard (single stream mono/stereo), 1=vorbis, 255: not defined */
|
||||
#if 0
|
||||
int stream_count = 1; /* number of internal mono/stereo streams (N mono/stereo streams form M channels) */
|
||||
int coupled_count = channels - 1; /* number of stereo streams (packet has which one is mono or stereo) */
|
||||
|
||||
/* special multichannel config */
|
||||
if (channels > 2) {
|
||||
header_size += 0x01+0x01+channels;
|
||||
switch(type) {
|
||||
|
||||
case ...:
|
||||
...
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (header_size > buf_size) {
|
||||
VGM_LOG("OPUS: buffer can't hold header\n");
|
||||
@ -403,7 +432,19 @@ static size_t make_opus_header(uint8_t * buf, int buf_size, int channels, int sk
|
||||
put_16bitLE(buf+0x0A, skip);
|
||||
put_32bitLE(buf+0x0c, sample_rate);
|
||||
put_16bitLE(buf+0x10, 0); /* output gain */
|
||||
put_8bit (buf+0x12, 0); /* channel mapping family */
|
||||
put_8bit (buf+0x12, mapping_family);
|
||||
|
||||
#if 0
|
||||
if (mapping_family > 0) {
|
||||
int 0;
|
||||
|
||||
put_8bit(buf+0x13, stream_count);
|
||||
put_8bit(buf+0x14, coupled_count);
|
||||
for (i = 0; i < channels; i++) {
|
||||
put_8bit(buf+0x15+i, i); /* channel mapping (may need to change per family) */
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return header_size;
|
||||
fail:
|
||||
@ -471,6 +512,7 @@ static size_t opus_get_packet_samples(const uint8_t * buf, int len) {
|
||||
static size_t custom_opus_get_samples(off_t offset, size_t data_size, STREAMFILE *streamFile, opus_type_t type) {
|
||||
size_t num_samples = 0;
|
||||
off_t end_offset = offset + data_size;
|
||||
int packet = 0;
|
||||
|
||||
if (end_offset > get_streamfile_size(streamFile)) {
|
||||
VGM_LOG("OPUS: wrong end offset found\n");
|
||||
@ -495,6 +537,10 @@ static size_t custom_opus_get_samples(off_t offset, size_t data_size, STREAMFILE
|
||||
data_size = (uint16_t)read_16bitBE(offset, streamFile);
|
||||
skip_size = 0x02;
|
||||
break;
|
||||
case OPUS_X:
|
||||
data_size = get_xopus_packet_size(packet, streamFile);
|
||||
skip_size = 0x00;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -503,6 +549,7 @@ static size_t custom_opus_get_samples(off_t offset, size_t data_size, STREAMFILE
|
||||
num_samples += opus_get_packet_samples(buf, 0x04);
|
||||
|
||||
offset += skip_size + data_size;
|
||||
packet++;
|
||||
}
|
||||
|
||||
return num_samples;
|
||||
@ -527,6 +574,9 @@ static size_t custom_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile
|
||||
case OPUS_EA:
|
||||
skip_size = 0x02;
|
||||
break;
|
||||
case OPUS_X:
|
||||
skip_size = 0x00;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -544,8 +594,21 @@ size_t ue4_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) {
|
||||
size_t ea_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) {
|
||||
return custom_opus_get_encoder_delay(offset, streamFile, OPUS_EA);
|
||||
}
|
||||
size_t x_opus_get_encoder_delay(off_t offset, STREAMFILE *streamFile) {
|
||||
return custom_opus_get_encoder_delay(offset, streamFile, OPUS_X);
|
||||
}
|
||||
|
||||
|
||||
static size_t get_xopus_packet_size(int packet, STREAMFILE * streamfile) {
|
||||
/* XOPUS has a packet size table at the beginning, get size from there.
|
||||
* Maybe should copy the table during setup to avoid IO, but all XOPUS are
|
||||
* quite small so it isn't very noticeable. */
|
||||
return (uint16_t)read_16bitLE(0x20 + packet*0x02, streamfile);
|
||||
}
|
||||
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static ffmpeg_codec_data * init_ffmpeg_custom_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate, opus_type_t type) {
|
||||
ffmpeg_codec_data * ffmpeg_data = NULL;
|
||||
STREAMFILE *temp_streamFile = NULL;
|
||||
@ -577,5 +640,8 @@ ffmpeg_codec_data * init_ffmpeg_ue4_opus(STREAMFILE *streamFile, off_t start_off
|
||||
ffmpeg_codec_data * init_ffmpeg_ea_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) {
|
||||
return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_EA);
|
||||
}
|
||||
ffmpeg_codec_data * init_ffmpeg_x_opus(STREAMFILE *streamFile, off_t start_offset, size_t data_size, int channels, int skip, int sample_rate) {
|
||||
return init_ffmpeg_custom_opus(streamFile, start_offset, data_size, channels, skip, sample_rate, OPUS_X);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -123,6 +123,7 @@ void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do) {
|
||||
void reset_hca(hca_codec_data * data) {
|
||||
if (!data) return;
|
||||
|
||||
clHCA_DecodeReset(data->handle);
|
||||
data->current_block = 0;
|
||||
data->samples_filled = 0;
|
||||
data->samples_consumed = 0;
|
||||
@ -152,15 +153,18 @@ void free_hca(hca_codec_data * data) {
|
||||
|
||||
#define HCA_KEY_MAX_BLANK_FRAMES 15 /* ignored up to N blank frames (not uncommon to have ~10, if more something is off) */
|
||||
#define HCA_KEY_MAX_TEST_FRAMES 10 /* 5~15 should be enough, but mostly silent or badly mastered files may need more */
|
||||
#define HCA_KEY_MAX_ACCEPTABLE_SCORE 300 /* unlikely to work correctly, 10~30 may be ok */
|
||||
#define HCA_KEY_MAX_ACCEPTABLE_SCORE 150 /* more is unlikely to work correctly, 10~30 isn't uncommon */
|
||||
|
||||
/* Test a number of frames if key decrypts correctly.
|
||||
* Returns score: <0: error/wrong, 0: unknown/silent file, >0: good (the closest to 1 the better) */
|
||||
* Returns score: <0: error/wrong, 0: unknown/silent file, >0: good (the closest to 1 the better). */
|
||||
int test_hca_key(hca_codec_data * data, unsigned long long keycode) {
|
||||
size_t test_frame = 0, current_frame = 0, blank_frames = 0;
|
||||
int total_score = 0;
|
||||
const unsigned int blockSize = data->info.blockSize;
|
||||
|
||||
/* Due to the potentially large number of keys this must be tuned for speed.
|
||||
* Buffered IO seems fast enough (not very different reading a large block once vs frame by frame).
|
||||
* clHCA_TestBlock could be optimized a bit more. */
|
||||
|
||||
clHCA_SetKey(data->handle, keycode);
|
||||
|
||||
@ -204,5 +208,6 @@ int test_hca_key(hca_codec_data * data, unsigned long long keycode) {
|
||||
total_score = 1;
|
||||
}
|
||||
|
||||
clHCA_DecodeReset(data->handle);
|
||||
return total_score;
|
||||
}
|
||||
|
@ -459,6 +459,7 @@ static const char* extension_list[] = {
|
||||
"xwav",//fake, to be removed
|
||||
"xwb",
|
||||
"xmd",
|
||||
"xopus",
|
||||
"xwc",
|
||||
"xwm",
|
||||
"xwma",
|
||||
@ -1094,6 +1095,7 @@ static const meta_info meta_info_list[] = {
|
||||
{meta_UE4OPUS, "Epic Games UE4OPUS header"},
|
||||
{meta_XWMA, "Microsoft XWMA RIFF header"},
|
||||
{meta_VA3, "Konami / Sony ATRAC3 Header" },
|
||||
{meta_XOPUS, "Rovio XOPUS header"},
|
||||
|
||||
};
|
||||
|
||||
|
@ -204,6 +204,10 @@
|
||||
RelativePath=".\meta\hca_keys.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\hca_keys_awb.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\fsb_keys.h"
|
||||
>
|
||||
@ -1589,6 +1593,10 @@
|
||||
<File
|
||||
RelativePath=".\meta\xnb.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\xopus.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\meta\xss.c"
|
||||
|
@ -107,9 +107,11 @@
|
||||
<ClInclude Include="meta\ps2_psh_streamfile.h" />
|
||||
<ClInclude Include="meta\opus_interleave_streamfile.h" />
|
||||
<ClInclude Include="meta\sqex_scd_streamfile.h" />
|
||||
<ClInclude Include="meta\xvag_streamfile.h" />
|
||||
<ClInclude Include="meta\sqex_scd_streamfile.h" />
|
||||
<ClInclude Include="meta\ubi_lyn_ogg_streamfile.h" />
|
||||
<ClInclude Include="meta\meta.h" />
|
||||
<ClInclude Include="meta\hca_keys.h" />
|
||||
<ClInclude Include="meta\hca_keys_awb.h" />
|
||||
<ClInclude Include="meta\fsb_keys.h" />
|
||||
<ClInclude Include="coding\acm_decoder_libacm.h" />
|
||||
<ClInclude Include="coding\coding.h" />
|
||||
@ -470,6 +472,7 @@
|
||||
<ClCompile Include="meta\x360_pasx.c" />
|
||||
<ClCompile Include="meta\xma.c" />
|
||||
<ClCompile Include="meta\xnb.c" />
|
||||
<ClCompile Include="meta\xopus.c" />
|
||||
<ClCompile Include="meta\xss.c" />
|
||||
<ClCompile Include="meta\xvag.c" />
|
||||
<ClCompile Include="meta\xmd.c" />
|
||||
|
@ -113,6 +113,9 @@
|
||||
<ClInclude Include="meta\hca_keys.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\hca_keys_awb.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="meta\fsb_keys.h">
|
||||
<Filter>meta\Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -1306,7 +1309,7 @@
|
||||
<ClCompile Include="meta\sqex_scd.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\sqex_scd_sscfs.c">
|
||||
<ClCompile Include="meta\sqex_scd_sscf.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\sqex_sead.c">
|
||||
@ -1432,6 +1435,9 @@
|
||||
<ClCompile Include="meta\xnb.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\xopus.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="meta\x360_cxs.c">
|
||||
<Filter>meta\Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
@ -21,10 +21,19 @@ VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) {
|
||||
|
||||
/* find decryption key in external file or preloaded list */
|
||||
if (hca_data->info.encryptionEnabled) {
|
||||
uint8_t keybuf[8];
|
||||
if (read_key_file(keybuf, 8, streamFile) == 8) {
|
||||
uint8_t keybuf[0x08+0x02];
|
||||
size_t keysize;
|
||||
|
||||
keysize = read_key_file(keybuf, 0x08+0x04, streamFile);
|
||||
if (keysize == 0x08) { /* standard */
|
||||
keycode = (uint64_t)get_64bitBE(keybuf+0x00);
|
||||
} else {
|
||||
}
|
||||
else if (keysize == 0x08+0x02) { /* seed key + AWB subkey */
|
||||
uint64_t key = (uint64_t)get_64bitBE(keybuf+0x00);
|
||||
uint16_t sub = (uint16_t)get_16bitBE(keybuf+0x08);
|
||||
keycode = key * ( ((uint64_t)sub << 16u) | ((uint16_t)~sub + 2u) );
|
||||
}
|
||||
else {
|
||||
find_hca_key(hca_data, &keycode);
|
||||
}
|
||||
|
||||
@ -63,46 +72,61 @@ fail:
|
||||
}
|
||||
|
||||
|
||||
static inline void test_key(hca_codec_data * hca_data, uint64_t key, uint16_t subkey, int *best_score, uint64_t *best_keycode) {
|
||||
int score;
|
||||
|
||||
if (subkey) {
|
||||
key = key * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) );
|
||||
}
|
||||
|
||||
score = test_hca_key(hca_data, (unsigned long long)key);
|
||||
|
||||
//;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x, score=%i\n",
|
||||
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score);
|
||||
|
||||
/* wrong key */
|
||||
if (score < 0)
|
||||
return;
|
||||
|
||||
/* score 0 is not trustable, update too if something better is found */
|
||||
if (*best_score < 0 || (score < *best_score && score > 0) || (*best_score == 0 && score == 1)) {
|
||||
*best_score = score;
|
||||
*best_keycode = key;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to find the decryption key from a list. */
|
||||
static void find_hca_key(hca_codec_data * hca_data, unsigned long long * out_keycode) {
|
||||
const size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info);
|
||||
unsigned long long best_keycode;
|
||||
int best_score = -1;
|
||||
int i;
|
||||
|
||||
best_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */
|
||||
int i,j;
|
||||
|
||||
*out_keycode = 0xCC55463930DBE1AB; /* defaults to PSO2 key, most common */
|
||||
|
||||
/* find a candidate key */
|
||||
for (i = 0; i < keys_length; i++) {
|
||||
int score;
|
||||
unsigned long long keycode = (unsigned long long)hcakey_list[i].key;
|
||||
uint64_t key = hcakey_list[i].key;
|
||||
size_t subkeys_size = hcakey_list[i].subkeys_size;
|
||||
const uint16_t *subkeys = hcakey_list[i].subkeys;
|
||||
|
||||
score = test_hca_key(hca_data, keycode);
|
||||
|
||||
//;VGM_LOG("HCA: test key=%08x%08x, score=%i\n",
|
||||
// (uint32_t)((keycode >> 32) & 0xFFFFFFFF), (uint32_t)(keycode & 0xFFFFFFFF), score);
|
||||
|
||||
/* wrong key */
|
||||
if (score < 0)
|
||||
continue;
|
||||
|
||||
/* score 0 is not trustable, update too if something better is found */
|
||||
if (best_score < 0 || score < best_score || (best_score == 0 && score == 1)) {
|
||||
best_score = score;
|
||||
best_keycode = keycode;
|
||||
if (subkeys_size > 0) {
|
||||
for (j = 0; j < subkeys_size; j++) {
|
||||
test_key(hca_data, key, subkeys[j], &best_score, out_keycode);
|
||||
if (best_score == 1) /* best possible score */
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* best possible score */
|
||||
if (score == 1) {
|
||||
break;
|
||||
else {
|
||||
test_key(hca_data, key, 0, &best_score, out_keycode);
|
||||
if (best_score == 1) /* best possible score */
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
//;VGM_LOG("HCA: best key=%08x%08x (score=%i)\n",
|
||||
// (uint32_t)((best_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(best_keycode & 0xFFFFFFFF), best_score);
|
||||
// (uint32_t)((*out_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*out_keycode & 0xFFFFFFFF), best_score);
|
||||
|
||||
VGM_ASSERT(best_score > 1, "HCA: best key=%08x%08x (score=%i)\n",
|
||||
(uint32_t)((best_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(best_keycode & 0xFFFFFFFF), best_score);
|
||||
*out_keycode = best_keycode;
|
||||
(uint32_t)((*out_keycode >> 32) & 0xFFFFFFFF), (uint32_t)(*out_keycode & 0xFFFFFFFF), best_score);
|
||||
}
|
||||
|
@ -1,14 +1,23 @@
|
||||
#ifndef _HCA_KEYS_H_
|
||||
#define _HCA_KEYS_H_
|
||||
|
||||
#include "hca_keys_awb.h"
|
||||
|
||||
typedef struct {
|
||||
uint64_t key;
|
||||
uint64_t key; /* hca key or seed key */
|
||||
const uint16_t *subkeys; /* derivation subkey table for seed key */
|
||||
size_t subkeys_size; /* size of the derivation subkey table */
|
||||
} hcakey_info;
|
||||
|
||||
|
||||
/**
|
||||
* List of known keys, extracted from the game files (mostly found in 2ch.net).
|
||||
* CRI's tools expect an unsigned 64 bit number string, but keys are commonly found online in hex form.
|
||||
* Keys only use 56 bits though, so the upper 8 bits can be ignored.
|
||||
*
|
||||
* ACB+AWB after mid 2018 use a master seed key + a derivation subkey in the AWB (normally 16b LE at 0x0e)
|
||||
* to create the final HCA key, which means there is one key per AWB (so most HCA have a unique key).
|
||||
* vgmstream derives the key if subkey table is provided.
|
||||
*/
|
||||
static const hcakey_info hcakey_list[] = {
|
||||
|
||||
@ -249,6 +258,9 @@ static const hcakey_info hcakey_list[] = {
|
||||
// Onsen Musume: Yunohana Kore Kushon (Android) voices
|
||||
{6667}, // 0000000000001A0B
|
||||
|
||||
/* Dragalia Lost (Android) */
|
||||
{2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD
|
||||
|
||||
};
|
||||
|
||||
#endif/*_HCA_KEYS_H_*/
|
||||
|
4112
src/meta/hca_keys_awb.h
Normal file
4112
src/meta/hca_keys_awb.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -797,4 +797,6 @@ VGMSTREAM * init_vgmstream_ue4opus(STREAMFILE *streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_xwma(STREAMFILE * streamFile);
|
||||
|
||||
VGMSTREAM * init_vgmstream_xopus(STREAMFILE * streamFile);
|
||||
|
||||
#endif /*_META_H*/
|
||||
|
@ -272,8 +272,9 @@ VGMSTREAM * init_vgmstream_riff(STREAMFILE *streamFile) {
|
||||
* .adx: Remember11 (PC) sfx
|
||||
* .adp: Headhunter (DC)
|
||||
* .xss: Spider-Man The Movie (Xbox)
|
||||
* .xsew: Mega Man X Legacy Collections (PC) */
|
||||
if ( check_extensions(streamFile, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew") ) {
|
||||
* .xsew: Mega Man X Legacy Collections (PC)
|
||||
* .adpcm: Angry Birds Transformers (Android) */
|
||||
if ( check_extensions(streamFile, "wav,lwav,xwav,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm") ) {
|
||||
;
|
||||
}
|
||||
else if ( check_extensions(streamFile, "mwv") ) {
|
||||
|
66
src/meta/xopus.c
Normal file
66
src/meta/xopus.c
Normal file
@ -0,0 +1,66 @@
|
||||
#include "meta.h"
|
||||
#include "../coding/coding.h"
|
||||
|
||||
|
||||
/* XOPUS - from Rovio games [Angry Birds: Transformers (Android), Angry Birds: Go (Android)] */
|
||||
VGMSTREAM * init_vgmstream_xopus(STREAMFILE *streamFile) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
off_t start_offset;
|
||||
int loop_flag = 0, channel_count, sample_rate, num_samples, skip;
|
||||
size_t data_size;
|
||||
int entries;
|
||||
|
||||
|
||||
/* checks*/
|
||||
if (!check_extensions(streamFile, "xopus"))
|
||||
goto fail;
|
||||
if (read_32bitBE(0x00, streamFile) != 0x584F7075) /* "XOpu" */
|
||||
goto fail;
|
||||
|
||||
/* 0x04: always 1? 0x30? */
|
||||
channel_count = read_8bit(0x05, streamFile);
|
||||
/* 0x06: always 0x30? */
|
||||
/* 0x08: always 0xc8? encoder delay? */
|
||||
num_samples = read_32bitLE(0x0c, streamFile);
|
||||
/* 0x10: always 0x138? max packet size? */
|
||||
entries = read_32bitLE(0x14, streamFile); /* packet sizes */
|
||||
/* 0x18: data size */
|
||||
/* 0x1c: unused */
|
||||
/* 0x20+: packet sizes table */
|
||||
|
||||
|
||||
sample_rate = 48000;
|
||||
loop_flag = 0;
|
||||
|
||||
start_offset = 0x20 + 0x02*entries;
|
||||
data_size = get_streamfile_size(streamFile) - start_offset;
|
||||
skip = x_opus_get_encoder_delay(start_offset, streamFile);
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(channel_count, loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
|
||||
vgmstream->meta_type = meta_XOPUS;
|
||||
vgmstream->sample_rate = sample_rate;
|
||||
vgmstream->num_samples = num_samples /*- skip*/;
|
||||
|
||||
#ifdef VGM_USE_FFMPEG
|
||||
{
|
||||
vgmstream->codec_data = init_ffmpeg_x_opus(streamFile, start_offset,data_size, vgmstream->channels, skip, vgmstream->sample_rate);
|
||||
if (!vgmstream->codec_data) goto fail;
|
||||
vgmstream->coding_type = coding_FFmpeg;
|
||||
vgmstream->layout_type = layout_none;
|
||||
}
|
||||
#else
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
if (!vgmstream_open_stream(vgmstream, streamFile, start_offset))
|
||||
goto fail;
|
||||
return vgmstream;
|
||||
|
||||
fail:
|
||||
close_vgmstream(vgmstream);
|
||||
return NULL;
|
||||
}
|
@ -443,6 +443,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
|
||||
init_vgmstream_adpcm_capcom,
|
||||
init_vgmstream_ue4opus,
|
||||
init_vgmstream_xwma,
|
||||
init_vgmstream_xopus,
|
||||
|
||||
|
||||
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
|
||||
|
@ -700,6 +700,7 @@ typedef enum {
|
||||
meta_UE4OPUS,
|
||||
meta_XWMA,
|
||||
meta_VA3, /* DDR Supernova 2 AC */
|
||||
meta_XOPUS,
|
||||
|
||||
} meta_t;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user