Add HCA support.

This commit is contained in:
Chris Moeller 2016-06-28 00:20:37 -07:00
parent f55a23f2e0
commit 33563f4e7c
14 changed files with 1971 additions and 13 deletions

66
ext_includes/clHCA.h Normal file
View File

@ -0,0 +1,66 @@
#ifndef _clHCA_H
#define _clHCA_H
#ifdef __cplusplus
extern "C" {
#endif
enum { clHCA_samplesPerBlock = 0x80 * 8 };
/* Must pass at least 8 bytes of data to this function. Returns -1 on non-match, or
* positive byte count on success. */
int clHCA_isOurFile0(const void *data);
/* Must pass a full header block for success. Returns 0 on success, -1 on failure. */
int clHCA_isOurFile1(const void *data, unsigned int size);
/* The opaque state structure. */
typedef struct clHCA clHCA;
/* In case you wish to allocate the structure on your own. */
int clHCA_sizeof();
void clHCA_clear(clHCA *, unsigned int ciphKey1, unsigned int ciphKey2);
/* Or you could let the library allocate it. */
clHCA * clHCA_new(unsigned int ciphKey1, unsigned int ciphKey2);
void clHCA_delete(clHCA *);
/* Requires a pre-allocated data structure.
* Before any decoding may be performed, the header block must be passed in.
* The recommended way of doing this is to detect the header length with
* clHCA_isOurFile0, validate the header with clHCA_isOurFile1, then pass
* it to this function, with the address of 0.
* Subsequent decodes with non-zero address are assumed to be sample blocks,
* and should be of the blockSize returned by the clHCA_getInfo function.
* Returns 0 on success, -1 on failure. */
int clHCA_Decode(clHCA *, void *data, unsigned int size, unsigned int address);
/* This is the simplest decode function, for signed and clipped 16 bit samples.
* May be called after clHCA_Decode, and will return the same data until the next
* block of sample data is passed to clHCA_Decode. */
void clHCA_DecodeSamples16(clHCA *, signed short * outSamples);
typedef struct clHCA_stInfo {
unsigned int version;
unsigned int dataOffset;
unsigned int samplingRate;
unsigned int channelCount;
unsigned int blockSize;
unsigned int blockCount;
unsigned int loopEnabled;
unsigned int loopStart;
unsigned int loopEnd;
} clHCA_stInfo;
/* Retrieve information relevant for decoding and playback with this function.
* Must be called after successfully decoding a header block with clHCA_Decode,
* or else it will fail.
* Returns 0 on success, -1 on failure. */
int clHCA_getInfo(clHCA *, clHCA_stInfo *out);
#ifdef __cplusplus
}
#endif
#endif

1605
ext_libs/clHCA.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,8 @@ CODING_OBJS=coding/adx_decoder.o \
coding/lsf_decoder.o \
coding/mtaf_decoder.o \
coding/at3_decoder.o \
coding/g719_decoder.o
coding/g719_decoder.o \
coding/hca_decoder.o
LAYOUT_OBJS=layout/ast_blocked.o \
layout/blocked.o \
@ -300,9 +301,12 @@ META_OBJS=meta/adx_header.o \
meta/bfwav.o \
meta/g1l.o \
meta/mca.o \
meta/btsnd.o
meta/btsnd.o \
meta/hca.o
OBJECTS=vgmstream.o streamfile.o util.o $(CODING_OBJS) $(LAYOUT_OBJS) $(META_OBJS)
EXT_LIBS = ../ext_libs/clHCA.o
OBJECTS=vgmstream.o streamfile.o util.o $(CODING_OBJS) $(LAYOUT_OBJS) $(META_OBJS) $(EXT_LIBS)
libvgmstream.a: $(OBJECTS)
$(AR) crs libvgmstream.a $(OBJECTS)

View File

@ -4,7 +4,7 @@ AM_CFLAGS = -Wall @CFLAGS@ -I$(top_builddir) -I$(top_srcdir)
AM_MAKEFLAGS=-f Makefile.unix
libvgmstream_la_LDFLAGS = coding/libcoding.la layout/liblayout.la meta/libmeta.la
libvgmstream_la_SOURCES = vgmstream.c util.c streamfile.c
libvgmstream_la_SOURCES = vgmstream.c util.c streamfile.c ../ext_libs/clHCA.c
SUBDIRS = coding layout meta

View File

@ -30,5 +30,6 @@ libcoding_la_SOURCES += g7221_decoder.c
libcoding_la_SOURCES += lsf_decoder.c
libcoding_la_SOURCES += mtaf_decoder.c
libcoding_la_SOURCES += g719_decoder.c
libcoding_la_SOURCES += hca_decoder.c
EXTRA_DIST = coding.h g72x_state.h
EXTRA_DIST = coding.h g72x_state.h clHCA.h

82
src/coding/hca_decoder.c Normal file
View File

@ -0,0 +1,82 @@
#include "../vgmstream.h"
void decode_hca(hca_codec_data * data, sample * outbuf, int32_t samples_to_do, int channels) {
int samples_done = 0;
int32_t samples_remain = clHCA_samplesPerBlock - data->sample_ptr;
void *hca_data = NULL;
clHCA *hca;
if ( data->samples_discard ) {
if ( samples_remain <= data->samples_discard ) {
data->samples_discard -= samples_remain;
samples_remain = 0;
}
else {
samples_remain -= data->samples_discard;
data->sample_ptr += data->samples_discard;
data->samples_discard = 0;
}
}
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);
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;
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 );
samples_remain = clHCA_samplesPerBlock;
data->sample_ptr = 0;
if ( data->samples_discard ) {
if ( samples_remain <= data->samples_discard ) {
data->samples_discard -= samples_remain;
samples_remain = 0;
}
else {
samples_remain -= data->samples_discard;
data->sample_ptr = data->samples_discard;
data->samples_discard = 0;
}
}
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 );
}

View File

@ -380,6 +380,10 @@
RelativePath=".\meta\halpst.c"
>
</File>
<File
RelativePath=".\meta\hca.c"
>
</File>
<File
RelativePath=".\meta\his.c"
>
@ -1230,6 +1234,10 @@
RelativePath=".\coding\g7221_decoder.c"
>
</File>
<File
RelativePath=".\coding\hca_decoder.c"
>
</File>
<File
RelativePath=".\coding\ima_decoder.c"
>
@ -1302,6 +1310,10 @@
RelativePath=".\coding\xa_decoder.c"
>
</File>
<File
RelativePath="..\ext_libs\clHCA.c"
>
</File>
</Filter>
</Filter>
<Filter

View File

@ -183,6 +183,7 @@
<ClCompile Include="meta\gh3_bar.c" />
<ClCompile Include="meta\gsp_gsb.c" />
<ClCompile Include="meta\halpst.c" />
<ClCompile Include="meta\hca.c" />
<ClCompile Include="meta\his.c" />
<ClCompile Include="meta\idsp.c" />
<ClCompile Include="meta\ish_isd.c" />
@ -362,6 +363,7 @@
<ClCompile Include="coding\g719_decoder.c" />
<ClCompile Include="coding\g721_decoder.c" />
<ClCompile Include="coding\g7221_decoder.c" />
<ClCompile Include="coding\hca_decoder.c" />
<ClCompile Include="coding\ima_decoder.c" />
<ClCompile Include="coding\l5_555_decoder.c" />
<ClCompile Include="coding\mpeg_decoder.c" />
@ -406,8 +408,9 @@
<ClCompile Include="layout\wsi_blocked.c" />
<ClCompile Include="layout\xa_blocked.c" />
<ClCompile Include="layout\xvas_block.c" />
<ClCompile Include="..\ext_libs\clHCA.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@ -36,6 +36,15 @@
<Filter Include="layout\Source Files">
<UniqueIdentifier>{e729b7b3-e13c-4cf9-9ded-428c209b6f62}</UniqueIdentifier>
</Filter>
<Filter Include="ext_libs">
<UniqueIdentifier>{48DB0DEF-3694-40E0-9FF6-8A736E6C3A62}</UniqueIdentifier>
</Filter>
<Filter Include="ext_libs\Source Files">
<UniqueIdentifier>{78A32BD9-0DB4-4164-A7E6-41506B78392E}</UniqueIdentifier>
</Filter>
<Filter Include="ext_libs\Header Files">
<UniqueIdentifier>{20824073-8817-41CF-8A21-D54294A56050}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="streamfile.h">
@ -68,6 +77,9 @@
<ClInclude Include="layout\layout.h">
<Filter>layout\Header Files</Filter>
</ClInclude>
<ClInclude Include="..\ext_includes\clHCA.h">
<Filter>ext_libs\Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="streamfile.c">
@ -202,6 +214,9 @@
<ClCompile Include="meta\halpst.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\hca.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\his.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
@ -721,6 +736,9 @@
<ClCompile Include="coding\g7221_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\hca_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\ima_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
@ -982,5 +1000,8 @@
<ClCompile Include="coding\g719_decoder.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\ext_libs\clHCA.c">
<Filter>ext_libs\Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>
</Project>

View File

@ -244,5 +244,6 @@ libmeta_la_SOURCES += g1l.c
libmeta_la_SOURCES += ps2_vbk.c
libmeta_la_SOURCES += mca.c
libmeta_la_SOURCES += btsnd.c
libmeta_la_SOURCES += hca.c
EXTRA_DIST = meta.h

98
src/meta/hca.c Normal file
View File

@ -0,0 +1,98 @@
#include "../vgmstream.h"
#include "meta.h"
#include "../util.h"
VGMSTREAM * init_vgmstream_hca_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile) {
return init_vgmstream_hca_offset( streamFile, 0, streamFile->get_size(streamFile) );
}
VGMSTREAM * init_vgmstream_hca_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size) {
/* These I don't know about... */
static const unsigned int ciphKey1=0x30DBE1AB;
static const unsigned int ciphKey2=0xCC554639;
VGMSTREAM * vgmstream = NULL;
char filename[PATH_LIMIT];
hca_codec_data * hca_file = ( hca_codec_data * ) calloc(1, sizeof(hca_codec_data) + clHCA_sizeof());
void * hca_data = NULL;
clHCA * hca;
uint8_t header[8];
int header_size;
if ( !hca_file ) goto fail;
if ( size < 8 ) goto fail;
hca_file->streamfile = streamFile;
hca_file->start = start;
hca_file->size = size;
if ( read_streamfile( header, start, 8, streamFile) != 8 ) goto fail;
header_size = clHCA_isOurFile0( header );
if ( header_size < 0 ) goto fail;
hca_data = malloc( header_size );
if ( !hca_data ) goto fail;
memcpy( hca_data, header, 8 );
if ( read_streamfile( ((uint8_t*)hca_data) + 8, start + 8, header_size - 8, streamFile ) != header_size - 8 ) goto fail;
if ( clHCA_isOurFile1( hca_data, header_size ) < 0 ) goto fail;
hca = (clHCA *)(hca_file + 1);
clHCA_clear(hca, ciphKey1, ciphKey2);
if (clHCA_Decode(hca, hca_data, header_size, 0) < 0) goto fail;
if (clHCA_getInfo(hca, &hca_file->info) < 0) goto fail;
hca_file->sample_ptr = clHCA_samplesPerBlock;
hca_file->samples_discard = 0;
streamFile->get_name( streamFile, filename, sizeof(filename) );
hca_file->streamfile = streamFile->open(streamFile, filename, STREAMFILE_DEFAULT_BUFFER_SIZE);
if (!hca_file->streamfile) goto fail;
vgmstream = allocate_vgmstream( hca_file->info.channelCount, 1 );
if (!vgmstream) goto fail;
free( hca_data );
vgmstream->loop_flag = hca_file->info.loopEnabled;
vgmstream->loop_start_sample = hca_file->info.loopStart * clHCA_samplesPerBlock;
vgmstream->loop_end_sample = hca_file->info.loopEnd * clHCA_samplesPerBlock;
vgmstream->codec_data = hca_file;
vgmstream->channels = hca_file->info.channelCount;
vgmstream->sample_rate = hca_file->info.samplingRate;
vgmstream->num_samples = hca_file->info.blockCount * clHCA_samplesPerBlock;
vgmstream->coding_type = coding_CRI_HCA;
vgmstream->layout_type = layout_none;
vgmstream->meta_type = meta_HCA;
return vgmstream;
fail:
if ( hca_data ) {
free( hca_data );
}
if ( hca_file ) {
free( hca_file );
}
return NULL;
}

View File

@ -111,6 +111,10 @@ VGMSTREAM * init_vgmstream_ogg_vorbis_callbacks(STREAMFILE *streamFile, const ch
VGMSTREAM * init_vgmstream_sli_ogg(STREAMFILE * streamFile);
VGMSTREAM * init_vgmstream_hca(STREAMFILE *streamFile);
VGMSTREAM * init_vgmstream_hca_offset(STREAMFILE *streamFile, uint64_t start, uint64_t size);
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
VGMSTREAM * init_vgmstream_mp4_aac(STREAMFILE * streamFile);

View File

@ -338,6 +338,7 @@ VGMSTREAM * (*init_vgmstream_fcns[])(STREAMFILE *streamFile) = {
init_vgmstream_bcstm,
init_vgmstream_3ds_idsp,
init_vgmstream_g1l,
init_vgmstream_hca,
};
#define INIT_VGMSTREAM_FCNS (sizeof(init_vgmstream_fcns)/sizeof(init_vgmstream_fcns[0]))
@ -432,6 +433,13 @@ void reset_vgmstream(VGMSTREAM * vgmstream) {
ov_pcm_seek(ogg_vorbis_file, 0);
}
#endif
if (vgmstream->coding_type==coding_CRI_HCA) {
hca_codec_data *data = vgmstream->codec_data;
clHCA *hca = (clHCA *)(data + 1);
data->curblock = 0;
data->sample_ptr = clHCA_samplesPerBlock;
data->samples_discard = 0;
}
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type==coding_MP4_AAC) {
mp4_aac_codec_data *data = vgmstream->codec_data;
@ -623,6 +631,12 @@ void close_vgmstream(VGMSTREAM * vgmstream) {
}
}
#endif
if (vgmstream->coding_type==coding_CRI_HCA) {
if (vgmstream->codec_data) {
free(vgmstream->codec_data);
vgmstream->codec_data = NULL;
}
}
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type==coding_MP4_AAC) {
@ -1018,6 +1032,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
return 54;
case coding_MTAF:
return 0x80*2;
case coding_CRI_HCA:
return clHCA_samplesPerBlock;
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
case coding_MP4_AAC:
return ((mp4_aac_codec_data*)vgmstream->codec_data)->samples_per_frame;
@ -1399,6 +1415,11 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
vgmstream->channels);
break;
#endif
case coding_CRI_HCA:
decode_hca(vgmstream->codec_data,
buffer+samples_written*vgmstream->channels,samples_to_do,
vgmstream->channels);
break;
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
case coding_MP4_AAC:
decode_mp4_aac(vgmstream->codec_data,
@ -1685,6 +1706,12 @@ int vgmstream_do_loop(VGMSTREAM * vgmstream) {
ov_pcm_seek_lap(ogg_vorbis_file, vgmstream->loop_sample);
}
#endif
if (vgmstream->coding_type==coding_CRI_HCA) {
hca_codec_data *data = (hca_codec_data *)(vgmstream->codec_data);
data->curblock = data->info.loopStart;
data->sample_ptr = clHCA_samplesPerBlock;
data->samples_discard = 0;
}
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type==coding_MP4_AAC) {
mp4_aac_codec_data *data = (mp4_aac_codec_data *)(vgmstream->codec_data);
@ -1832,6 +1859,9 @@ void describe_vgmstream(VGMSTREAM * vgmstream, char * desc, int length) {
case coding_CRI_ADX_enc_9:
snprintf(temp,TEMPSIZE,"encrypted (type 9) CRI ADX 4-bit ADPCM");
break;
case coding_CRI_HCA:
snprintf(temp,TEMPSIZE,"CRI HCA");
break;
case coding_NDS_IMA:
snprintf(temp,TEMPSIZE,"NDS-style 4-bit IMA ADPCM");
break;
@ -3314,7 +3344,7 @@ fail:
static int get_vgmstream_channel_count(VGMSTREAM * vgmstream)
{
if (vgmstream->layout_type==layout_scd_int) {
scd_int_codec_data *data = vgmstream->codec_data;
scd_int_codec_data *data = (scd_int_codec_data *) vgmstream->codec_data;
if (data) {
return data->substream_count;
}
@ -3324,7 +3354,7 @@ static int get_vgmstream_channel_count(VGMSTREAM * vgmstream)
}
#ifdef VGM_USE_VORBIS
if (vgmstream->coding_type==coding_ogg_vorbis) {
ogg_vorbis_codec_data *data = vgmstream->codec_data;
ogg_vorbis_codec_data *data = (ogg_vorbis_codec_data *) vgmstream->codec_data;
if (data) {
return 1;
@ -3334,9 +3364,19 @@ static int get_vgmstream_channel_count(VGMSTREAM * vgmstream)
}
}
#endif
if (vgmstream->coding_type==coding_CRI_HCA) {
hca_codec_data *data = (hca_codec_data *) vgmstream->codec_data;
if (data) {
return 1;
}
else {
return 0;
}
}
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type==coding_MP4_AAC) {
mp4_aac_codec_data *data = vgmstream->codec_data;
mp4_aac_codec_data *data = (mp4_aac_codec_data *) vgmstream->codec_data;
if (data) {
return 1;
}
@ -3351,19 +3391,24 @@ static int get_vgmstream_channel_count(VGMSTREAM * vgmstream)
static STREAMFILE * get_vgmstream_streamfile(VGMSTREAM * vgmstream, int channel)
{
if (vgmstream->layout_type==layout_scd_int) {
scd_int_codec_data *data = vgmstream->codec_data;
scd_int_codec_data *data = (scd_int_codec_data *) vgmstream->codec_data;
return data->intfiles[channel];
}
#ifdef VGM_USE_VORBIS
if (vgmstream->coding_type==coding_ogg_vorbis) {
ogg_vorbis_codec_data *data = vgmstream->codec_data;
ogg_vorbis_codec_data *data = (ogg_vorbis_codec_data *) vgmstream->codec_data;
return data->ov_streamfile.streamfile;
}
#endif
if (vgmstream->coding_type==coding_CRI_HCA) {
hca_codec_data *data = (hca_codec_data *) vgmstream->codec_data;
return data->streamfile;
}
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
if (vgmstream->coding_type==coding_MP4_AAC) {
mp4_aac_codec_data *data = vgmstream->codec_data;
mp4_aac_codec_data *data = (mp4_aac_codec_data *) vgmstream->codec_data;
return data->if_file.streamfile;
}
#endif

View File

@ -46,6 +46,8 @@ enum { PATH_LIMIT = 32768 };
#include "maiatrac3plus.h"
#endif
#include "clHCA.h"
#include "coding/acm_decoder.h"
#include "coding/nwa_decoder.h"
@ -149,6 +151,8 @@ typedef enum {
coding_PCM16LE_XOR_int, /* sample-level xor */
coding_LSF, /* lsf ADPCM */
coding_MTAF, /* Konami IMA-derived MTAF ADPCM */
coding_CRI_HCA, /* CRI High Compression Audio */
#if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC)
coding_MP4_AAC,
@ -581,6 +585,7 @@ typedef enum {
meta_G1L, // Tecmo Koei G1L
meta_MCA, // Capcom MCA "MADP"
meta_XB3D_ADX, // Xenoblade Chronicles 3D ADX
meta_HCA,
#ifdef VGM_USE_MP4V2
meta_MP4,
#endif
@ -814,6 +819,17 @@ typedef struct {
STREAMFILE **intfiles;
} scd_int_codec_data;
typedef struct {
STREAMFILE *streamfile;
uint64_t start;
uint64_t size;
clHCA_stInfo info;
unsigned int curblock;
unsigned int sample_ptr, samples_discard;
signed short sample_buffer[clHCA_samplesPerBlock * 16];
// clHCA exists here
} hca_codec_data;
#ifdef VGM_USE_MP4V2
typedef struct {
STREAMFILE *streamfile;