Merge pull request #307 from bnnm/hca-xopus

hca xopus
This commit is contained in:
Christopher Snowhill 2018-10-13 15:39:40 -07:00 committed by GitHub
commit 1c68e8cb7b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 4457 additions and 112 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -700,6 +700,7 @@ typedef enum {
meta_UE4OPUS,
meta_XWMA,
meta_VA3, /* DDR Supernova 2 AC */
meta_XOPUS,
} meta_t;