Greatly improve HCA key detection

This commit is contained in:
bnnm 2021-03-14 16:28:35 +01:00
parent 623a0df14d
commit d4209c2d43
3 changed files with 51 additions and 24 deletions

View File

@ -69,7 +69,7 @@
#define HCA_DEFAULT_RANDOM 1
#define HCA_ERROR_OK 0
#define HCA_RESULT_OK 0
#define HCA_ERROR_PARAMS -1
#define HCA_ERROR_HEADER -2
#define HCA_ERROR_CHECKSUM -3
@ -472,7 +472,7 @@ static int ath_init(unsigned char* ath_curve, int type, unsigned int sample_rate
default:
return HCA_ERROR_HEADER;
}
return HCA_ERROR_OK;
return HCA_RESULT_OK;
}
@ -604,7 +604,7 @@ static int cipher_init(unsigned char* cipher_table, int type, unsigned long long
default:
return HCA_ERROR_HEADER;
}
return HCA_ERROR_OK;
return HCA_RESULT_OK;
}
//--------------------------------------------------
@ -973,7 +973,7 @@ int clHCA_DecodeHeader(clHCA* hca, const void *data, unsigned int size) {
* (keycode is not changed between calls) */
hca->is_valid = 1;
return HCA_ERROR_OK;
return HCA_RESULT_OK;
}
void clHCA_SetKey(clHCA* hca, unsigned long long keycode) {
@ -993,18 +993,16 @@ void clHCA_SetKey(clHCA* hca, unsigned long long keycode) {
int clHCA_TestBlock(clHCA* hca, void *data, unsigned int size) {
const int frame_samples = HCA_SUBFRAMES_PER_FRAME * HCA_SAMPLES_PER_SUBFRAME;
const float scale = 32768.0f;
unsigned int ch, sf, s;
unsigned int i, ch, sf, s;
int status;
int clips = 0, blanks = 0, channel_blanks[HCA_MAX_CHANNELS] = {0};
const unsigned char* buf = data;
/* 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++) {
for (i = 0x02; i < size - 0x02; i++) {
if (buf[i] != 0) {
is_empty = 0;
break;
@ -1021,7 +1019,30 @@ int clHCA_TestBlock(clHCA* hca, void *data, unsigned int size) {
if (status < 0)
return -1;
/* check decode results as bad keys may still get here */
/* detect data errors */
{
int bits_max = hca->frame_size * 8;
int byte_start;
/* Should read all frame sans end checksum (16b), but allow 14b as one frame was found to
* read up to that (cross referenced with CRI's tools), perhaps some encoding hiccup
* [World of Final Fantasy Maxima (Switch) am_ev21_0170 video] */
if (status + 14 > bits_max)
return HCA_ERROR_BITREADER;
/* leftover data after read bits in HCA is always null (up to end 16b checksum), so bad keys
* give garbage beyond those bits (data is decrypted at this point and size >= frame_size) */
byte_start = (status / 8) + (status % 8 ? 0x01 : 0);
/* maybe should memcmp with a null frame, unsure of max though, and in most cases
* should fail fast (this check catches almost everything) */
for (i = byte_start; i < hca->frame_size - 0x02; i++) {
if (buf[i] != 0) {
return -1;
}
}
}
/* check decode results as (rarely) 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++) {
@ -1191,15 +1212,7 @@ int clHCA_DecodeBlock(clHCA* hca, void *data, unsigned int size) {
}
/* should read all frame sans checksum (16b) at most */
/* one frame was found to read up to 14b left (cross referenced with CRI's tools),
* perhaps some encoding hiccup [World of Final Fantasy Maxima (Switch) am_ev21_0170 video],
* though this validation makes more sense when testing keys and isn't normally done on decode */
if (br.bit + 14 > br.size) { /* relax validation a bit for that case */
return HCA_ERROR_BITREADER;
}
return HCA_ERROR_OK;
return br.bit; /* numbers of read bits for validations */
}
//--------------------------------------------------
@ -1303,7 +1316,7 @@ static int unpack_scalefactors(stChannel* ch, clData* br, unsigned int hfr_group
ch->scalefactors[HCA_SAMPLES_PER_SUBFRAME - 1 - i] = ch->scalefactors[cs_count - i];
}
return HCA_ERROR_OK;
return HCA_RESULT_OK;
}
/* read intensity (for joint stereo R) or v2.0 high frequency scales (for regular channels) */
@ -1386,7 +1399,7 @@ static int unpack_intensity(stChannel* ch, clData* br, unsigned int hfr_group_co
}
}
return HCA_ERROR_OK;
return HCA_RESULT_OK;
}
/* get resolutions, that determines range of values per encoded spectrum coefficients */

View File

@ -179,6 +179,13 @@ void free_hca(hca_codec_data* data) {
}
/* ************************************************************************* */
/* Test a single HCA key and assign an score for comparison. Multiple keys could potentially result
* in "playable" results (mostly silent with random clips), so it also checks the resulting PCM.
* Currently wrong keys should be detected during decoding+un-xor test, so this score may not
* be needed anymore, but keep around in case CRI breaks those tests in the future. */
/* arbitrary scale to simplify score comparisons */
#define HCA_KEY_SCORE_SCALE 10
/* ignores beginning frames (~10 is not uncommon, Dragalia Lost vocal layers have lots) */
@ -215,7 +222,8 @@ int test_hca_key(hca_codec_data* data, unsigned long long keycode) {
/* read and test frame */
bytes = read_streamfile(data->data_buffer, offset, blockSize, data->streamfile);
if (bytes != blockSize) {
total_score = -1;
/* normally this shouldn't happen, but pre-fetch ACB stop with frames in half, so just keep score */
//total_score = -1;
break;
}

View File

@ -10,11 +10,11 @@ static void find_hca_key(hca_codec_data* hca_data, uint64_t* p_keycode, uint16_t
/* CRI HCA - streamed audio from CRI ADX2/Atom middleware */
VGMSTREAM * init_vgmstream_hca(STREAMFILE* sf) {
VGMSTREAM* init_vgmstream_hca(STREAMFILE* sf) {
return init_vgmstream_hca_subkey(sf, 0x0000);
}
VGMSTREAM * init_vgmstream_hca_subkey(STREAMFILE* sf, uint16_t subkey) {
VGMSTREAM* init_vgmstream_hca_subkey(STREAMFILE* sf, uint16_t subkey) {
VGMSTREAM * vgmstream = NULL;
hca_codec_data* hca_data = NULL;
clHCA_stInfo* hca_info;
@ -122,6 +122,9 @@ 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;
//;VGM_LOG("HCA: test key=%08x%08x, subkey=%04x\n",
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey);
if (subkey) {
key = key * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) );
}
@ -135,6 +138,9 @@ static inline void test_key(hca_codec_data* hca_data, uint64_t key, uint16_t sub
if (score < 0)
return;
//;VGM_LOG("HCA: ok key=%08x%08x, subkey=%04x, score=%i\n",
// (uint32_t)((key >> 32) & 0xFFFFFFFF), (uint32_t)(key & 0xFFFFFFFF), subkey, score);
/* update if something better is found */
if (*best_score <= 0 || (score < *best_score && score > 0)) {
*best_score = score;