From 411fa498bb5a7c3e4aaa0a565cef33bc620c9d89 Mon Sep 17 00:00:00 2001 From: bnnm Date: Wed, 29 Aug 2018 20:05:31 +0200 Subject: [PATCH] Clean HCA meta/decoder for future changes --- src/coding/coding.h | 3 +- src/coding/hca_decoder.c | 107 ++++++++++++++++++++++----------- src/meta/hca.c | 127 ++++++++++++++++++--------------------- src/vgmstream.c | 2 +- src/vgmstream.h | 5 +- 5 files changed, 135 insertions(+), 109 deletions(-) diff --git a/src/coding/coding.h b/src/coding/coding.h index 3fa15733..78dc08bc 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -170,7 +170,8 @@ void seek_ea_mt(VGMSTREAM * vgmstream, int32_t num_sample); void free_ea_mt(ea_mt_codec_data *data); /* hca_decoder */ -void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels); +hca_codec_data *init_hca(STREAMFILE *streamFile); +void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do); void reset_hca(VGMSTREAM *vgmstream); void loop_hca(VGMSTREAM *vgmstream); void free_hca(hca_codec_data * data); diff --git a/src/coding/hca_decoder.c b/src/coding/hca_decoder.c index 8288229f..9ddec2b2 100644 --- a/src/coding/hca_decoder.c +++ b/src/coding/hca_decoder.c @@ -1,12 +1,45 @@ #include "coding.h" -void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) { + +/* init a HCA stream; STREAMFILE will be duplicated for internal use. */ +hca_codec_data * init_hca(STREAMFILE *streamFile) { + char filename[PATH_LIMIT]; + hca_codec_data * data = NULL; /* vgmstream HCA context */ + + /* init vgmstream context */ + data = calloc(1, sizeof(hca_codec_data)); + if (!data) goto fail; + + /* init library handle */ + data->handle = calloc(1, clHCA_sizeof()); + + data->sample_ptr = clHCA_samplesPerBlock; + + /* load streamfile for reads */ + get_streamfile_name(streamFile,filename, sizeof(filename)); + data->streamfile = open_streamfile(streamFile,filename); + if (!data->streamfile) goto fail; + + return data; + +fail: + free_hca(data); + return NULL; +} + +void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do) { int samples_done = 0; int32_t samples_remain = clHCA_samplesPerBlock - data->sample_ptr; - - void *hca_data = NULL; - - clHCA *hca; + + //todo improve (can't be done on init since data->info is only read after setting key) + if (!data->data_buffer) { + data->data_buffer = malloc(data->info.blockSize); + if (!data->data_buffer) return; + + data->sample_buffer = malloc(sizeof(signed short) * data->info.channelCount * clHCA_samplesPerBlock); + if (!data->sample_buffer) return; + } + if ( data->samples_discard ) { if ( samples_remain <= data->samples_discard ) { @@ -20,41 +53,45 @@ void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do, i } } - if ( samples_remain > samples_to_do ) samples_remain = samples_to_do; - - memcpy( outbuf, data->sample_buffer + data->sample_ptr * data->info.channelCount, samples_remain * data->info.channelCount * sizeof(sample) ); - + if (samples_remain > samples_to_do) + samples_remain = samples_to_do; + memcpy(outbuf, data->sample_buffer + data->sample_ptr * data->info.channelCount, samples_remain * data->info.channelCount * sizeof(sample)); outbuf += samples_remain * data->info.channelCount; - data->sample_ptr += samples_remain; - samples_done += samples_remain; - - hca_data = malloc( data->info.blockSize ); - - if ( !hca_data ) return; - - hca = (clHCA *)(data + 1); + + /* feed */ while ( samples_done < samples_to_do ) { const unsigned int blockSize = data->info.blockSize; const unsigned int channelCount = data->info.channelCount; const unsigned int address = data->info.dataOffset + data->curblock * blockSize; - + int status; + size_t bytes; + + /* EOF/error */ if (data->curblock >= data->info.blockCount) { memset(outbuf, 0, (samples_to_do - samples_done) * channelCount * sizeof(sample)); break; } - - if ( read_streamfile((uint8_t*) hca_data, data->start + address, blockSize, data->streamfile) != blockSize ) - break; - - if ( clHCA_Decode( hca, hca_data, blockSize, address ) < 0 ) - break; - - ++data->curblock; - clHCA_DecodeSamples16( hca, data->sample_buffer ); + /* read frame */ + bytes = read_streamfile(data->data_buffer, address, blockSize, data->streamfile); + if (bytes != blockSize) { + VGM_LOG("HCA: read %x vs expected %x bytes at %x\n", bytes, blockSize, address); + break; + } + + /* decode frame */ + status = clHCA_Decode(data->handle, (void*)(data->data_buffer), blockSize, address); + if (status < 0) { + VGM_LOG("HCA: decode fail at %x", address); + break; + } + data->curblock++; + + /* extract samples */ + clHCA_DecodeSamples16(data->handle, data->sample_buffer); samples_remain = clHCA_samplesPerBlock; data->sample_ptr = 0; @@ -70,14 +107,13 @@ void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do, i } } - if ( samples_remain > samples_to_do - samples_done ) samples_remain = samples_to_do - samples_done; - memcpy( outbuf, data->sample_buffer, samples_remain * channelCount * sizeof(sample) ); + if (samples_remain > samples_to_do - samples_done) + samples_remain = samples_to_do - samples_done; + memcpy(outbuf, data->sample_buffer, samples_remain * channelCount * sizeof(sample)); samples_done += samples_remain; outbuf += samples_remain * channelCount; data->sample_ptr = samples_remain; } - - free( hca_data ); } @@ -101,10 +137,11 @@ void loop_hca(VGMSTREAM *vgmstream) { void free_hca(hca_codec_data * data) { if (data) { - clHCA *hca = (clHCA *)(data + 1); - clHCA_done(hca); - if (data->streamfile) - close_streamfile(data->streamfile); + close_streamfile(data->streamfile); + clHCA_done(data->handle); + free(data->handle); + free(data->data_buffer); + free(data->sample_buffer); free(data); } } diff --git a/src/meta/hca.c b/src/meta/hca.c index fcd599be..fce60855 100644 --- a/src/meta/hca.c +++ b/src/meta/hca.c @@ -2,76 +2,59 @@ #include "hca_keys.h" #include "../coding/coding.h" -#define HCA_KEY_MAX_TEST_CLIPS 400 /* hopefully nobody masters files with more that a handful... */ -#define HCA_KEY_MAX_TEST_FRAMES 100 /* ~102400 samples */ -#define HCA_KEY_MAX_TEST_SAMPLES 10240 /* ~10 frames of non-blank samples */ - -static void find_hca_key(hca_codec_data * hca_data, clHCA * hca, uint8_t * buffer, int header_size, unsigned int * out_key1, unsigned int * out_key2); +static void find_hca_key(hca_codec_data * hca_data, uint8_t * header_buffer, int header_size, unsigned int * out_keycode_upper, unsigned int * out_keycode_lower); VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; - uint8_t buffer[0x8000]; /* hca header buffer data (probably max ~0x400) */ - char filename[PATH_LIMIT]; - off_t start = 0; - size_t file_size = streamFile->get_size(streamFile); + uint8_t header_buffer[0x8000]; /* hca header buffer data (probably max ~0x400) */ - int header_size; hca_codec_data * hca_data = NULL; /* vgmstream HCA context */ - clHCA * hca; /* HCA_Decoder context */ - unsigned int ciphKey1, ciphKey2; + unsigned int keycode_upper, keycode_lower; + int header_size; - /* check extension, case insensitive */ - if ( !check_extensions(streamFile, "hca")) return NULL; + /* checks */ + if ( !check_extensions(streamFile, "hca")) + return NULL; + /* init header */ + if (read_streamfile(header_buffer, 0x00, 0x08, streamFile) != 0x08) + goto fail; + header_size = clHCA_isOurFile0(header_buffer); + if (header_size < 0 || header_size > 0x8000) goto fail; - /* test/init header (find real header size first) */ - if ( file_size < 8 ) goto fail; - if ( read_streamfile(buffer, start, 8, streamFile) != 8 ) goto fail; - - header_size = clHCA_isOurFile0(buffer); - if ( header_size < 0 || header_size > 0x8000 ) goto fail; - - if ( read_streamfile(buffer, start, header_size, streamFile) != header_size ) goto fail; - if ( clHCA_isOurFile1(buffer, header_size) < 0 ) goto fail; + if (read_streamfile(header_buffer, 0x00, header_size, streamFile) != header_size) goto fail; + if (clHCA_isOurFile1(header_buffer, header_size) < 0) + goto fail; /* init vgmstream context */ - hca_data = (hca_codec_data *) calloc(1, sizeof(hca_codec_data) + clHCA_sizeof()); - if (!hca_data) goto fail; - //hca_data->size = file_size; - hca_data->start = 0; - hca_data->sample_ptr = clHCA_samplesPerBlock; - - /* HCA_Decoder context memory goes right after our codec data (reserved in alloc'ed) */ - hca = (clHCA *)(hca_data + 1); - - /* pre-load streamfile so the hca_data is ready before key detection */ - streamFile->get_name( streamFile, filename, sizeof(filename) ); - hca_data->streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE); - if (!hca_data->streamfile) goto fail; + hca_data = init_hca(streamFile); /* find decryption key in external file or preloaded list */ { uint8_t keybuf[8]; if (read_key_file(keybuf, 8, streamFile) == 8) { - ciphKey2 = get_32bitBE(keybuf+0); - ciphKey1 = get_32bitBE(keybuf+4); + keycode_upper = get_32bitBE(keybuf+0); + keycode_lower = get_32bitBE(keybuf+4); } else { - find_hca_key(hca_data, hca, buffer, header_size, &ciphKey1, &ciphKey2); + find_hca_key(hca_data, header_buffer, header_size, &keycode_upper, &keycode_lower); } } - /* init decoder with key */ - clHCA_clear(hca, ciphKey1, ciphKey2); - if ( clHCA_Decode(hca, buffer, header_size, 0) < 0 ) goto fail; - if ( clHCA_getInfo(hca, &hca_data->info) < 0 ) goto fail; + /* re-init decoder with key (as it must be supplied on header read) */ + clHCA_clear(hca_data->handle, keycode_lower, keycode_upper); + if (clHCA_Decode(hca_data->handle, header_buffer, header_size, 0) < 0) /* read header at 0x00 */ + goto fail; + if (clHCA_getInfo(hca_data->handle, &hca_data->info) < 0) /* copy important header values to info struct */ + goto fail; /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(hca_data->info.channelCount, hca_data->info.loopEnabled); if (!vgmstream) goto fail; + vgmstream->meta_type = meta_HCA; vgmstream->sample_rate = hca_data->info.samplingRate; vgmstream->num_samples = hca_data->info.blockCount * clHCA_samplesPerBlock; vgmstream->loop_start_sample = hca_data->info.loopStart * clHCA_samplesPerBlock; @@ -79,64 +62,68 @@ VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) { vgmstream->coding_type = coding_CRI_HCA; vgmstream->layout_type = layout_none; - vgmstream->meta_type = meta_HCA; - vgmstream->codec_data = hca_data; return vgmstream; fail: - free(hca_data); + free_hca(hca_data); return NULL; } +#define HCA_KEY_MAX_TEST_CLIPS 400 /* hopefully nobody masters files with more that a handful... */ +#define HCA_KEY_MAX_TEST_FRAMES 100 /* ~102400 samples */ +#define HCA_KEY_MAX_TEST_SAMPLES 10240 /* ~10 frames of non-blank samples */ + /* Tries to find the decryption key from a list. Simply decodes a few frames and checks if there aren't too many * clipped samples, as it's common for invalid keys (though possible with valid keys in poorly mastered files). */ -static void find_hca_key(hca_codec_data * hca_data, clHCA * hca, uint8_t * buffer, int header_size, unsigned int * out_key1, unsigned int * out_key2) { - sample *testbuf = NULL, *temp; +static void find_hca_key(hca_codec_data * hca_data, uint8_t * header_buffer, int header_size, unsigned int * out_keycode_upper, unsigned int * out_keycode_lower) { + sample *testbuf = NULL; int i, j, bufsize = 0, tempsize; size_t keys_length = sizeof(hcakey_list) / sizeof(hcakey_info); int min_clip_count = -1; /* defaults to PSO2 key, most common */ - unsigned int best_key2 = 0xCC554639; - unsigned int best_key1 = 0x30DBE1AB; + unsigned int best_keycode_upper = 0xCC554639; + unsigned int best_keycode_lower = 0x30DBE1AB; /* find a candidate key */ for (i = 0; i < keys_length; i++) { int clip_count = 0, sample_count = 0; - int f = 0, s; + int frame = 0, s; - unsigned int key1, key2; + unsigned int keycode_upper, keycode_lower; uint64_t key = hcakey_list[i].key; - key2 = (key >> 32) & 0xFFFFFFFF; - key1 = (key >> 0) & 0xFFFFFFFF; + keycode_upper = (key >> 32) & 0xFFFFFFFF; + keycode_lower = (key >> 0) & 0xFFFFFFFF; /* re-init HCA with the current key as buffer becomes invalid (probably can be simplified) */ hca_data->curblock = 0; hca_data->sample_ptr = clHCA_samplesPerBlock; - if ( read_streamfile(buffer, hca_data->start, header_size, hca_data->streamfile) != header_size ) continue; + if (read_streamfile(header_buffer, hca_data->start, header_size, hca_data->streamfile) != header_size) + continue; - clHCA_clear(hca, key1, key2); - if (clHCA_Decode(hca, buffer, header_size, 0) < 0) continue; - if (clHCA_getInfo(hca, &hca_data->info) < 0) continue; - if (hca_data->info.channelCount > 32) continue; /* nonsense don't alloc too much */ + clHCA_clear(hca_data->handle, keycode_lower, keycode_upper); + if (clHCA_Decode(hca_data->handle, header_buffer, header_size, 0) < 0) + continue; + if (clHCA_getInfo(hca_data->handle, &hca_data->info) < 0) + continue; tempsize = sizeof(sample) * clHCA_samplesPerBlock * hca_data->info.channelCount; if (tempsize > bufsize) { /* should happen once */ - temp = (sample *)realloc(testbuf, tempsize); + sample *temp = (sample *)realloc(testbuf, tempsize); if (!temp) goto end; testbuf = temp; bufsize = tempsize; } /* test enough frames, but not too many */ - while (f < HCA_KEY_MAX_TEST_FRAMES && f < hca_data->info.blockCount) { + while (frame < HCA_KEY_MAX_TEST_FRAMES && frame < hca_data->info.blockCount) { j = clHCA_samplesPerBlock; - decode_hca(hca_data, testbuf, j, hca_data->info.channelCount); + decode_hca(hca_data, testbuf, j); j *= hca_data->info.channelCount; for (s = 0; s < j; s++) { @@ -152,13 +139,13 @@ static void find_hca_key(hca_codec_data * hca_data, clHCA * hca, uint8_t * buffe if (sample_count >= HCA_KEY_MAX_TEST_SAMPLES) break; /* enough non-blank samples tested */ - f++; + frame++; } if (min_clip_count < 0 || clip_count < min_clip_count) { min_clip_count = clip_count; - best_key2 = key2; - best_key1 = key1; + best_keycode_upper = keycode_upper; + best_keycode_lower = keycode_lower; } if (min_clip_count == 0) @@ -172,11 +159,11 @@ static void find_hca_key(hca_codec_data * hca_data, clHCA * hca, uint8_t * buffe /* reset HCA */ hca_data->curblock = 0; hca_data->sample_ptr = clHCA_samplesPerBlock; - read_streamfile(buffer, hca_data->start, header_size, hca_data->streamfile); + read_streamfile(header_buffer, hca_data->start, header_size, hca_data->streamfile); end: - VGM_ASSERT(min_clip_count > 0, "HCA: best key=%08x%08x (clips=%i)\n", best_key2,best_key1, min_clip_count); - *out_key2 = best_key2; - *out_key1 = best_key1; - free(testbuf);//free(temp); + VGM_ASSERT(min_clip_count > 0, "HCA: best key=%08x%08x (clips=%i)\n", best_keycode_upper,best_keycode_lower, min_clip_count); + *out_keycode_upper = best_keycode_upper; + *out_keycode_lower = best_keycode_lower; + free(testbuf); } diff --git a/src/vgmstream.c b/src/vgmstream.c index f7caa4a3..a51a06e9 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -1676,7 +1676,7 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to #endif case coding_CRI_HCA: decode_hca(vgmstream->codec_data, buffer+samples_written*vgmstream->channels, - samples_to_do,vgmstream->channels); + samples_to_do); break; #ifdef VGM_USE_FFMPEG case coding_FFmpeg: diff --git a/src/vgmstream.h b/src/vgmstream.h index 2c00b5c5..32af3dc4 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -1097,8 +1097,9 @@ typedef struct { unsigned int curblock; unsigned int sample_ptr; unsigned int samples_discard; - signed short sample_buffer[clHCA_samplesPerBlock * 16]; - //clHCA * hca exists here (pre-alloc'ed) + signed short *sample_buffer; + void* handle; + void* data_buffer; } hca_codec_data; #ifdef VGM_USE_FFMPEG