diff --git a/Frameworks/vgmstream/vgmstream/ext_libs/clHCA.c b/Frameworks/vgmstream/vgmstream/ext_libs/clHCA.c index 1877d8127..7d48cd2f5 100644 --- a/Frameworks/vgmstream/vgmstream/ext_libs/clHCA.c +++ b/Frameworks/vgmstream/vgmstream/ext_libs/clHCA.c @@ -1,23 +1,27 @@ /** * clHCA DECODER * + * Decodes CRI's HCA (High Compression Audio), a CBR DCT-based codec (similar to AAC). + * Also supports what CRI calls HCA-MX, which basically is the same thing with constrained + * encoder settings. + * * - Original decompilation and C++ decoder by nyaga * https://github.com/Nyagamon/HCADecoder * - Ported to C by kode54 * https://gist.github.com/kode54/ce2bf799b445002e125f06ed833903c0 - * - Cleaned up by bnnm using Thealexbarney's VGAudio decoder as reference + * - Cleaned up and re-reverse engineered for HCA v3 by bnnm, using Thealexbarney's VGAudio decoder as reference * https://github.com/Thealexbarney/VGAudio */ /* TODO: * - improve portability on types and float casts, sizeof(int) isn't necessarily sizeof(float) - * - check "packed_noise_level" vs VGAudio (CriHcaPacking.UnpackFrameHeader), weird behaviour - * - check "delta scalefactors" vs VGAudio (CriHcaPacking.DeltaDecode), may be setting wrong values on bad data - * - check "read intensity" vs VGAudio (CriHcaPacking.ReadIntensity), skips intensities if first is 15 * - simplify DCT4 code - * - add extra validations: encoder_delay/padding < sample_count, bands/totals (max: 128?), track count==1, etc - * - calling clHCA_clear multiple times will not deallocate "comment" correctly + * - add extra validations: encoder_delay/padding < sample_count, etc + * - intensity should memset if intensity is 15 or set in reset? (no games hit 15?) + * - check mdct + tables, add floats + * - simplify bitreader to use in decoder only (no need to read +16 bits) */ + //-------------------------------------------------- // Includes //-------------------------------------------------- @@ -26,14 +30,46 @@ #include #include -#define HCA_MASK 0x7F7F7F7F /* chunk obfuscation when the HCA is encrypted with key */ +/* CRI libs may only accept last version in some cases/modes, though most decoding takes older versions + * into account. Lib is identified with "HCA Decoder (Float)" + version string. Some known versions: + * - ~V1.1 2011 [first public version] + * - ~V1.2 2011 [ciph/ath chunks, disabled ATH] + * - Ver.1.40 2011-04 [header mask] + * - Ver.1.42 2011-05 + * - Ver.1.45.02 2012-03 + * - Ver.2.00.02 2012-06 [decoding updates] + * - Ver.2.02.02 2013-12, 2014-11 + * - Ver.2.06.05 2018-12 [scramble subkey API] + * - Ver.2.06.07 2020-02, 2021-02 + * - Ver.3.01.00 2020-11 [decoding updates] + * Same version rebuilt gets a newer date, and new APIs change header strings, but header versions + * only change when decoder does. Despite the name, no "Integer" version seems to exist. + */ +#define HCA_VERSION_V101 0x0101 /* V1.1+ [El Shaddai (PS3/X360)] */ +#define HCA_VERSION_V102 0x0102 /* V1.2+ [Gekka Ryouran Romance (PSP)] */ +#define HCA_VERSION_V103 0x0103 /* V1.4+ [Phantasy Star Online 2 (PC), Binary Domain (PS3)] */ +#define HCA_VERSION_V200 0x0200 /* V2.0+ [Yakuza 5 (PS3)] */ +#define HCA_VERSION_V300 0x0300 /* V3.0+ [Uma Musume (Android)] */ + +/* maxs depend on encoder quality settings (for example, stereo has: + * highest=0x400, high=0x2AA, medium=0x200, low=0x155, lowest=0x100) */ +#define HCA_MIN_FRAME_SIZE 0x8 /* lib min */ +#define HCA_MAX_FRAME_SIZE 0xFFFF /* lib max */ + +#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_SUBFRAME 128 /* also spectrum points/etc */ #define HCA_SAMPLES_PER_FRAME (HCA_SUBFRAMES_PER_FRAME*HCA_SAMPLES_PER_SUBFRAME) -#define HCA_MDCT_BITS 7 /* (1<<7) = 128 */ +#define HCA_MDCT_BITS 7 /* (1<<7) = 128 */ -#define HCA_MAX_CHANNELS 16 /* internal max? in practice only 8 can be encoded */ +#define HCA_MIN_CHANNELS 1 +#define HCA_MAX_CHANNELS 16 /* internal max (in practice only 8 can be encoded) */ +#define HCA_MIN_SAMPLE_RATE 1 /* assumed */ +#define HCA_MAX_SAMPLE_RATE 0x7FFFFF /* encoder max seems 48000 */ +#define HCA_DEFAULT_RANDOM 1 + +#define HCA_RESULT_OK 0 #define HCA_ERROR_PARAMS -1 #define HCA_ERROR_HEADER -2 #define HCA_ERROR_CHECKSUM -3 @@ -45,16 +81,19 @@ // Decoder config/state //-------------------------------------------------- typedef enum { DISCRETE = 0, STEREO_PRIMARY = 1, STEREO_SECONDARY = 2 } channel_type_t; + typedef struct stChannel { /* HCA channel config */ - int type; /* discrete / stereo-primary / stereo-secondary */ - unsigned int coded_scalefactor_count; /* scalefactors used (depending on channel type) */ - unsigned char *hfr_scales; /* high frequency scales, pointing to higher scalefactors (simplification) */ + channel_type_t type; + unsigned int coded_count; /* encoded scales/resolutions/coefs */ /* subframe state */ - unsigned char intensity[HCA_SUBFRAMES_PER_FRAME]; /* intensity indexes (value max: 15 / 4b) */ + unsigned char intensity[HCA_SUBFRAMES_PER_FRAME]; /* intensity indexes for joins stereo (value max: 15 / 4b) */ unsigned char scalefactors[HCA_SAMPLES_PER_SUBFRAME]; /* scale indexes (value max: 64 / 6b)*/ unsigned char resolution[HCA_SAMPLES_PER_SUBFRAME]; /* resolution indexes (value max: 15 / 4b) */ + unsigned char noises[HCA_SAMPLES_PER_SUBFRAME]; /* indexes to coefs that need noise fill + coefs that don't (value max: 128 / 8b) */ + unsigned int noise_count; /* resolutions with noise values saved in 'noises' */ + unsigned int valid_count; /* resolutions with valid values saved in 'noises' */ float gain[HCA_SAMPLES_PER_SUBFRAME]; /* gain to apply to quantized spectral data */ float spectra[HCA_SAMPLES_PER_SUBFRAME]; /* resulting dequantized data */ @@ -89,8 +128,8 @@ typedef struct clHCA { unsigned int base_band_count; unsigned int stereo_band_count; unsigned int bands_per_hfr_group; - unsigned int reserved1; - unsigned int reserved2; + unsigned int ms_stereo; + unsigned int reserved; /* vbr chunk */ unsigned int vbr_max_frame_size; unsigned int vbr_noise_Level; @@ -108,28 +147,29 @@ typedef struct clHCA { /* rva chunk */ float rva_volume; /* comm chunk */ - unsigned int comment_len; - char *comment; + unsigned int comment_len; /* max 0xFF */ + char comment[255+1]; /* initial state */ - unsigned int hfr_group_count; + unsigned int hfr_group_count; /* high frequency band groups not encoded directly */ unsigned char ath_curve[HCA_SAMPLES_PER_SUBFRAME]; unsigned char cipher_table[256]; /* variable state */ + unsigned int random; stChannel channel[HCA_MAX_CHANNELS]; } clHCA; typedef struct clData { - const unsigned char *data; + const unsigned char* data; int size; int bit; -} clData; +} clData; //-------------------------------------------------- // Checksum //-------------------------------------------------- -static const unsigned short crc16_lookup_table[256] = { +static const unsigned short hcacommon_crc_mask_table[256] = { 0x0000,0x8005,0x800F,0x000A,0x801B,0x001E,0x0014,0x8011,0x8033,0x0036,0x003C,0x8039,0x0028,0x802D,0x8027,0x0022, 0x8063,0x0066,0x006C,0x8069,0x0078,0x807D,0x8077,0x0072,0x0050,0x8055,0x805F,0x005A,0x804B,0x004E,0x0044,0x8041, 0x80C3,0x00C6,0x00CC,0x80C9,0x00D8,0x80DD,0x80D7,0x00D2,0x00F0,0x80F5,0x80FF,0x00FA,0x80EB,0x00EE,0x00E4,0x80E1, @@ -148,13 +188,14 @@ static const unsigned short crc16_lookup_table[256] = { 0x0220,0x8225,0x822F,0x022A,0x823B,0x023E,0x0234,0x8231,0x8213,0x0216,0x021C,0x8219,0x0208,0x820D,0x8207,0x0202, }; -static unsigned short crc16_checksum(const unsigned char *data, unsigned int size) { +//HCACommon_CalculateCrc +static unsigned short crc16_checksum(const unsigned char* data, unsigned int size) { unsigned int i; unsigned short sum = 0; /* HCA header/frames should always have checksum 0 (checksum(size-16b) = last 16b) */ for (i = 0; i < size; i++) { - sum = (sum << 8) ^ crc16_lookup_table[(sum >> 8) ^ data[i]]; + sum = (sum << 8) ^ hcacommon_crc_mask_table[(sum >> 8) ^ data[i]]; } return sum; } @@ -162,13 +203,15 @@ static unsigned short crc16_checksum(const unsigned char *data, unsigned int siz //-------------------------------------------------- // Bitstream reader //-------------------------------------------------- -static void bitreader_init(clData *br, const void *data, int size) { +static void bitreader_init(clData* br, const void *data, int size) { br->data = data; br->size = size * 8; br->bit = 0; } -static unsigned int bitreader_peek(clData *br, int bitsize) { +/* CRI's bitreader only handles 16b max during decode (header just reads bytes) + * so maybe could be optimized by ignoring higher cases */ +static unsigned int bitreader_peek(clData* br, int bitsize) { const unsigned int bit = br->bit; const unsigned int bit_rem = bit & 7; const unsigned int size = br->size; @@ -185,7 +228,7 @@ static unsigned int bitreader_peek(clData *br, int bitsize) { 0xFFFFFFFF,0x7FFFFFFF,0x3FFFFFFF,0x1FFFFFFF, 0x0FFFFFFF,0x07FFFFFF,0x03FFFFFF,0x01FFFFFF }; - const unsigned char *data = &br->data[bit >> 3]; + const unsigned char* data = &br->data[bit >> 3]; v = data[0]; v = (v << 8) | data[1]; v = (v << 8) | data[2]; @@ -198,7 +241,7 @@ static unsigned int bitreader_peek(clData *br, int bitsize) { 0xFFFFFF,0x7FFFFF,0x3FFFFF,0x1FFFFF, 0x0FFFFF,0x07FFFF,0x03FFFF,0x01FFFF }; - const unsigned char *data = &br->data[bit >> 3]; + const unsigned char* data = &br->data[bit >> 3]; v = data[0]; v = (v << 8) | data[1]; v = (v << 8) | data[2]; @@ -209,7 +252,7 @@ static unsigned int bitreader_peek(clData *br, int bitsize) { static const unsigned int mask[8] = { 0xFFFF,0x7FFF,0x3FFF,0x1FFF,0x0FFF,0x07FF,0x03FF,0x01FF }; - const unsigned char *data = &br->data[bit >> 3]; + const unsigned char* data = &br->data[bit >> 3]; v = data[0]; v = (v << 8) | data[1]; v &= mask[bit_rem]; @@ -219,7 +262,7 @@ static unsigned int bitreader_peek(clData *br, int bitsize) { static const unsigned int mask[8] = { 0xFF,0x7F,0x3F,0x1F,0x0F,0x07,0x03,0x01 }; - const unsigned char *data = &br->data[bit >> 3]; + const unsigned char* data = &br->data[bit >> 3]; v = data[0]; v &= mask[bit_rem]; v >>= 8 - bit_rem - bitsize; @@ -227,13 +270,13 @@ static unsigned int bitreader_peek(clData *br, int bitsize) { return v; } -static unsigned int bitreader_read(clData *br, int bitsize) { +static unsigned int bitreader_read(clData* br, int bitsize) { unsigned int v = bitreader_peek(br, bitsize); br->bit += bitsize; return v; } -static void bitreader_skip(clData *br, int bitsize) { +static void bitreader_skip(clData* br, int bitsize) { br->bit += bitsize; } @@ -259,7 +302,7 @@ int clHCA_isOurFile(const void *data, unsigned int size) { return header_size; } -int clHCA_getInfo(clHCA *hca, clHCA_stInfo *info) { +int clHCA_getInfo(clHCA* hca, clHCA_stInfo *info) { if (!hca || !info || !hca->is_valid) return HCA_ERROR_PARAMS; @@ -282,26 +325,25 @@ int clHCA_getInfo(clHCA *hca, clHCA_stInfo *info) { return 0; } -void clHCA_ReadSamples16(clHCA *hca, signed short *samples) { - const float scale = 32768.0f; +//HCADecoder_DecodeBlockInt32 +void clHCA_ReadSamples16(clHCA* hca, signed short *samples) { + const float scale_f = 32768.0f; float f; signed int s; unsigned int i, j, k; + /* PCM output is generally unused, but lib functions seem to use SIMD for f32 to s32 + round to zero */ for (i = 0; i < HCA_SUBFRAMES_PER_FRAME; i++) { for (j = 0; j < HCA_SAMPLES_PER_SUBFRAME; j++) { 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.0f) { - f = 1.0f; - } else if (f < -1.0f) { - f = -1.0f; - } - s = (signed int) (f * scale); - if ((unsigned) (s + 0x8000) & 0xFFFF0000) - s = (s >> 31) ^ 0x7FFF; - *samples++ = (signed short) s; + s = (signed int)(f * scale_f); + if (s > 32767) + s = 32767; + else if (s < -32768) + s = -32768; + *samples++ = (signed short)s; } } } @@ -311,42 +353,38 @@ void clHCA_ReadSamples16(clHCA *hca, signed short *samples) { //-------------------------------------------------- // Allocation and creation //-------------------------------------------------- -static void clHCA_constructor(clHCA *hca) { +static void clHCA_constructor(clHCA* hca) { if (!hca) return; memset(hca, 0, sizeof(*hca)); hca->is_valid = 0; - hca->comment = 0; } -static void clHCA_destructor(clHCA *hca) { - if (!hca) - return; - free(hca->comment); - hca->comment = 0; +static void clHCA_destructor(clHCA* hca) { + hca->is_valid = 0; } int clHCA_sizeof() { return sizeof(clHCA); } -void clHCA_clear(clHCA *hca) { +void clHCA_clear(clHCA* hca) { clHCA_constructor(hca); } -void clHCA_done(clHCA *hca) { +void clHCA_done(clHCA* hca) { clHCA_destructor(hca); } -clHCA * clHCA_new() { - clHCA *hca = (clHCA *) malloc(clHCA_sizeof()); +clHCA* clHCA_new() { + clHCA* hca = malloc(clHCA_sizeof()); if (hca) { clHCA_constructor(hca); } return hca; } -void clHCA_delete(clHCA *hca) { +void clHCA_delete(clHCA* hca) { clHCA_destructor(hca); free(hca); } @@ -400,12 +438,12 @@ static const unsigned char ath_base_curve[656] = { 0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFF,0xFF, }; -static void ath_init0(unsigned char *ath_curve) { +static void ath_init0(unsigned char* ath_curve) { /* disable curve */ memset(ath_curve, 0, sizeof(ath_curve[0]) * HCA_SAMPLES_PER_SUBFRAME); } -static void ath_init1(unsigned char *ath_curve, unsigned int sample_rate) { +static void ath_init1(unsigned char* ath_curve, unsigned int sample_rate) { unsigned int i, index; unsigned int acc = 0; @@ -422,25 +460,25 @@ static void ath_init1(unsigned char *ath_curve, unsigned int sample_rate) { } } -static int ath_init(unsigned char *ath_curve, int type, unsigned int sample_rate) { +static int ath_init(unsigned char* ath_curve, int type, unsigned int sample_rate) { switch (type) { - case 0: - ath_init0(ath_curve); - break; - case 1: - ath_init1(ath_curve, sample_rate); - break; - default: - return HCA_ERROR_HEADER; + case 0: + ath_init0(ath_curve); + break; + case 1: + ath_init1(ath_curve, sample_rate); + break; + default: + return HCA_ERROR_HEADER; } - return 0; + return HCA_RESULT_OK; } //-------------------------------------------------- // Encryption //-------------------------------------------------- -static void cipher_decrypt(unsigned char *cipher_table, unsigned char *data, int size) { +static void cipher_decrypt(unsigned char* cipher_table, unsigned char* data, int size) { unsigned int i; for (i = 0; i < size; i++) { @@ -448,7 +486,7 @@ static void cipher_decrypt(unsigned char *cipher_table, unsigned char *data, int } } -static void cipher_init0(unsigned char *cipher_table) { +static void cipher_init0(unsigned char* cipher_table) { unsigned int i; /* no encryption */ @@ -457,7 +495,7 @@ static void cipher_init0(unsigned char *cipher_table) { } } -static void cipher_init1(unsigned char *cipher_table) { +static void cipher_init1(unsigned char* cipher_table) { const int mul = 13; const int add = 11; unsigned int i, v = 0; @@ -473,7 +511,7 @@ static void cipher_init1(unsigned char *cipher_table) { cipher_table[0xFF] = 0xFF; } -static void cipher_init56_create_table(unsigned char *r, unsigned char key) { +static void cipher_init56_create_table(unsigned char* r, unsigned char key) { const int mul = ((key & 1) << 3) | 5; const int add = (key & 0xE) | 1; unsigned int i; @@ -485,7 +523,7 @@ static void cipher_init56_create_table(unsigned char *r, unsigned char key) { } } -static void cipher_init56(unsigned char *cipher_table, unsigned long long keycode) { +static void cipher_init56(unsigned char* cipher_table, unsigned long long keycode) { unsigned char kc[8]; unsigned char seed[16]; unsigned char base[256], base_r[16], base_c[16]; @@ -548,34 +586,36 @@ 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) { +static int cipher_init(unsigned char* cipher_table, int type, unsigned long long keycode) { if (type == 56 && !(keycode)) type = 0; switch (type) { - case 0: - cipher_init0(cipher_table); - break; - case 1: - cipher_init1(cipher_table); - break; - case 56: - cipher_init56(cipher_table, keycode); - break; - default: - return HCA_ERROR_HEADER; + case 0: + cipher_init0(cipher_table); + break; + case 1: + cipher_init1(cipher_table); + break; + case 56: + cipher_init56(cipher_table, keycode); + break; + default: + return HCA_ERROR_HEADER; } - return 0; + return HCA_RESULT_OK; } //-------------------------------------------------- // Parse //-------------------------------------------------- static unsigned int header_ceil2(unsigned int a, unsigned int b) { - return (b > 0) ? (a / b + ((a % b) ? 1 : 0)) : 0; + if (b < 1) + return 0; + return (a / b + ((a % b) ? 1 : 0)); /* lib modulo: a - (a / b * b) */ } -int clHCA_DecodeHeader(clHCA *hca, const void *data, unsigned int size) { +int clHCA_DecodeHeader(clHCA* hca, const void *data, unsigned int size) { clData br; int res; @@ -589,21 +629,21 @@ int clHCA_DecodeHeader(clHCA *hca, const void *data, unsigned int size) { bitreader_init(&br, data, size); - /* read header chunks */ + /* read header chunks (in HCA chunks must follow a fixed order) */ /* HCA base header */ if ((bitreader_peek(&br, 32) & HCA_MASK) == 0x48434100) { /* "HCA\0" */ bitreader_skip(&br, 32); - hca->version = bitreader_read(&br, 16); + hca->version = bitreader_read(&br, 16); /* lib reads as version + subversion (uses main version for feature checks) */ hca->header_size = bitreader_read(&br, 16); -#if 0 // play unknown versions anyway (confirmed to exist: v1.1/v1.2/v1.3/v2.0) - if (hca->version != 0x0101 && - hca->version != 0x0102 && - hca->version != 0x0103 && - hca->version != 0x0200) + if (hca->version != HCA_VERSION_V101 && + hca->version != HCA_VERSION_V102 && + hca->version != HCA_VERSION_V103 && + hca->version != HCA_VERSION_V200 && + hca->version != HCA_VERSION_V300) return HCA_ERROR_HEADER; -#endif + if (size < hca->header_size) return HCA_ERROR_PARAMS; @@ -625,13 +665,13 @@ int clHCA_DecodeHeader(clHCA *hca, const void *data, unsigned int size) { hca->encoder_delay = bitreader_read(&br, 16); hca->encoder_padding = bitreader_read(&br, 16); - if (!(hca->channels >= 1 && hca->channels <= HCA_MAX_CHANNELS)) + if (!(hca->channels >= HCA_MIN_CHANNELS && hca->channels <= HCA_MAX_CHANNELS)) return HCA_ERROR_HEADER; if (hca->frame_count == 0) return HCA_ERROR_HEADER; - if (!(hca->sample_rate >= 1 && hca->sample_rate <= 0x7FFFFF)) /* encoder max seems 48000 */ + if (!(hca->sample_rate >= HCA_MIN_SAMPLE_RATE && hca->sample_rate <= HCA_MAX_SAMPLE_RATE)) return HCA_ERROR_HEADER; size -= 0x10; @@ -652,8 +692,8 @@ int clHCA_DecodeHeader(clHCA *hca, const void *data, unsigned int size) { hca->base_band_count = bitreader_read(&br, 8); hca->stereo_band_count = bitreader_read(&br, 8); hca->bands_per_hfr_group = bitreader_read(&br, 8); - hca->reserved1 = bitreader_read(&br, 8); - hca->reserved2 = bitreader_read(&br, 8); + hca->ms_stereo = bitreader_read(&br, 8); + hca->reserved = bitreader_read(&br, 8); /* not actually read by lib */ size -= 0x10; } @@ -702,8 +742,8 @@ int clHCA_DecodeHeader(clHCA *hca, const void *data, unsigned int size) { hca->ath_type = bitreader_read(&br, 16); } else { - /* removed in v2.0, default in v1.x (maybe only used in v1.1, as v1.2/v1.3 set ath_type = 0) */ - hca->ath_type = (hca->version < 0x200) ? 1 : 0; + /* removed in v2.0, default in v1.x (only used in v1.1, as v1.2/v1.3 set ath_type = 0) */ + hca->ath_type = (hca->version < HCA_VERSION_V200) ? 1 : 0; } /* loop info */ @@ -757,23 +797,18 @@ int clHCA_DecodeHeader(clHCA *hca, const void *data, unsigned int size) { size -= 0x08; } else { - hca->rva_volume = 1; + hca->rva_volume = 1.0f; /* encoder volume setting is pre-applied to data, though chunk still exists in +v3.0 */ } /* comment */ if (size >= 0x05 && (bitreader_peek(&br, 32) & HCA_MASK) == 0x636F6D6D) {/* "comm" */ unsigned int i; - char *temp; bitreader_skip(&br, 32); hca->comment_len = bitreader_read(&br, 8); if (hca->comment_len > size) return HCA_ERROR_HEADER; - temp = realloc(hca->comment, hca->comment_len + 1); - if (!temp) - return HCA_ERROR_HEADER; - hca->comment = temp; for (i = 0; i < hca->comment_len; ++i) hca->comment[i] = bitreader_read(&br, 8); hca->comment[i] = '\0'; /* should be null terminated but make sure */ @@ -782,7 +817,6 @@ int clHCA_DecodeHeader(clHCA *hca, const void *data, unsigned int size) { } else { hca->comment_len = 0; - hca->comment = NULL; } /* padding info */ @@ -796,16 +830,34 @@ int clHCA_DecodeHeader(clHCA *hca, const void *data, unsigned int size) { /* extra validations */ - if (!(hca->frame_size >= 0x08 && hca->frame_size <= 0xFFFF)) /* actual max seems 0x155*channels */ + if (!(hca->frame_size >= HCA_MIN_FRAME_SIZE && hca->frame_size <= HCA_MAX_FRAME_SIZE)) /* actual max seems 0x155*channels */ return HCA_ERROR_HEADER; /* theoretically can be 0 if VBR (not seen) */ - if (!(hca->min_resolution == 1 && hca->max_resolution == 15)) + if (hca->version <= HCA_VERSION_V200) { + if (hca->min_resolution != 1 || hca->max_resolution != 15) + return HCA_ERROR_HEADER; + } + else { + if (hca->min_resolution > hca->max_resolution || hca->max_resolution > 15) /* header seems to allow 31, but later max is 15 */ + return HCA_ERROR_HEADER; + } + + + /* init state */ + + if (hca->track_count == 0) + hca->track_count = 1; /* as done by lib, can be 0 in old HCAs */ + + if (hca->track_count > hca->channels) return HCA_ERROR_HEADER; - - /* inits state */ - if (hca->track_count == 0) - hca->track_count = 1; /* default to avoid division by zero */ + /* encoded coefs (up to 128) depend in the encoder's "cutoff" hz option */ + if (hca->total_band_count > HCA_SAMPLES_PER_SUBFRAME || + hca->base_band_count > HCA_SAMPLES_PER_SUBFRAME || + hca->stereo_band_count > HCA_SAMPLES_PER_SUBFRAME || + hca->base_band_count + hca->stereo_band_count > HCA_SAMPLES_PER_SUBFRAME || + hca->bands_per_hfr_group > HCA_SAMPLES_PER_SUBFRAME) + return HCA_ERROR_HEADER; hca->hfr_group_count = header_ceil2( hca->total_band_count - hca->base_band_count - hca->stereo_band_count, @@ -819,75 +871,80 @@ int clHCA_DecodeHeader(clHCA *hca, const void *data, unsigned int size) { return res; + //todo separate into function, cleanup + //HCAHeaderUtility_GetElementTypes /* init channels */ { - int channel_types[HCA_MAX_CHANNELS] = {0}; + channel_type_t channel_types[HCA_MAX_CHANNELS] = {0}; /* part of lib struct */ unsigned int i, channels_per_track; channels_per_track = hca->channels / hca->track_count; - if (hca->stereo_band_count && channels_per_track > 1) { - int *ct = channel_types; + if (hca->stereo_band_count > 0 && channels_per_track > 1) { + channel_type_t* ct = channel_types; for (i = 0; i < hca->track_count; i++, ct += channels_per_track) { switch (channels_per_track) { - case 2: - ct[0] = STEREO_PRIMARY; - ct[1] = STEREO_SECONDARY; - break; - case 3: - ct[0] = STEREO_PRIMARY; - ct[1] = STEREO_SECONDARY; - ct[2] = DISCRETE; - break; - case 4: - ct[0] = STEREO_PRIMARY; - ct[1] = 2; - if (hca->channel_config == 0) { - ct[2] = STEREO_PRIMARY; - ct[3] = STEREO_SECONDARY; - } else { + case 2: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + break; + case 3: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + ct[2] = DISCRETE; + break; + case 4: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + if (hca->channel_config == 0) { + ct[2] = STEREO_PRIMARY; + ct[3] = STEREO_SECONDARY; + } else { + ct[2] = DISCRETE; + ct[3] = DISCRETE; + } + break; + case 5: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + ct[2] = DISCRETE; + if (hca->channel_config <= 2) { + ct[3] = STEREO_PRIMARY; + ct[4] = STEREO_SECONDARY; + } else { + ct[3] = DISCRETE; + ct[4] = DISCRETE; + } + break; + case 6: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; ct[2] = DISCRETE; ct[3] = DISCRETE; - } - break; - case 5: - ct[0] = STEREO_PRIMARY; - ct[1] = STEREO_SECONDARY; - ct[2] = DISCRETE; - if (hca->channel_config <= 2) { - ct[3] = STEREO_PRIMARY; - ct[4] = STEREO_SECONDARY; - } else { + ct[4] = STEREO_PRIMARY; + ct[5] = STEREO_SECONDARY; + break; + case 7: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + ct[2] = DISCRETE; ct[3] = DISCRETE; - ct[4] = DISCRETE; - } - break; - case 6: - ct[0] = STEREO_PRIMARY; - ct[1] = STEREO_SECONDARY; - ct[2] = DISCRETE; - ct[3] = DISCRETE; - ct[4] = STEREO_PRIMARY; - ct[5] = STEREO_SECONDARY; - break; - case 7: - ct[0] = STEREO_PRIMARY; - ct[1] = STEREO_SECONDARY; - ct[2] = DISCRETE; - ct[3] = DISCRETE; - ct[4] = STEREO_PRIMARY; - ct[5] = STEREO_SECONDARY; - ct[6] = DISCRETE; - break; - case 8: - ct[0] = STEREO_PRIMARY; - ct[1] = STEREO_SECONDARY; - ct[2] = DISCRETE; - ct[3] = DISCRETE; - ct[4] = STEREO_PRIMARY; - ct[5] = STEREO_SECONDARY; - ct[6] = STEREO_PRIMARY; - ct[7] = STEREO_SECONDARY; - break; + ct[4] = STEREO_PRIMARY; + ct[5] = STEREO_SECONDARY; + ct[6] = DISCRETE; + break; + case 8: + ct[0] = STEREO_PRIMARY; + ct[1] = STEREO_SECONDARY; + ct[2] = DISCRETE; + ct[3] = DISCRETE; + ct[4] = STEREO_PRIMARY; + ct[5] = STEREO_SECONDARY; + ct[6] = STEREO_PRIMARY; + ct[7] = STEREO_SECONDARY; + break; + default: + /* implied all 0 (DISCRETE) */ + break; } } } @@ -895,22 +952,30 @@ int clHCA_DecodeHeader(clHCA *hca, const void *data, unsigned int size) { memset(hca->channel, 0, sizeof(hca->channel)); for (i = 0; i < hca->channels; i++) { hca->channel[i].type = channel_types[i]; - hca->channel[i].coded_scalefactor_count = (channel_types[i] != 2) ? + + hca->channel[i].coded_count = (channel_types[i] != STEREO_SECONDARY) ? hca->base_band_count + hca->stereo_band_count : hca->base_band_count; - hca->channel[i].hfr_scales = &hca->channel[i].scalefactors[hca->base_band_count + hca->stereo_band_count]; } } + hca->random = HCA_DEFAULT_RANDOM; + + + //TODO: should work but untested + if (hca->ms_stereo) + return HCA_ERROR_HEADER; + if (hca->hfr_group_count > 0 && hca->version == HCA_VERSION_V300) + return HCA_ERROR_HEADER; /* clHCA is correctly initialized and decoder state reset * (keycode is not changed between calls) */ hca->is_valid = 1; - return 0; + return HCA_RESULT_OK; } -void clHCA_SetKey(clHCA *hca, unsigned long long keycode) { +void clHCA_SetKey(clHCA* hca, unsigned long long keycode) { if (!hca) return; hca->keycode = keycode; @@ -924,21 +989,19 @@ void clHCA_SetKey(clHCA *hca, unsigned long long keycode) { } } -int clHCA_TestBlock(clHCA *hca, void *data, unsigned int size) { +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; @@ -955,7 +1018,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++) { @@ -1003,8 +1089,10 @@ void clHCA_DecodeReset(clHCA * hca) { if (!hca || !hca->is_valid) return; + hca->random = HCA_DEFAULT_RANDOM; + for (i = 0; i < hca->channels; i++) { - stChannel *ch = &hca->channel[i]; + stChannel* ch = &hca->channel[i]; /* most values get overwritten during decode */ //memset(ch->intensity, 0, sizeof(ch->intensity[0]) * HCA_SUBFRAMES_PER_FRAME); @@ -1022,22 +1110,32 @@ void clHCA_DecodeReset(clHCA * hca) { //-------------------------------------------------- // Decode //-------------------------------------------------- -static int decode1_unpack_channel(stChannel *ch, clData *br, - unsigned int hfr_group_count, unsigned int packed_noise_level, const unsigned char *ath_curve); +static int unpack_scalefactors(stChannel* ch, clData* br, unsigned int hfr_group_count, unsigned int version); -static void decode2_dequantize_coefficients(stChannel *ch, clData *br); +static int unpack_intensity(stChannel* ch, clData* br, unsigned int hfr_group_count, unsigned int version); -static void decode3_reconstruct_high_frequency(stChannel *ch, - unsigned int hfr_group_count, unsigned int bands_per_hfr_group, - unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count); +static void calculate_resolution(stChannel* ch, unsigned int packed_noise_level, const unsigned char* ath_curve, + unsigned int min_resolution, unsigned int max_resolution); -static void decode4_apply_intensity_stereo(stChannel *ch, int subframe, - unsigned int usable_band_count, unsigned int base_band_count, unsigned int stereo_band_count); +static void calculate_gain(stChannel* ch); -static void decoder5_run_imdct(stChannel *ch, int subframe); +static void dequantize_coefficients(stChannel* ch, clData* br); + +static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsigned int ms_stereo, unsigned int* random_p); + +static void reconstruct_high_frequency(stChannel* ch, unsigned int hfr_group_count, unsigned int bands_per_hfr_group, + unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count, unsigned int version); + +static void apply_intensity_stereo(stChannel* ch_pair, int subframe, unsigned int base_band_count, unsigned int total_band_count); + +static void apply_ms_stereo(stChannel* ch_pair, unsigned int ms_stereo, unsigned int base_band_count, unsigned int total_band_count); + +static void imdct_transform(stChannel* ch, int subframe); -int clHCA_DecodeBlock(clHCA *hca, void *data, unsigned int size) { +/* takes HCA data and decodes all of a frame's samples */ +//hcadecoder_decode_block +int clHCA_DecodeBlock(clHCA* hca, void *data, unsigned int size) { clData br; unsigned short sync; unsigned int subframe, ch; @@ -1062,73 +1160,75 @@ int clHCA_DecodeBlock(clHCA *hca, void *data, unsigned int size) { /* unpack frame values */ { + /* lib saves this in the struct since they can stop/resume subframe decoding */ unsigned int frame_acceptable_noise_level = bitreader_read(&br, 9); unsigned int frame_evaluation_boundary = bitreader_read(&br, 7); + unsigned int packed_noise_level = (frame_acceptable_noise_level << 8) - frame_evaluation_boundary; for (ch = 0; ch < hca->channels; ch++) { - int unpack = decode1_unpack_channel(&hca->channel[ch], &br, - hca->hfr_group_count, packed_noise_level, hca->ath_curve); - if (unpack < 0) - return unpack; + int err = unpack_scalefactors(&hca->channel[ch], &br, hca->hfr_group_count, hca->version); + if (err < 0) + return err; + + unpack_intensity(&hca->channel[ch], &br, hca->hfr_group_count, hca->version); + + calculate_resolution(&hca->channel[ch], packed_noise_level, hca->ath_curve, hca->min_resolution, hca->max_resolution); + + calculate_gain(&hca->channel[ch]); } } + /* lib seems to use a state value to skip parts (unpacking/subframe N/etc) as needed */ for (subframe = 0; subframe < HCA_SUBFRAMES_PER_FRAME; subframe++) { /* unpack channel data and get dequantized spectra */ for (ch = 0; ch < hca->channels; ch++){ - decode2_dequantize_coefficients(&hca->channel[ch], &br); + dequantize_coefficients(&hca->channel[ch], &br); } - /* restore missing bands from spectra 1 */ + /* restore missing bands from spectra */ for (ch = 0; ch < hca->channels; ch++) { - decode3_reconstruct_high_frequency(&hca->channel[ch], - hca->hfr_group_count, hca->bands_per_hfr_group, - hca->stereo_band_count, hca->base_band_count, hca->total_band_count); + reconstruct_noise(&hca->channel[ch], hca->min_resolution, hca->ms_stereo, &hca->random); + + reconstruct_high_frequency(&hca->channel[ch], hca->hfr_group_count, hca->bands_per_hfr_group, + hca->stereo_band_count, hca->base_band_count, hca->total_band_count, hca->version); } - /* restore missing bands from spectra 2 */ - for (ch = 0; ch < hca->channels - 1; ch++) { - decode4_apply_intensity_stereo(&hca->channel[ch], subframe, - hca->total_band_count, hca->base_band_count, hca->stereo_band_count); + /* restore missing joint stereo bands */ + if (hca->stereo_band_count > 0) { + for (ch = 0; ch < hca->channels - 1; ch++) { + apply_intensity_stereo(&hca->channel[ch], subframe, hca->base_band_count, hca->total_band_count); + + apply_ms_stereo(&hca->channel[ch], hca->ms_stereo, hca->base_band_count, hca->total_band_count); + } } /* apply imdct */ for (ch = 0; ch < hca->channels; ch++) { - decoder5_run_imdct(&hca->channel[ch], subframe); + imdct_transform(&hca->channel[ch], subframe); } } - /* 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 0; + return br.bit; /* numbers of read bits for validations */ } //-------------------------------------------------- // Decode 1st step //-------------------------------------------------- -static const unsigned char decode1_scale_to_resolution_curve[64] = { - 0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0D,0x0D, - 0x0D,0x0D,0x0D,0x0D,0x0C,0x0C,0x0C,0x0C, - 0x0C,0x0C,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B, - 0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x09, - 0x09,0x09,0x09,0x09,0x09,0x08,0x08,0x08, - 0x08,0x08,0x08,0x07,0x06,0x06,0x05,0x04, - 0x04,0x04,0x03,0x03,0x03,0x02,0x02,0x02, - 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - /* for v1.x indexes after 56 are different, but can't be used anyway */ - //0x02,0x01,0x01,0x01,0x01,0x01,0x01,0x01, +/* curve/scale to quantized resolution */ +static const unsigned char hcadecoder_invert_table[66] = { + 14,14,14,14,14,14,13,13, 13,13,13,13,12,12,12,12, + 12,12,11,11,11,11,11,11, 10,10,10,10,10,10,10, 9, + 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 7, 6, 6, 5, 4, + 4, 4, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, + 1, 1, + /* indexes after 56 are not defined in v2.0<= (manually clamped to 1) */ }; /* scalefactor-to-scaling table, generated from sqrt(128) * (2^(53/128))^(scale_factor - 63) */ -static const unsigned int decode1_dequantizer_scaling_table_int[64] = { +static const unsigned int hcadequantizer_scaling_table_float_hex[64] = { 0x342A8D26,0x34633F89,0x3497657D,0x34C9B9BE,0x35066491,0x353311C4,0x356E9910,0x359EF532, 0x35D3CCF1,0x360D1ADF,0x363C034A,0x367A83B3,0x36A6E595,0x36DE60F5,0x371426FF,0x3745672A, 0x37838359,0x37AF3B79,0x37E97C38,0x381B8D3A,0x384F4319,0x388A14D5,0x38B7FBF0,0x38F5257D, @@ -1138,239 +1238,306 @@ static const unsigned int decode1_dequantizer_scaling_table_int[64] = { 0x3E1C6573,0x3E506334,0x3E8AD4C6,0x3EB8FBAF,0x3EF67A41,0x3F243516,0x3F5ACB94,0x3F91C3D3, 0x3FC238D2,0x400164D2,0x402C6897,0x4065B907,0x40990B88,0x40CBEC15,0x4107DB35,0x413504F3, }; -static const float *decode1_dequantizer_scaling_table = (const float *)decode1_dequantizer_scaling_table_int; +static const float* hcadequantizer_scaling_table_float = (const float*)hcadequantizer_scaling_table_float_hex; -static const unsigned int decode1_quantizer_step_size_int[16] = { - 0x00000000,0x3F2AAAAB,0x3ECCCCCD,0x3E924925,0x3E638E39,0x3E3A2E8C,0x3E1D89D9,0x3E088889, +/* in v2.0 lib index 0 is 0x00000000, but resolution 0 is only valid in v3.0 files */ +static const unsigned int hcadequantizer_range_table_float_hex[16] = { + 0x3F800000,0x3F2AAAAB,0x3ECCCCCD,0x3E924925,0x3E638E39,0x3E3A2E8C,0x3E1D89D9,0x3E088889, 0x3D842108,0x3D020821,0x3C810204,0x3C008081,0x3B804020,0x3B002008,0x3A801002,0x3A000801, }; -static const float *decode1_quantizer_step_size = (const float *)decode1_quantizer_step_size_int; +static const float* hcadequantizer_range_table_float = (const float*)hcadequantizer_range_table_float_hex; -static int decode1_unpack_channel(stChannel *ch, clData *br, - unsigned int hfr_group_count, unsigned int packed_noise_level, const unsigned char *ath_curve) { - unsigned int i; - const unsigned int csf_count = ch->coded_scalefactor_count; +/* get scale indexes to normalize dequantized coefficients */ +static int unpack_scalefactors(stChannel* ch, clData* br, unsigned int hfr_group_count, unsigned int version) { + int i; + unsigned int cs_count = ch->coded_count; + unsigned int extra_count; + unsigned char delta_bits = bitreader_read(br, 3); - - /* read scalefactors */ - { - /* scale indexes to normalize dequantized coefficients */ - unsigned char scalefactor_delta_bits = bitreader_read(br, 3); - if (scalefactor_delta_bits >= 6) { - /* normal scalefactors */ - for (i = 0; i < csf_count; i++) { - ch->scalefactors[i] = bitreader_read(br, 6); - } - } - else if (scalefactor_delta_bits > 0) { - /* delta scalefactors */ - const unsigned char expected_delta = (1 << scalefactor_delta_bits) - 1; - const unsigned char extra_delta = expected_delta >> 1; - unsigned char scalefactor_prev = bitreader_read(br, 6); - - ch->scalefactors[0] = scalefactor_prev; - for (i = 1; i < csf_count; i++) { - unsigned char delta = bitreader_read(br, scalefactor_delta_bits); - - if (delta != expected_delta) { - /* 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 HCA_ERROR_UNPACK; - } - - scalefactor_prev += delta - extra_delta; - } else { - scalefactor_prev = bitreader_read(br, 6); - } - ch->scalefactors[i] = scalefactor_prev; - } - } - else { - /* no scalefactors */ - memset(ch->scalefactors, 0, sizeof(ch->scalefactors[0]) * HCA_SAMPLES_PER_SUBFRAME); - } - } - - if (ch->type == STEREO_SECONDARY) { - /* read intensity */ - unsigned char intensity_value = bitreader_peek(br, 4); - - ch->intensity[0] = intensity_value; - if (intensity_value < 15) { - for (i = 0; i < HCA_SUBFRAMES_PER_FRAME; i++) { - ch->intensity[i] = bitreader_read(br, 4); - } - } - /* 15 may be an invalid value? */ - //else { - // return HCA_ERROR_INSENSITY; - //} + /* added in v3.0 */ + if (ch->type == STEREO_SECONDARY || hfr_group_count <= 0 || version <= HCA_VERSION_V200) { + extra_count = 0; } else { - /* read hfr scalefactors */ - for (i = 0; i < hfr_group_count; i++) { - ch->hfr_scales[i] = bitreader_read(br, 6); - } + extra_count = hfr_group_count; + cs_count = cs_count + extra_count; + + /* just in case */ + if (cs_count > HCA_SAMPLES_PER_SUBFRAME) + return HCA_ERROR_UNPACK; } - /* calculate resolutions */ - { - /* resolution determines the range of values per encoded spectra, - * using codebooks for lower resolutions during dequantization */ - for (i = 0; i < csf_count; i++) { - unsigned char new_resolution = 0; - unsigned char scalefactor = ch->scalefactors[i]; - if (scalefactor > 0) { - int noise_level = ath_curve[i] + ((packed_noise_level + i) >> 8); - int curve_position = noise_level - ((5 * scalefactor) >> 1) + 1; + /* lib does check that cs_count is 2+ in fixed/delta case, but doesn't seem to affect anything */ + if (delta_bits >= 6) { + /* fixed scalefactors */ + for (i = 0; i < cs_count; i++) { + ch->scalefactors[i] = bitreader_read(br, 6); + } + } + else if (delta_bits > 0) { + /* delta scalefactors */ + const unsigned char expected_delta = (1 << delta_bits) - 1; + unsigned char value = bitreader_read(br, 6); - /* curve values can be simplified by clamping position to (0,58) and making - * scale_table[0] = 15, table[58] = 1 (like VGAudio does) */ - if (curve_position < 0) - new_resolution = 15; - else if (curve_position >= 57) - new_resolution = 1; - else - new_resolution = decode1_scale_to_resolution_curve[curve_position]; + ch->scalefactors[0] = value; + for (i = 1; i < cs_count; i++) { + unsigned char delta = bitreader_read(br, delta_bits); + + if (delta == expected_delta) { + value = bitreader_read(br, 6); /* encoded */ } - ch->resolution[i] = new_resolution; - } - memset(&ch->resolution[csf_count], 0, sizeof(ch->resolution[0]) * (HCA_SAMPLES_PER_SUBFRAME - csf_count)); - } + else { + /* may happen with bad keycodes, scalefactors must be 6b indexes */ + int scalefactor_test = (int)value + ((int)delta - (int)(expected_delta >> 1)); + if (scalefactor_test < 0 || scalefactor_test >= 64) { + return HCA_ERROR_UNPACK; + } - /* calculate gain */ - { - /* get actual scales to dequantize */ - for (i = 0; i < csf_count; i++) { - float scalefactor_scale = decode1_dequantizer_scaling_table[ ch->scalefactors[i] ]; - float resolution_scale = decode1_quantizer_step_size[ ch->resolution[i] ]; - ch->gain[i] = scalefactor_scale * resolution_scale; + value = value - (expected_delta >> 1) + delta; /* differential */ + value = value & 0x3F; /* v3.0 lib */ + + //todo as negative better? (may roll otherwise?) + //if (value >= 64) + // return HCA_ERROR_UNPACK; + } + ch->scalefactors[i] = value; + } + } + else { + /* no scalefactors */ + for (i = 0; i < HCA_SAMPLES_PER_SUBFRAME; i++) { + ch->scalefactors[i] = 0; } } - return 0; + /* set derived HFR scales for v3.0 */ + for (i = 0; i < extra_count; i++) { + ch->scalefactors[HCA_SAMPLES_PER_SUBFRAME - 1 - i] = ch->scalefactors[cs_count - i]; + } + + return HCA_RESULT_OK; +} + +/* read intensity (for joint stereo R) or v2.0 high frequency scales (for regular channels) */ +static int unpack_intensity(stChannel* ch, clData* br, unsigned int hfr_group_count, unsigned int version) { + int i; + + if (ch->type == STEREO_SECONDARY) { + /* read subframe intensity for channel pair (peek first for valid values, not sure why not consumed) */ + if (version <= HCA_VERSION_V200) { + unsigned char value = bitreader_peek(br, 4); + + ch->intensity[0] = value; + if (value < 15) { + bitreader_skip(br, 4); + for (i = 1; i < HCA_SUBFRAMES_PER_FRAME; i++) { + ch->intensity[i] = bitreader_read(br, 4); + } + } + /* 15 may be an invalid value? index 15 is 0, but may imply "reuse last subframe's intensity". + * no games seem to use 15 though */ + //else { + // return HCA_ERROR_UNPACK; + //} + } + else { + unsigned char value = bitreader_peek(br, 4); + unsigned char delta_bits; + + if (value < 15) { + bitreader_skip(br, 4); + + delta_bits = bitreader_read(br, 2); /* +1 */ + + ch->intensity[0] = value; + if (delta_bits == 3) { /* 3+1 = 4b */ + /* fixed intensities */ + for (i = 1; i < HCA_SUBFRAMES_PER_FRAME; i++) { + ch->intensity[i] = bitreader_read(br, 4); + } + } + else { + /* delta intensities */ + unsigned char bmax = (2 << delta_bits) - 1; + unsigned char bits = delta_bits + 1; + + for (i = 1; i < HCA_SUBFRAMES_PER_FRAME; i++) { + unsigned char delta = bitreader_read(br, bits); + if (delta == bmax) { + value = bitreader_read(br, 4); /* encoded */ + } + else { + value = value - (bmax >> 1) + delta; /* differential */ + if (value > 15) //todo check + return HCA_ERROR_UNPACK; /* not done in lib */ + } + + ch->intensity[i] = value; + } + } + } + else { + bitreader_skip(br, 4); + for (i = 0; i < HCA_SUBFRAMES_PER_FRAME; i++) { + ch->intensity[i] = 7; + } + } + } + } + else { + /* read high frequency scalefactors (v3.0 uses derived values in unpack_scalefactors instead) */ + if (version <= HCA_VERSION_V200) { + /* pointer in v2.0 lib for v2.0 files is base+stereo bands, while v3.0 lib for v2.0 files + * is last HFR. No output difference but v3.0 files need that to handle HFR */ + //unsigned char* hfr_scales = &ch->scalefactors[base_band_count + stereo_band_count]; /* v2.0 lib */ + unsigned char* hfr_scales = &ch->scalefactors[128 - hfr_group_count]; /* v3.0 lib */ + + for (i = 0; i < hfr_group_count; i++) { + hfr_scales[i] = bitreader_read(br, 6); + } + } + } + + return HCA_RESULT_OK; +} + +/* get resolutions, that determines range of values per encoded spectrum coefficients */ +static void calculate_resolution(stChannel* ch, unsigned int packed_noise_level, const unsigned char* ath_curve, unsigned int min_resolution, unsigned int max_resolution) { + int i; + unsigned int cr_count = ch->coded_count; + unsigned int noise_count = 0; + unsigned int valid_count = 0; + + for (i = 0; i < cr_count; i++) { + unsigned char new_resolution = 0; + unsigned char scalefactor = ch->scalefactors[i]; + + if (scalefactor > 0) { + /* curve values are 0 in v1.2>= so ath_curve is actually removed in CRI's code */ + int noise_level = ath_curve[i] + ((packed_noise_level + i) >> 8); + int curve_position = noise_level + 1 - ((5 * scalefactor) >> 1); + + /* v2.0<= allows max 56 + sets rest to 1, while v3.0 table has 1 for 57..65 and + * clamps to min_resolution below, so v2.0 files are still supported */ + if (curve_position < 0) { + new_resolution = 15; + } + else if (curve_position <= 65) { + new_resolution = hcadecoder_invert_table[curve_position]; + } + else { + new_resolution = 0; + } + + /* added in v3.0 (before, min_resolution was always 1) */ + if (new_resolution > max_resolution) + new_resolution = max_resolution; + else if (new_resolution < min_resolution) + new_resolution = min_resolution; + + /* save resolution 0 (not encoded) indexes (from 0..N), and regular indexes (from N..0) */ + if (new_resolution < 1) { + ch->noises[noise_count] = i; + noise_count++; + } + else { + ch->noises[HCA_SAMPLES_PER_SUBFRAME - 1 - valid_count] = i; + valid_count++; + } + } + ch->resolution[i] = new_resolution; + } + + ch->noise_count = noise_count; + ch->valid_count = valid_count; + + memset(&ch->resolution[cr_count], 0, sizeof(ch->resolution[0]) * (HCA_SAMPLES_PER_SUBFRAME - cr_count)); +} + +/* get actual scales to dequantize based on saved scalefactors */ +// HCADequantizer_CalculateGain +static void calculate_gain(stChannel* ch) { + int i; + unsigned int cg_count = ch->coded_count; + + for (i = 0; i < cg_count; i++) { + float scalefactor_scale = hcadequantizer_scaling_table_float[ ch->scalefactors[i] ]; + float resolution_scale = hcadequantizer_range_table_float[ ch->resolution[i] ]; + ch->gain[i] = scalefactor_scale * resolution_scale; + } } //-------------------------------------------------- // Decode 2nd step //-------------------------------------------------- -static const unsigned char decode2_quantized_spectrum_max_bits[16] = { - 0,2,3,3,4,4,4,4,5,6,7,8,9,10,11,12 +/* coded resolution to max bits */ +static const unsigned char hcatbdecoder_max_bit_table[16] = { + 0,2,3,3,4,4,4,4, 5,6,7,8,9,10,11,12 }; -static const unsigned char decode2_quantized_spectrum_bits[128] = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,2,2,0,0,0,0,0,0,0,0,0,0,0,0, - 2,2,2,2,2,2,3,3,0,0,0,0,0,0,0,0, - 2,2,3,3,3,3,3,3,0,0,0,0,0,0,0,0, - 3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4, - 3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4, - 3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4, - 3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4, +/* bits used for quant codes */ +static const unsigned char hcatbdecoder_read_bit_table[128] = { + 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, + 1,1,2,2,0,0,0,0, 0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,3,3, 0,0,0,0,0,0,0,0, + 2,2,3,3,3,3,3,3, 0,0,0,0,0,0,0,0, + 3,3,3,3,3,3,3,3, 3,3,3,3,3,3,4,4, + 3,3,3,3,3,3,3,3, 3,3,4,4,4,4,4,4, + 3,3,3,3,3,3,4,4, 4,4,4,4,4,4,4,4, + 3,3,4,4,4,4,4,4, 4,4,4,4,4,4,4,4, }; -static const float decode2_quantized_spectrum_value[128] = { - +0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0, - +0,+0,+1,-1,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0,+0, - +0,+0,+1,+1,-1,-1,+2,-2,+0,+0,+0,+0,+0,+0,+0,+0, - +0,+0,+1,-1,+2,-2,+3,-3,+0,+0,+0,+0,+0,+0,+0,+0, - +0,+0,+1,+1,-1,-1,+2,+2,-2,-2,+3,+3,-3,-3,+4,-4, - +0,+0,+1,+1,-1,-1,+2,+2,-2,-2,+3,-3,+4,-4,+5,-5, - +0,+0,+1,+1,-1,-1,+2,-2,+3,-3,+4,-4,+5,-5,+6,-6, - +0,+0,+1,-1,+2,-2,+3,-3,+4,-4,+5,-5,+6,-6,+7,-7, +/* code to quantized spectrum value */ +static const float hcatbdecoder_read_val_table[128] = { + +0.0f,+0.0f,+0.0f,+0.0f,+0.0f,+0.0f,+0.0f,+0.0f, +0.0f,+0.0f,+0.0f,+0.0f,+0.0f,+0.0f,+0.0f,+0.0f, + +0.0f,+0.0f,+1.0f,-1.0f,+0.0f,+0.0f,+0.0f,+0.0f, +0.0f,+0.0f,+0.0f,+0.0f,+0.0f,+0.0f,+0.0f,+0.0f, + +0.0f,+0.0f,+1.0f,+1.0f,-1.0f,-1.0f,+2.0f,-2.0f, +0.0f,+0.0f,+0.0f,+0.0f,+0.0f,+0.0f,+0.0f,+0.0f, + +0.0f,+0.0f,+1.0f,-1.0f,+2.0f,-2.0f,+3.0f,-3.0f, +0.0f,+0.0f,+0.0f,+0.0f,+0.0f,+0.0f,+0.0f,+0.0f, + +0.0f,+0.0f,+1.0f,+1.0f,-1.0f,-1.0f,+2.0f,+2.0f, -2.0f,-2.0f,+3.0f,+3.0f,-3.0f,-3.0f,+4.0f,-4.0f, + +0.0f,+0.0f,+1.0f,+1.0f,-1.0f,-1.0f,+2.0f,+2.0f, -2.0f,-2.0f,+3.0f,-3.0f,+4.0f,-4.0f,+5.0f,-5.0f, + +0.0f,+0.0f,+1.0f,+1.0f,-1.0f,-1.0f,+2.0f,-2.0f, +3.0f,-3.0f,+4.0f,-4.0f,+5.0f,-5.0f,+6.0f,-6.0f, + +0.0f,+0.0f,+1.0f,-1.0f,+2.0f,-2.0f,+3.0f,-3.0f, +4.0f,-4.0f,+5.0f,-5.0f,+6.0f,-6.0f,+7.0f,-7.0f, }; -static void decode2_dequantize_coefficients(stChannel *ch, clData *br) { - unsigned int i; - const unsigned int csf_count = ch->coded_scalefactor_count; +/* read spectral coefficients in the bitstream */ +static void dequantize_coefficients(stChannel* ch, clData* br) { + int i; + unsigned int cc_count = ch->coded_count; - - for (i = 0; i < csf_count; i++) { + for (i = 0; i < cc_count; i++) { float qc; unsigned char resolution = ch->resolution[i]; - unsigned char bits = decode2_quantized_spectrum_max_bits[resolution]; + unsigned char bits = hcatbdecoder_max_bit_table[resolution]; unsigned int code = bitreader_read(br, bits); - /* read spectral coefficients */ - if (resolution < 8) { - /* use prefix codebooks for lower resolutions */ - code += resolution << 4; - bitreader_skip(br, decode2_quantized_spectrum_bits[code] - bits); - qc = decode2_quantized_spectrum_value[code]; - } - else { + if (resolution > 7) { /* parse values in sign-magnitude form (lowest bit = sign) */ - int signed_code = (1 - ((code & 1) << 1)) * (code >> 1); /* move sign */ + int signed_code = (1 - ((code & 1) << 1)) * (code >> 1); /* move sign from low to up */ if (signed_code == 0) bitreader_skip(br, -1); /* zero uses one less bit since it has no sign */ qc = (float)signed_code; } + else { + /* use prefix codebooks for lower resolutions */ + int index = (resolution << 4) + code; + int skip = hcatbdecoder_read_bit_table[index] - bits; + bitreader_skip(br, skip); + qc = hcatbdecoder_read_val_table[index]; + } /* dequantize coef with gain */ ch->spectra[i] = ch->gain[i] * qc; } /* clean rest of spectra */ - memset(&ch->spectra[csf_count], 0, sizeof(ch->spectra[0]) * (HCA_SAMPLES_PER_SUBFRAME - csf_count)); + memset(&ch->spectra[cc_count], 0, sizeof(ch->spectra[0]) * (HCA_SAMPLES_PER_SUBFRAME - cc_count)); } + //-------------------------------------------------- // Decode 3rd step //-------------------------------------------------- -static const unsigned int decode3_scale_conversion_table_int[128] = { - 0x00000000,0x00000000,0x32A0B051,0x32D61B5E,0x330EA43A,0x333E0F68,0x337D3E0C,0x33A8B6D5, - 0x33E0CCDF,0x3415C3FF,0x34478D75,0x3484F1F6,0x34B123F6,0x34EC0719,0x351D3EDA,0x355184DF, - 0x358B95C2,0x35B9FCD2,0x35F7D0DF,0x36251958,0x365BFBB8,0x36928E72,0x36C346CD,0x370218AF, - 0x372D583F,0x3766F85B,0x3799E046,0x37CD078C,0x3808980F,0x38360094,0x38728177,0x38A18FAF, - 0x38D744FD,0x390F6A81,0x393F179A,0x397E9E11,0x39A9A15B,0x39E2055B,0x3A16942D,0x3A48A2D8, - 0x3A85AAC3,0x3AB21A32,0x3AED4F30,0x3B1E196E,0x3B52A81E,0x3B8C57CA,0x3BBAFF5B,0x3BF9295A, - 0x3C25FED7,0x3C5D2D82,0x3C935A2B,0x3CC4563F,0x3D02CD87,0x3D2E4934,0x3D68396A,0x3D9AB62B, - 0x3DCE248C,0x3E0955EE,0x3E36FD92,0x3E73D290,0x3EA27043,0x3ED87039,0x3F1031DC,0x3F40213B, - - 0x3F800000,0x3FAA8D26,0x3FE33F89,0x4017657D,0x4049B9BE,0x40866491,0x40B311C4,0x40EE9910, - 0x411EF532,0x4153CCF1,0x418D1ADF,0x41BC034A,0x41FA83B3,0x4226E595,0x425E60F5,0x429426FF, - 0x42C5672A,0x43038359,0x432F3B79,0x43697C38,0x439B8D3A,0x43CF4319,0x440A14D5,0x4437FBF0, - 0x4475257D,0x44A3520F,0x44D99D16,0x4510FA4D,0x45412C4D,0x4580B1ED,0x45AB7A3A,0x45E47B6D, - 0x461837F0,0x464AD226,0x46871F62,0x46B40AAF,0x46EFE4BA,0x471FD228,0x4754F35B,0x478DDF04, - 0x47BD08A4,0x47FBDFED,0x4827CD94,0x485F9613,0x4894F4F0,0x48C67991,0x49043A29,0x49302F0E, - 0x496AC0C7,0x499C6573,0x49D06334,0x4A0AD4C6,0x4A38FBAF,0x4A767A41,0x4AA43516,0x4ADACB94, - 0x4B11C3D3,0x4B4238D2,0x4B8164D2,0x4BAC6897,0x4BE5B907,0x4C190B88,0x4C4BEC15,0x00000000, -}; -static const float *decode3_scale_conversion_table = (const float *)decode3_scale_conversion_table_int; - -static void decode3_reconstruct_high_frequency(stChannel *ch, - unsigned int hfr_group_count, unsigned int bands_per_hfr_group, - unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count) { - if (ch->type == STEREO_SECONDARY) - return; - if (bands_per_hfr_group == 0) /* not in v1.x */ - return; - - { - unsigned int group, i; - unsigned int start_band = stereo_band_count + base_band_count; - unsigned int highband = start_band; - unsigned int lowband = start_band - 1; - - for (group = 0; group < hfr_group_count; group++) { - for (i = 0; i < bands_per_hfr_group && highband < total_band_count; i++) { - unsigned int sc_index = ch->hfr_scales[group] - ch->scalefactors[lowband] + 64; - ch->spectra[highband] = decode3_scale_conversion_table[sc_index] * ch->spectra[lowband]; - highband++; - lowband--; - } - } - - ch->spectra[HCA_SAMPLES_PER_SUBFRAME - 1] = 0; /* last spectral coefficient should be 0 */ - } -} - -//-------------------------------------------------- -// Decode 4th step -//-------------------------------------------------- -static const unsigned int decode4_intensity_ratio_table_int[80] = { - 0x40000000,0x3FEDB6DB,0x3FDB6DB7,0x3FC92492,0x3FB6DB6E,0x3FA49249,0x3F924925,0x3F800000, - 0x3F5B6DB7,0x3F36DB6E,0x3F124925,0x3EDB6DB7,0x3E924925,0x3E124925,0x00000000,0x00000000, - /* v2.0 seems to define indexes over 15, but intensity is packed in 4b thus unused */ +/* in lib this table does start with a single 0.0 and adds + 63 below + * (other decoders start with two 0.0 and add + 64 below, that should be equivalent) */ +static const unsigned int hcadecoder_scale_conversion_table_hex[128] = { 0x00000000,0x32A0B051,0x32D61B5E,0x330EA43A,0x333E0F68,0x337D3E0C,0x33A8B6D5,0x33E0CCDF, 0x3415C3FF,0x34478D75,0x3484F1F6,0x34B123F6,0x34EC0719,0x351D3EDA,0x355184DF,0x358B95C2, 0x35B9FCD2,0x35F7D0DF,0x36251958,0x365BFBB8,0x36928E72,0x36C346CD,0x370218AF,0x372D583F, @@ -1378,27 +1545,152 @@ static const unsigned int decode4_intensity_ratio_table_int[80] = { 0x390F6A81,0x393F179A,0x397E9E11,0x39A9A15B,0x39E2055B,0x3A16942D,0x3A48A2D8,0x3A85AAC3, 0x3AB21A32,0x3AED4F30,0x3B1E196E,0x3B52A81E,0x3B8C57CA,0x3BBAFF5B,0x3BF9295A,0x3C25FED7, 0x3C5D2D82,0x3C935A2B,0x3CC4563F,0x3D02CD87,0x3D2E4934,0x3D68396A,0x3D9AB62B,0x3DCE248C, - 0x3E0955EE,0x3E36FD92,0x3E73D290,0x3EA27043,0x3ED87039,0x3F1031DC,0x3F40213B,0x00000000, -}; -static const float *decode4_intensity_ratio_table = (const float *)decode4_intensity_ratio_table_int; + 0x3E0955EE,0x3E36FD92,0x3E73D290,0x3EA27043,0x3ED87039,0x3F1031DC,0x3F40213B,0x3F800000, -static void decode4_apply_intensity_stereo(stChannel *ch_pair, int subframe, - unsigned int total_band_count, unsigned int base_band_count, unsigned int stereo_band_count) { - if (ch_pair[0].type != STEREO_PRIMARY) + 0x3FAA8D26,0x3FE33F89,0x4017657D,0x4049B9BE,0x40866491,0x40B311C4,0x40EE9910,0x411EF532, + 0x4153CCF1,0x418D1ADF,0x41BC034A,0x41FA83B3,0x4226E595,0x425E60F5,0x429426FF,0x42C5672A, + 0x43038359,0x432F3B79,0x43697C38,0x439B8D3A,0x43CF4319,0x440A14D5,0x4437FBF0,0x4475257D, + 0x44A3520F,0x44D99D16,0x4510FA4D,0x45412C4D,0x4580B1ED,0x45AB7A3A,0x45E47B6D,0x461837F0, + 0x464AD226,0x46871F62,0x46B40AAF,0x46EFE4BA,0x471FD228,0x4754F35B,0x478DDF04,0x47BD08A4, + 0x47FBDFED,0x4827CD94,0x485F9613,0x4894F4F0,0x48C67991,0x49043A29,0x49302F0E,0x496AC0C7, + 0x499C6573,0x49D06334,0x4A0AD4C6,0x4A38FBAF,0x4A767A41,0x4AA43516,0x4ADACB94,0x4B11C3D3, + 0x4B4238D2,0x4B8164D2,0x4BAC6897,0x4BE5B907,0x4C190B88,0x4C4BEC15,0x00000000,0x00000000, +}; +static const float* hcadecoder_scale_conversion_table = (const float*)hcadecoder_scale_conversion_table_hex; + +/* recreate resolution 0 coefs (not encoded) with pseudo-random noise based on + * other coefs/scales (probably similar to AAC's perceptual noise substitution) */ +static void reconstruct_noise(stChannel* ch, unsigned int min_resolution, unsigned int ms_stereo, unsigned int* random_p) { + if (min_resolution > 0) /* added in v3.0 */ return; - if (stereo_band_count == 0) + if (ch->valid_count <= 0 || ch->noise_count <= 0) + return; + if (!(!ms_stereo || ch->type == STEREO_PRIMARY)) return; { - float ratio_l = decode4_intensity_ratio_table[ ch_pair[1].intensity[subframe] ]; - float ratio_r = ratio_l - 2.0f; - float *sp_l = ch_pair[0].spectra; - float *sp_r = ch_pair[1].spectra; - unsigned int band; + int i; + int random_index, noise_index, valid_index, sf_noise, sf_valid, sc_index; + unsigned int random = *random_p; + + for (i = 0; i < ch->noise_count; i++) { + random = 0x343FD * random + 0x269EC3; /* typical rand() */ + + random_index = HCA_SAMPLES_PER_SUBFRAME - ch->valid_count + (((random & 0x7FFF) * ch->valid_count) >> 15); /* can't go over 128 */ + + /* points to next resolution 0 index and random non-resolution 0 index */ + noise_index = ch->noises[i]; + valid_index = ch->noises[random_index]; + + /* get final scale index */ + sf_noise = ch->scalefactors[noise_index]; + sf_valid = ch->scalefactors[valid_index]; + sc_index = (sf_noise - sf_valid + 62) & ~((sf_noise - sf_valid + 62) >> 31); + + ch->spectra[noise_index] = hcadecoder_scale_conversion_table[sc_index] * ch->spectra[valid_index]; + } + + *random_p = random; /* lib saves this in the bitreader, maybe for simplified passing around */ + } +} + +/* recreate missing coefs in high bands based on lower bands (probably similar to AAC's spectral band replication) */ +static void reconstruct_high_frequency(stChannel* ch, unsigned int hfr_group_count, unsigned int bands_per_hfr_group, + unsigned int stereo_band_count, unsigned int base_band_count, unsigned int total_band_count, unsigned int version) { + if (bands_per_hfr_group == 0) /* added in v2.0, skipped in v2.0 files with 0 bands too */ + return; + if (ch->type == STEREO_SECONDARY) + return; + + { + int i; + int group, group_limit; + int start_band = stereo_band_count + base_band_count; + int highband = start_band; + int lowband = start_band - 1; + int sc_index; + //unsigned char* hfr_scales = &ch->scalefactors[base_band_count + stereo_band_count]; /* v2.0 lib */ + unsigned char* hfr_scales = &ch->scalefactors[128 - hfr_group_count]; /* v3.0 lib */ + + if (version <= HCA_VERSION_V200) { + group_limit = hfr_group_count; + } + else { + group_limit = (hfr_group_count >= 0) ? hfr_group_count : hfr_group_count + 1; /* ??? */ + group_limit = group_limit >> 1; + } + + for (group = 0; group < hfr_group_count; group++) { + int lowband_sub = (group < group_limit) ? 1 : 0; /* move lowband towards 0 until group reachs limit */ + + for (i = 0; i < bands_per_hfr_group; i++) { + if (highband >= total_band_count || lowband < 0) + break; + + sc_index = hfr_scales[group] - ch->scalefactors[lowband] + 63; + sc_index = sc_index & ~(sc_index >> 31); /* clamped in v3.0 lib (in theory 6b sf are 0..128) */ + + ch->spectra[highband] = hcadecoder_scale_conversion_table[sc_index] * ch->spectra[lowband]; + + highband += 1; + lowband -= lowband_sub; + } + } + + /* last spectrum coefficient is 0 (normally highband = 128, but perhaps could 'break' before) */ + ch->spectra[highband - 1] = 0.0f; + } +} + +//-------------------------------------------------- +// Decode 4th step +//-------------------------------------------------- +/* index to scale */ +static const unsigned int hcadecoder_intensity_ratio_table_hex[16] = { /* max 4b */ + 0x40000000,0x3FEDB6DB,0x3FDB6DB7,0x3FC92492,0x3FB6DB6E,0x3FA49249,0x3F924925,0x3F800000, + 0x3F5B6DB7,0x3F36DB6E,0x3F124925,0x3EDB6DB7,0x3E924925,0x3E124925,0x00000000,0x00000000, +}; +static const float* hcadecoder_intensity_ratio_table = (const float*)hcadecoder_intensity_ratio_table_hex; + +/* restore L/R bands based on channel coef + panning */ +static void apply_intensity_stereo(stChannel* ch_pair, int subframe, unsigned int base_band_count, unsigned int total_band_count) { + if (ch_pair[0].type != STEREO_PRIMARY) + return; + + { + int band; + float ratio_l = hcadecoder_intensity_ratio_table[ ch_pair[1].intensity[subframe] ]; + float ratio_r = 2.0f - ratio_l; /* correct, though other decoders substract 2.0 (it does use 'fsubr 2.0' and such) */ + float* sp_l = ch_pair[0].spectra; + float* sp_r = ch_pair[1].spectra; for (band = base_band_count; band < total_band_count; band++) { - sp_r[band] = sp_l[band] * ratio_r; - sp_l[band] = sp_l[band] * ratio_l; + float coef_l = sp_l[band] * ratio_l; + float coef_r = sp_l[band] * ratio_r; + sp_l[band] = coef_l; + sp_r[band] = coef_r; + } + } +} + +/* restore L/R bands based on mid channel + side differences */ +static void apply_ms_stereo(stChannel* ch_pair, unsigned int ms_stereo, unsigned int base_band_count, unsigned int total_band_count) { + if (!ms_stereo) /* added in v3.0 */ + return; + if (ch_pair[0].type != STEREO_PRIMARY) + return; + + { + int band; + const float ratio = 0.70710676908493; /* 0x3F3504F3 */ + float* sp_l = ch_pair[0].spectra; + float* sp_r = ch_pair[1].spectra; + + for (band = base_band_count; band < total_band_count; band++) { + float coef_l = (sp_l[band] + sp_r[band]) * ratio; + float coef_r = (sp_l[band] - sp_r[band]) * ratio; + sp_l[band] = coef_l; + sp_r[band] = coef_r; } } } @@ -1406,7 +1698,7 @@ static void decode4_apply_intensity_stereo(stChannel *ch_pair, int subframe, //-------------------------------------------------- // Decode 5th step //-------------------------------------------------- -static const unsigned int decode5_sin_tables_int[7][64] = { +static const unsigned int sin_tables_hex[7][64] = { { 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, 0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75,0x3DA73D75, @@ -1472,8 +1764,7 @@ static const unsigned int decode5_sin_tables_int[7][64] = { 0x3F44E3F5,0x3F42DE29,0x3F40D0DA,0x3F3EBC1B,0x3F3CA003,0x3F3A7CA4,0x3F385216,0x3F36206C, } }; - -static const unsigned int decode5_cos_tables_int[7][64]={ +static const unsigned int cos_tables_hex[7][64]={ { 0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4, 0x3D0A8BD4,0xBD0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0xBD0A8BD4,0x3D0A8BD4,0x3D0A8BD4,0xBD0A8BD4, @@ -1541,7 +1832,7 @@ static const unsigned int decode5_cos_tables_int[7][64]={ }; /* HCA window function, close to a KBD window with an alpha of around 3.82 (similar to AAC/Vorbis) */ -static const unsigned int decode5_imdct_window_int[128] = { +static const unsigned int hcaimdct_window_float_hex[128] = { 0x3A3504F0,0x3B0183B8,0x3B70C538,0x3BBB9268,0x3C04A809,0x3C308200,0x3C61284C,0x3C8B3F17, 0x3CA83992,0x3CC77FBD,0x3CE91110,0x3D0677CD,0x3D198FC4,0x3D2DD35C,0x3D434643,0x3D59ECC1, 0x3D71CBA8,0x3D85741E,0x3D92A413,0x3DA078B4,0x3DAEF522,0x3DBE1C9E,0x3DCDF27B,0x3DDE7A1D, @@ -1560,65 +1851,66 @@ static const unsigned int decode5_imdct_window_int[128] = { 0xBF7FA32E,0xBF7FB57B,0xBF7FC4F6,0xBF7FD1ED,0xBF7FDCAD,0xBF7FE579,0xBF7FEC90,0xBF7FF22E, 0xBF7FF688,0xBF7FF9D0,0xBF7FFC32,0xBF7FFDDA,0xBF7FFEED,0xBF7FFF8F,0xBF7FFFDF,0xBF7FFFFC, }; -static const float *decode5_imdct_window = (const float *)decode5_imdct_window_int; +static const float* hcaimdct_window_float = (const float*)hcaimdct_window_float_hex; -static void decoder5_run_imdct(stChannel *ch, int subframe) { +/* apply DCT-IV to dequantized spectra to get final samples */ +//HCAIMDCT_Transform +static void imdct_transform(stChannel* ch, int subframe) { static const unsigned int size = HCA_SAMPLES_PER_SUBFRAME; static const unsigned int half = HCA_SAMPLES_PER_SUBFRAME / 2; static const unsigned int mdct_bits = HCA_MDCT_BITS; + unsigned int i, j, k; + /* This IMDCT (supposedly standard) is all too crafty for me to simplify, see VGAudio (Mdct.Dct4). */ - /* apply DCT-IV to dequantized spectra */ + /* pre-pre-rotation(?) */ { - unsigned int i, j, k; - unsigned int count1a, count2a, count1b, count2b; - const float *temp1a, *temp1b; - float *temp2a, *temp2b; + unsigned int count1 = 1; + unsigned int count2 = half; + float* temp1 = ch->spectra; + float* temp2 = ch->temp; - /* this is all too crafty for me to simplify, see VGAudio (Mdct.Dct4) */ - - temp1a = ch->spectra; - temp2a = ch->temp; - count1a = 1; - count2a = half; for (i = 0; i < mdct_bits; i++) { - float *swap; - float *d1 = &temp2a[0]; - float *d2 = &temp2a[count2a]; + float* swap; + float* d1 = &temp2[0]; + float* d2 = &temp2[count2]; - for (j = 0; j < count1a; j++) { - for (k = 0; k < count2a; k++) { - float a = *(temp1a++); - float b = *(temp1a++); - *(d1++) = b + a; + for (j = 0; j < count1; j++) { + for (k = 0; k < count2; k++) { + float a = *(temp1++); + float b = *(temp1++); + *(d1++) = a + b; *(d2++) = a - b; } - d1 += count2a; - d2 += count2a; + d1 += count2; + d2 += count2; } - swap = (float*) temp1a - HCA_SAMPLES_PER_SUBFRAME; /* move spectra/temp to beginning */ - temp1a = temp2a; - temp2a = swap; + swap = temp1 - HCA_SAMPLES_PER_SUBFRAME; /* move spectra or temp to beginning */ + temp1 = temp2; + temp2 = swap; - count1a = count1a << 1; - count2a = count2a >> 1; + count1 = count1 << 1; + count2 = count2 >> 1; } + } + + { + unsigned int count1 = half; + unsigned int count2 = 1; + float* temp1 = ch->temp; + float* temp2 = ch->spectra; - temp1b = ch->temp; - temp2b = ch->spectra; - count1b = half; - count2b = 1; for (i = 0; i < mdct_bits; i++) { - const float *sin_table = (const float *) decode5_sin_tables_int[i]; - const float *cos_table = (const float *) decode5_cos_tables_int[i]; - float *swap; - float *d1 = temp2b; - float *d2 = &temp2b[count2b * 2 - 1]; - const float *s1 = &temp1b[0]; - const float *s2 = &temp1b[count2b]; + const float* sin_table = (const float*) sin_tables_hex[i];//todo cleanup + const float* cos_table = (const float*) cos_tables_hex[i]; + float* swap; + float* d1 = &temp2[0]; + float* d2 = &temp2[count2 * 2 - 1]; + const float* s1 = &temp1[0]; + const float* s2 = &temp1[count2]; - for (j = 0; j < count1b; j++) { - for (k = 0; k < count2b; k++) { + for (j = 0; j < count1; j++) { + for (k = 0; k < count2; k++) { float a = *(s1++); float b = *(s2++); float sin = *(sin_table++); @@ -1626,42 +1918,45 @@ static void decoder5_run_imdct(stChannel *ch, int subframe) { *(d1++) = a * sin - b * cos; *(d2--) = a * cos + b * sin; } - s1 += count2b; - s2 += count2b; - d1 += count2b; - d2 += count2b * 3; + s1 += count2; + s2 += count2; + d1 += count2; + d2 += count2 * 3; } - swap = (float*) temp1b; - temp1b = temp2b; - temp2b = swap; + swap = temp1; + temp1 = temp2; + temp2 = swap; - count1b = count1b >> 1; - count2b = count2b << 1; + count1 = count1 >> 1; + count2 = count2 << 1; } - +#if 0 /* copy dct */ /* (with the above optimization spectra is already modified, so this is redundant) */ for (i = 0; i < size; i++) { ch->dct[i] = ch->spectra[i]; } +#endif } - /* update output/imdct */ + /* update output/imdct with overlapped window (lib fuses this with the above) */ { unsigned int i; + const float* dct = ch->spectra; //ch->dct; + const float* prev = ch->imdct_previous; for (i = 0; i < half; i++) { - ch->wave[subframe][i] = decode5_imdct_window[i] * ch->dct[i + half] + ch->imdct_previous[i]; - ch->wave[subframe][i + half] = decode5_imdct_window[i + half] * ch->dct[size - 1 - i] - ch->imdct_previous[i + half]; - ch->imdct_previous[i] = decode5_imdct_window[size - 1 - i] * ch->dct[half - i - 1]; - ch->imdct_previous[i + half] = decode5_imdct_window[half - i - 1] * ch->dct[i]; + ch->wave[subframe][i] = hcaimdct_window_float[i] * dct[i + half] + prev[i]; + ch->wave[subframe][i + half] = hcaimdct_window_float[i + half] * dct[size - 1 - i] - prev[i + half]; + ch->imdct_previous[i] = hcaimdct_window_float[size - 1 - i] * dct[half - i - 1]; + ch->imdct_previous[i + half] = hcaimdct_window_float[half - i - 1] * dct[i]; } #if 0 - /* over-optimized IMDCT (for reference), barely noticeable even when decoding hundred of files */ - const float *imdct_window = decode5_imdct_window; - const float *dct; - float *imdct_previous; - float *wave = ch->wave[subframe]; + /* over-optimized IMDCT window (for reference), barely noticeable even when decoding hundred of files */ + const float* imdct_window = hcaimdct_window_float; + const float* dct; + float* imdct_previous; + float* wave = ch->wave[subframe]; dct = &ch->dct[half]; imdct_previous = ch->imdct_previous; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 42fb19e70..acd34e561 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -345,7 +345,7 @@ typedef struct { //todo simplify ogg_vorbis_codec_data* init_ogg_vorbis(STREAMFILE* sf, off_t start, off_t size, ogg_vorbis_io* io); void decode_ogg_vorbis(ogg_vorbis_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels); -void reset_ogg_vorbis(VGMSTREAM* vgmstream); +void reset_ogg_vorbis(ogg_vorbis_codec_data* data); void seek_ogg_vorbis(ogg_vorbis_codec_data* data, int32_t num_sample); void free_ogg_vorbis(ogg_vorbis_codec_data* data); @@ -353,6 +353,7 @@ int ogg_vorbis_get_comment(ogg_vorbis_codec_data* data, const char** comment); void ogg_vorbis_get_info(ogg_vorbis_codec_data* data, int* p_channels, int* p_sample_rate); void ogg_vorbis_get_samples(ogg_vorbis_codec_data* data, int* p_samples); void ogg_vorbis_set_disable_reordering(ogg_vorbis_codec_data* data, int set); +void ogg_vorbis_set_force_seek(ogg_vorbis_codec_data* data, int set); STREAMFILE* ogg_vorbis_get_streamfile(ogg_vorbis_codec_data* data); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c index 65385b133..f1f46520a 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ffmpeg_decoder.c @@ -933,12 +933,14 @@ void ffmpeg_set_force_seek(ffmpeg_codec_data* data) { const char* ffmpeg_get_metadata_value(ffmpeg_codec_data* data, const char* key) { AVDictionary* avd; - AVDictionaryEntry* avde; + AVDictionaryEntry* avde = NULL; if (!data || !data->codec) return NULL; - avd = data->formatCtx->streams[data->streamIndex]->metadata; + avd = data->formatCtx->streams[data->streamIndex]->metadata; /* per stream (like Ogg) */ + if (!avd) + avd = data->formatCtx->metadata; /* per format (like Flac) */ if (!avd) return NULL; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c index 48205b405..122ca89af 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/hca_decoder.c @@ -42,7 +42,10 @@ hca_codec_data* init_hca(STREAMFILE* sf) { clHCA_clear(data->handle); status = clHCA_DecodeHeader(data->handle, header_buffer, header_size); /* parse header */ - if (status < 0) goto fail; + if (status < 0) { + VGM_LOG("HCA: unsupported header found, %i\n", status); + goto fail; + } status = clHCA_getInfo(data->handle, &data->info); /* extract header info */ if (status < 0) goto fail; @@ -117,6 +120,8 @@ void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do) { break; } + data->current_block++; + /* decode frame */ status = clHCA_DecodeBlock(data->handle, (void*)(data->data_buffer), blockSize); if (status < 0) { @@ -127,7 +132,6 @@ void decode_hca(hca_codec_data* data, sample_t* outbuf, int32_t samples_to_do) { /* extract samples */ clHCA_ReadSamples16(data->handle, data->sample_buffer); - data->current_block++; data->samples_consumed = 0; data->samples_filled += data->info.samplesPerBlock; } @@ -175,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) */ @@ -211,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; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c index f27e46f6c..5f8a43d63 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ogg_vorbis_decoder.c @@ -13,24 +13,27 @@ struct ogg_vorbis_codec_data { int ovf_init; int bitstream; int disable_reordering; /* Xiph Ogg must reorder channels on output, but some pre-ordered games don't need it */ + int force_seek; /* Ogg with wrong granules can't seek correctly */ + + int32_t discard; ogg_vorbis_io io; - vorbis_comment *comment; + vorbis_comment* comment; int comment_number; - vorbis_info *info; + vorbis_info* info; }; -static void pcm_convert_float_to_16(int channels, sample_t *outbuf, int samples_to_do, float **pcm, int disable_ordering); +static void pcm_convert_float_to_16(int channels, sample_t* outbuf, int start_sample, int samples_to_do, float** pcm, int disable_ordering); -static size_t ov_read_func(void *ptr, size_t size, size_t nmemb, void *datasource); -static int ov_seek_func(void *datasource, ogg_int64_t offset, int whence); -static long ov_tell_func(void *datasource); -static int ov_close_func(void *datasource); +static size_t ov_read_func(void* ptr, size_t size, size_t nmemb, void* datasource); +static int ov_seek_func(void* datasource, ogg_int64_t offset, int whence); +static long ov_tell_func(void* datasource); +static int ov_close_func(void* datasource); -ogg_vorbis_codec_data* init_ogg_vorbis(STREAMFILE *sf, off_t start, off_t size, ogg_vorbis_io *io) { - ogg_vorbis_codec_data *data = NULL; +ogg_vorbis_codec_data* init_ogg_vorbis(STREAMFILE* sf, off_t start, off_t size, ogg_vorbis_io* io) { + ogg_vorbis_codec_data* data = NULL; ov_callbacks callbacks = {0}; //todo clean up @@ -105,7 +108,7 @@ fail: return NULL; } -static size_t ov_read_func(void *ptr, size_t size, size_t nmemb, void *datasource) { +static size_t ov_read_func(void* ptr, size_t size, size_t nmemb, void* datasource) { ogg_vorbis_io *io = datasource; size_t bytes_read, items_read; @@ -129,8 +132,8 @@ static size_t ov_read_func(void *ptr, size_t size, size_t nmemb, void *datasourc return items_read; } -static int ov_seek_func(void *datasource, ogg_int64_t offset, int whence) { - ogg_vorbis_io *io = datasource; +static int ov_seek_func(void* datasource, ogg_int64_t offset, int whence) { + ogg_vorbis_io* io = datasource; ogg_int64_t base_offset, new_offset; switch (whence) { @@ -148,7 +151,6 @@ static int ov_seek_func(void *datasource, ogg_int64_t offset, int whence) { break; } - new_offset = base_offset + offset; if (new_offset < 0 || new_offset > io->size) { return -1; /* *must* return -1 if stream is unseekable */ @@ -158,12 +160,12 @@ static int ov_seek_func(void *datasource, ogg_int64_t offset, int whence) { } } -static long ov_tell_func(void *datasource) { - ogg_vorbis_io *io = datasource; +static long ov_tell_func(void* datasource) { + ogg_vorbis_io* io = datasource; return io->offset; } -static int ov_close_func(void *datasource) { +static int ov_close_func(void* datasource) { /* needed as setting ov_close_func in ov_callbacks to NULL doesn't seem to work * (closing the streamfile is done in free_ogg_vorbis) */ return 0; @@ -171,10 +173,10 @@ static int ov_close_func(void *datasource) { /* ********************************************** */ -void decode_ogg_vorbis(ogg_vorbis_codec_data *data, sample_t *outbuf, int32_t samples_to_do, int channels) { +void decode_ogg_vorbis(ogg_vorbis_codec_data* data, sample_t* outbuf, int32_t samples_to_do, int channels) { int samples_done = 0; - long rc; - float **pcm_channels; /* pointer to Xiph's double array buffer */ + long start, rc; + float** pcm_channels; /* pointer to Xiph's double array buffer */ while (samples_done < samples_to_do) { rc = ov_read_float( @@ -184,10 +186,23 @@ void decode_ogg_vorbis(ogg_vorbis_codec_data *data, sample_t *outbuf, int32_t sa &data->bitstream); /* bitstream */ if (rc <= 0) goto fail; /* rc is samples done */ - pcm_convert_float_to_16(channels, outbuf, rc, pcm_channels, data->disable_reordering); + if (data->discard) { + start = data->discard; + if (start > rc) + start = rc; - outbuf += rc * channels; - samples_done += rc; + data->discard -= start; + if (start == rc) /* consume all */ + continue; + } + else { + start = 0; + } + + pcm_convert_float_to_16(channels, outbuf, start, rc, pcm_channels, data->disable_reordering); + + outbuf += (rc - start) * channels; + samples_done += (rc - start); #if 0 // alt decoding @@ -217,8 +232,8 @@ fail: } /* vorbis encodes channels in non-standard order, so we remap during conversion to fix this oddity. - * (feels a bit weird as one would think you could leave as-is and set the player's output - * order, but that isn't possible and remapping is what FFmpeg and every other plugin does). */ + * (feels a bit weird as one would think you could leave as-is and set the player's output order, + * but that isn't possible and remapping like this is what FFmpeg and every other plugin does). */ static const int xiph_channel_map[8][8] = { { 0 }, /* 1ch: FC > same */ { 0, 1 }, /* 2ch: FL FR > same */ @@ -231,7 +246,7 @@ static const int xiph_channel_map[8][8] = { }; /* converts from internal Vorbis format to standard PCM and remaps (mostly from Xiph's decoder_example.c) */ -static void pcm_convert_float_to_16(int channels, sample_t * outbuf, int samples_to_do, float ** pcm, int disable_ordering) { +static void pcm_convert_float_to_16(int channels, sample_t* outbuf, int start_sample, int samples_to_do, float** pcm, int disable_ordering) { int ch, s, ch_map; sample_t *ptr; float *channel; @@ -244,7 +259,7 @@ static void pcm_convert_float_to_16(int channels, sample_t * outbuf, int samples (channels > 8) ? ch : xiph_channel_map[channels - 1][ch]; /* put Vorbis' ch to other outbuf's ch */ ptr = outbuf + ch; channel = pcm[ch_map]; - for (s = 0; s < samples_to_do; s++) { + for (s = start_sample; s < samples_to_do; s++) { int val = (int)floor(channel[s] * 32767.0f + 0.5f); /* use floorf? doesn't seem any faster */ if (val > 32767) val = 32767; else if (val < -32768) val = -32768; @@ -257,23 +272,34 @@ static void pcm_convert_float_to_16(int channels, sample_t * outbuf, int samples /* ********************************************** */ -void reset_ogg_vorbis(VGMSTREAM *vgmstream) { - ogg_vorbis_codec_data *data = vgmstream->codec_data; +void reset_ogg_vorbis(ogg_vorbis_codec_data* data) { if (!data) return; - /* this seek cleans internal buffers */ - ov_pcm_seek(&data->ogg_vorbis_file, 0); + /* this raw seek cleans internal buffers, and it's preferable to + * ov_pcm_seek as doesn't get confused by wrong granules */ + ov_raw_seek(&data->ogg_vorbis_file, 0); + //;VGM_ASSERT(res != 0, "OGG: bad reset=%i\n", res); + + data->discard = 0; } -void seek_ogg_vorbis(ogg_vorbis_codec_data *data, int32_t num_sample) { +void seek_ogg_vorbis(ogg_vorbis_codec_data* data, int32_t num_sample) { if (!data) return; - /* this seek crosslaps to avoid possible clicks, so seeking to 0 will - * decode a bit differently than ov_pcm_seek */ + /* special seek for games with bad granule positions (since ov_*_seek uses granules to seek) */ + if (data->force_seek) { + reset_ogg_vorbis(data); + data->discard = num_sample; + return; + } + + /* this seek crosslaps to avoid possible clicks, so seeking to 0 + discarding + * will decode a bit differently than ov_pcm_seek */ ov_pcm_seek_lap(&data->ogg_vorbis_file, num_sample); + //VGM_ASSERT(res != 0, "OGG: bad seek=%i\n", res); /* not seen, in theory could give error */ } -void free_ogg_vorbis(ogg_vorbis_codec_data *data) { +void free_ogg_vorbis(ogg_vorbis_codec_data* data) { if (!data) return; if (data->ovf_init) @@ -285,7 +311,7 @@ void free_ogg_vorbis(ogg_vorbis_codec_data *data) { /* ********************************************** */ -int ogg_vorbis_get_comment(ogg_vorbis_codec_data *data, const char **comment) { +int ogg_vorbis_get_comment(ogg_vorbis_codec_data* data, const char** comment) { if (!data) return 0; /* dumb reset */ @@ -302,7 +328,7 @@ int ogg_vorbis_get_comment(ogg_vorbis_codec_data *data, const char **comment) { return 1; } -void ogg_vorbis_get_info(ogg_vorbis_codec_data *data, int *p_channels, int *p_sample_rate) { +void ogg_vorbis_get_info(ogg_vorbis_codec_data* data, int* p_channels, int* p_sample_rate) { if (!data) { if (p_channels) *p_channels = 0; if (p_sample_rate) *p_sample_rate = 0; @@ -313,7 +339,7 @@ void ogg_vorbis_get_info(ogg_vorbis_codec_data *data, int *p_channels, int *p_sa if (p_sample_rate) *p_sample_rate = (int)data->info->rate; } -void ogg_vorbis_get_samples(ogg_vorbis_codec_data *data, int *p_samples) { +void ogg_vorbis_get_samples(ogg_vorbis_codec_data* data, int* p_samples) { if (!data) { if (p_samples) *p_samples = 0; return; @@ -322,13 +348,19 @@ void ogg_vorbis_get_samples(ogg_vorbis_codec_data *data, int *p_samples) { if (p_samples) *p_samples = ov_pcm_total(&data->ogg_vorbis_file,-1); } -void ogg_vorbis_set_disable_reordering(ogg_vorbis_codec_data *data, int set) { +void ogg_vorbis_set_disable_reordering(ogg_vorbis_codec_data* data, int set) { if (!data) return; data->disable_reordering = set; } -STREAMFILE* ogg_vorbis_get_streamfile(ogg_vorbis_codec_data *data) { +void ogg_vorbis_set_force_seek(ogg_vorbis_codec_data* data, int set) { + if (!data) return; + + data->force_seek = set; +} + +STREAMFILE* ogg_vorbis_get_streamfile(ogg_vorbis_codec_data* data) { if (!data) return NULL; return data->io.streamfile; } diff --git a/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.c b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.c index 5b06f5f08..ef46dc6a3 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.c @@ -13,8 +13,8 @@ * - https://github.com/PCSX2/pcsx2/blob/master/pcsx2/VUops.cpp * * Codec has no apparent name, but most functions mention "St" (stream?) and "Sac" (sound audio - * container?) and handler lib may be "Csd". Looks inspired by MPEG (much simplified) with bits - * from other codecs (per-file codebook and 1024 samples). + * compression?) and main lib is called "Sac" (Sac.cpp/Sac.dsm), also set in a "Csd" ELF. Looks inspired + * by MPEG (much simplified) with bits from other codecs (per-file codebook and 1024 samples). * * Original decoder is mainly implemented in the PS2's VU1, a coprocessor specialized in vector/SIMD * and parallel instructions. As VU1 works with many 128 bit registers (typically x4 floats) algorithm @@ -134,7 +134,7 @@ static inline int16_t clamp_s16(int16_t value, int16_t min, int16_t max) { /* converts 4 huffman codes to 4 spectrums coefs */ -//SUB_1188 +//SUB_1188 (Pass1_Start?) static void unpack_code4(REG_VF* spectrum, const REG_VF* spc1, const REG_VF* spc2, const REG_VF* code, const REG_VF* idx, int out_pos) { const REG_VF* ST = SCALE_TABLE; REG_VF tbc1, tbc2, out; @@ -282,8 +282,8 @@ static void transform_dot_product(REG_VF* mac, const REG_VF* spectrum, const REG MADD (_xyzw, mac, &spectrum[pos_i+7], &TT[pos_t+7]); } -/* take spectrum coefs and, ahem, transform somehow, possibly using a SIMD'd FFT/DCT table. */ -//SUB_1410 +/* take spectrum coefs and, ahem, transform somehow, possibly using a SIMD'd DCT table. */ +//SUB_1410 (Idct_Start?) static void transform(REG_VF* wave, const REG_VF* spectrum) { const REG_VF* TT = TRANSFORM_TABLE; int i, j; @@ -330,7 +330,7 @@ static void transform(REG_VF* wave, const REG_VF* spectrum) { /* process and apply window/overlap. Similar to MP3's synth granule function. */ -//SUB_1690 +//SUB_1690 (Pass3_Start?) static void process(REG_VF* wave, REG_VF* hist) { const REG_VF* ST = SYNTH_TABLE; int i, j; @@ -918,7 +918,7 @@ static void finalize_output(tac_handle_t* h) { REG_VF* wave_l = h->wave[0]; REG_VF* wave_r = h->wave[1]; - /* Combine joint stereo channels that encode diffs in L/R. In pseudo-mono files R has */ + /* Combine joint stereo channels that encode diffs in L/R ("MS stereo"). In pseudo-mono files R has */ /* all samples as 0 (R only saves 28 huffman codes, signalling no coefs per 1+27 bands) */ for (i = 0; i < TAC_TOTAL_POINTS * 8; i++) { REG_VF samples_l, samples_r; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.h b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.h index aa28289d7..5389aeadb 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.h @@ -31,7 +31,7 @@ typedef struct { uint16_t frame_last; /* valid samples in final frame - 1 (lower = outputs less, 0 = outputs 1), even for non-looped files */ uint32_t loop_offset; /* points to a block; file size if not looped */ uint32_t file_size; /* block aligned; actual file size can be a bit smaller if last block is truncated */ - uint32_t joint_stereo; /* usually 0 and rarely 1 */ + uint32_t joint_stereo; /* usually 0 and rarely 1 ("MSStereoMode") */ uint32_t empty; /* always null */ } tac_header_t; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib_data.h b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib_data.h index 150a9bbe0..434a485df 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib_data.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib_data.h @@ -71,7 +71,7 @@ static const REG_VF ANTIALIASING_TABLE[64/4] = { /* Seemingly scale factor table (needs an extra index, see end). * ELF seems to store only xy, zw is just mirrored. */ -static const REG_VF SCALE_TABLE[2048/4 + 4/4] = { +static const REG_VF SCALE_TABLE[2048/4 + 4/4] = { //SacPass1TableVU? /* part 1 */ { .i = {0x00000000,0x3F7F8000,0x00000000,0x3F7F8000} }, //+0.000000000f, +0.998046875f, +0.000000000f, +0.998046875f { .i = {0x3A000000,0x3F5744FD,0x3A000000,0x3F5744FD} }, //+0.000488281f, +0.840896428f, +0.000488281f, +0.840896428f @@ -592,8 +592,8 @@ static const REG_VF SCALE_TABLE[2048/4 + 4/4] = { { .i = {0x00000000,0x00000000,0x00000000,0x00000000} }, //+0.000000000f, +0.000000000f, +0.000000000f, +0.000000000f }; -/* FFT?/DCT? table (Hanning window?) */ -static const REG_VF TRANSFORM_TABLE[1024/4] = { +/* IDCT? table (Hanning window?) */ +static const REG_VF TRANSFORM_TABLE[1024/4] = { //SacIdctTableVU? { .i = {0x3F3504F3,0x3F7FB10F,0x3F7EC46D,0x3F7D3AAC} }, //+0.707106769f, +0.998795450f, +0.995184720f, +0.989176512f { .i = {0x3F7B14BE,0x3F7853F8,0x3F74FA0B,0x3F710908} }, //+0.980785251f, +0.970031261f, +0.956940353f, +0.941544056f { .i = {0x3F6C835E,0x3F676BD8,0x3F61C597,0x3F5B941A} }, //+0.923879504f, +0.903989315f, +0.881921232f, +0.857728601f @@ -854,7 +854,7 @@ static const REG_VF TRANSFORM_TABLE[1024/4] = { /* standard MPEG1 filter bank (synth_window) * Seems divided into 2 parts, or at least loaded to VU1 memory in 2 steps */ -static const REG_VF WINDOW_TABLE[512/4] = { +static const REG_VF WINDOW_TABLE[512/4] = { //SacPass3TableVU? /* part 1 */ { .i = {0x00000000,0xB7800074,0xB7800074,0xB7800074} }, //+0.000000000f, -0.000015259f, -0.000015259f, -0.000015259f { .i = {0xB7800074,0xB7800074,0xB7800074,0xB8000074} }, //-0.000015259f, -0.000015259f, -0.000015259f, -0.000030518f diff --git a/Frameworks/vgmstream/vgmstream/src/decode.c b/Frameworks/vgmstream/vgmstream/src/decode.c index 17ecbb92b..a023a112e 100644 --- a/Frameworks/vgmstream/vgmstream/src/decode.c +++ b/Frameworks/vgmstream/vgmstream/src/decode.c @@ -219,7 +219,7 @@ void reset_codec(VGMSTREAM* vgmstream) { #ifdef VGM_USE_VORBIS if (vgmstream->coding_type == coding_OGG_VORBIS) { - reset_ogg_vorbis(vgmstream); + reset_ogg_vorbis(vgmstream->codec_data); } if (vgmstream->coding_type == coding_VORBIS_custom) { diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 95beabb0b..946d4100e 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -83,6 +83,7 @@ static const char* extension_list[] = { "atsl4", "atx", "aud", + "audio", //txth/reserved [Grimm Echoes (Android)] "aus", "awb", "awc", diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c index 084cf5187..fbe0b4a79 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ffmpeg.c @@ -4,7 +4,7 @@ #ifdef VGM_USE_FFMPEG static int read_pos_file(uint8_t* buf, size_t bufsize, STREAMFILE* sf); -static int find_ogg_loops(ffmpeg_codec_data* data, int32_t* p_loop_start, int32_t* p_loop_end); +static int find_meta_loops(ffmpeg_codec_data* data, int32_t* p_loop_start, int32_t* p_loop_end); /* parses any format supported by FFmpeg and not handled elsewhere: * - MP3 (.mp3, .mus): Marc Ecko's Getting Up (PC) @@ -58,9 +58,9 @@ VGMSTREAM* init_vgmstream_ffmpeg(STREAMFILE* sf) { } } - /* try to read Ogg loop tags (abridged) */ - if (loop_flag == 0 && read_u32be(0x00, sf) == 0x4F676753) { /* "OggS" */ - loop_flag = find_ogg_loops(data, &loop_start, &loop_end); + /* try to read Ogg/Flac loop tags (abridged) */ + if (!loop_flag && (is_id32be(0x00, sf, "OggS") || is_id32be(0x00, sf, "fLaC"))) { + loop_flag = find_meta_loops(data, &loop_start, &loop_end); } /* hack for AAC files (will return 0 samples if not an actual file) */ @@ -155,22 +155,30 @@ fail: } -/* loop tag handling could be unified with ogg_vorbis.c, but that one has a extra features too */ -static int find_ogg_loops(ffmpeg_codec_data* data, int32_t* p_loop_start, int32_t* p_loop_end) { +/* loop tag handling could be unified with ogg_vorbis.c, but that one has a extra features too. + * Also has support for flac meta loops, that can be used by stuff like Platformer Game Engine + * or ZDoom/Duke Nukem 3D source ports (maybe RPG Maker too). */ +//todo call ffmpeg_get_next_tag and iterate like ogg_vorbis.c +//todo also save title +static int find_meta_loops(ffmpeg_codec_data* data, int32_t* p_loop_start, int32_t* p_loop_end) { char* endptr; const char* value; int loop_flag = 0; int32_t loop_start = -1, loop_end = -1; - // Try to detect the loop flags based on current file metadata + // Try to detect the loop flags based on current file metadata (ignores case) value = ffmpeg_get_metadata_value(data, "LoopStart"); - if (value != NULL) { + if (!value) + value = ffmpeg_get_metadata_value(data, "LOOP_START"); /* ZDoom/DN3D */ + if (value) { loop_start = strtol(value, &endptr, 10); loop_flag = 1; } value = ffmpeg_get_metadata_value(data, "LoopEnd"); - if (value != NULL) { + if (!value) + value = ffmpeg_get_metadata_value(data, "LOOP_END"); /* ZDoom/DN3D */ + if (value) { loop_end = strtol(value, &endptr, 10); loop_flag = 1; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c index 1ae3d71a6..430572a13 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb5.c @@ -11,6 +11,7 @@ typedef struct { int flags; int channels; + int layers; int sample_rate; int32_t num_samples; int32_t loop_start; @@ -33,8 +34,7 @@ typedef struct { /* ********************************************************************************** */ -static layered_layout_data* build_layered_fsb5_celt(STREAMFILE* sf, fsb5_header* fsb5); -static layered_layout_data* build_layered_fsb5_atrac9(STREAMFILE* sf, fsb5_header* fsb5, off_t configs_offset, size_t configs_size); +static layered_layout_data* build_layered_fsb5(STREAMFILE* sf, fsb5_header* fsb5); /* FSB5 - Firelight's FMOD Studio SoundBank format */ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { @@ -50,23 +50,24 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { if (!check_extensions(sf,"fsb,snd")) goto fail; - if (read_32bitBE(0x00,sf) != 0x46534235) /* "FSB5" */ + if (!is_id32be(0x00,sf, "FSB5")) goto fail; /* 0x00 is rare (seen in Tales from Space Vita) */ - fsb5.version = read_32bitLE(0x04,sf); - if (fsb5.version != 0x00 && fsb5.version != 0x01) goto fail; + fsb5.version = read_u32le(0x04,sf); + if (fsb5.version != 0x00 && fsb5.version != 0x01) + goto fail; - fsb5.total_subsongs = read_32bitLE(0x08,sf); - fsb5.sample_header_size = read_32bitLE(0x0C,sf); - fsb5.name_table_size = read_32bitLE(0x10,sf); - fsb5.sample_data_size = read_32bitLE(0x14,sf); - fsb5.codec = read_32bitLE(0x18,sf); + fsb5.total_subsongs = read_u32le(0x08,sf); + fsb5.sample_header_size = read_u32le(0x0C,sf); + fsb5.name_table_size = read_u32le(0x10,sf); + fsb5.sample_data_size = read_u32le(0x14,sf); + fsb5.codec = read_u32le(0x18,sf); /* version 0x01 - 0x1c(4): zero, 0x24(16): hash, 0x34(8): unk * version 0x00 has an extra field (always 0?) at 0x1c */ if (fsb5.version == 0x01) { /* found by tests and assumed to be flags, no games known */ - fsb5.flags = read_32bitLE(0x20,sf); + fsb5.flags = read_u32le(0x20,sf); } fsb5.base_header_size = (fsb5.version==0x00) ? 0x40 : 0x3C; @@ -87,8 +88,8 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { off_t data_offset = 0; uint32_t sample_mode1, sample_mode2; /* maybe one uint64? */ - sample_mode1 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+0x00,sf); - sample_mode2 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+0x04,sf); + sample_mode1 = read_u32le(fsb5.sample_header_offset+0x00,sf); + sample_mode2 = read_u32le(fsb5.sample_header_offset+0x04,sf); stream_header_size += 0x08; /* get samples */ @@ -133,7 +134,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { uint32_t extraflag, extraflag_type, extraflag_size, extraflag_end; do { - extraflag = read_32bitLE(extraflag_offset,sf); + extraflag = read_u32le(extraflag_offset,sf); extraflag_type = (extraflag >> 25) & 0x7F; /* bits 32..26 (7) */ extraflag_size = (extraflag >> 1) & 0xFFFFFF; /* bits 25..1 (24)*/ extraflag_end = (extraflag & 0x01); /* bit 0 (1) */ @@ -142,15 +143,15 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { if (i + 1 == target_subsong) { switch(extraflag_type) { case 0x01: /* channels */ - fsb5.channels = read_8bit(extraflag_offset+0x04,sf); + fsb5.channels = read_u8(extraflag_offset+0x04,sf); break; case 0x02: /* sample rate */ - fsb5.sample_rate = read_32bitLE(extraflag_offset+0x04,sf); + fsb5.sample_rate = read_s32le(extraflag_offset+0x04,sf); break; case 0x03: /* loop info */ - fsb5.loop_start = read_32bitLE(extraflag_offset+0x04,sf); + fsb5.loop_start = read_s32le(extraflag_offset+0x04,sf); if (extraflag_size > 0x04) { /* probably not needed */ - fsb5.loop_end = read_32bitLE(extraflag_offset+0x08,sf); + fsb5.loop_end = read_s32le(extraflag_offset+0x08,sf); fsb5.loop_end += 1; /* correct compared to FMOD's tools */ } //;VGM_LOG("FSB5: stream %i loop start=%i, loop end=%i, samples=%i\n", i, fsb5.loop_start, fsb5.loop_end, fsb5.num_samples); @@ -183,7 +184,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { case 0x05: /* unknown 32b */ /* rare, found in Tearaway (Vita) with value 0 in first stream and * Shantae and the Seven Sirens (Mobile) with value 0x0003bd72 BE in #44 (Arena Town) */ - VGM_LOG("FSB5: stream %i flag %x with value %08x\n", i, extraflag_type, read_32bitLE(extraflag_offset+0x04,sf)); + VGM_LOG("FSB5: stream %i flag %x with value %08x\n", i, extraflag_type, read_u32le(extraflag_offset+0x04,sf)); break; case 0x06: /* XMA seek table */ /* no need for it */ @@ -204,14 +205,17 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { * 0x08: table_size (total_entries = seek_table_size / (4+4)), not counting this value; can be 0 * 0x0C: sample number (only some samples are saved in the table) * 0x10: offset within data, pointing to a FSB vorbis block (with the 16b block size header) - * (xN entries) - */ + * (xN entries) */ break; case 0x0d: /* peak volume float (optional setting when making fsb) */ break; case 0x0f: /* OPUS data size not counting frames headers */ break; - case 0x0e: /* number of layered Vorbis channels [Invisible, Inc. (Switch)] */ + case 0x0e: /* Vorbis intra-layers (multichannel FMOD ~2021) [Invisible, Inc. (Switch), Just Cause 4 (PC)] */ + fsb5.layers = read_u32le(extraflag_offset+0x04,sf); + /* info only as decoding is standard Vorbis that handles Nch multichannel (channels is 1 here) */ + fsb5.channels = fsb5.channels * fsb5.layers; + break; default: VGM_LOG("FSB5: stream %i unknown flag 0x%x at %x + 0x04 (size 0x%x)\n", i, extraflag_type, (uint32_t)extraflag_offset, extraflag_size); break; @@ -240,8 +244,8 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { else { off_t next_data_offset; uint32_t next_sample_mode1, next_sample_mode2; - next_sample_mode1 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+stream_header_size+0x00,sf); - next_sample_mode2 = (uint32_t)read_32bitLE(fsb5.sample_header_offset+stream_header_size+0x04,sf); + next_sample_mode1 = read_u32le(fsb5.sample_header_offset+stream_header_size+0x00,sf); + next_sample_mode2 = read_u32le(fsb5.sample_header_offset+stream_header_size+0x04,sf); next_data_offset = (((next_sample_mode2 & 0x03) << 25) | ((next_sample_mode1 >> 7) & 0x1FFFFFF)) << 5; fsb5.stream_size = next_data_offset - data_offset; @@ -254,17 +258,18 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { fsb5.sample_header_offset += stream_header_size; } /* target stream not found*/ - if (!fsb5.stream_offset || !fsb5.stream_size) goto fail; + if (!fsb5.stream_offset || !fsb5.stream_size) + goto fail; /* get stream name */ if (fsb5.name_table_size) { off_t name_suboffset = fsb5.base_header_size + fsb5.sample_header_size + 0x04*(target_subsong-1); - fsb5.name_offset = fsb5.base_header_size + fsb5.sample_header_size + read_32bitLE(name_suboffset,sf); + fsb5.name_offset = fsb5.base_header_size + fsb5.sample_header_size + read_u32le(name_suboffset,sf); } /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(fsb5.channels,fsb5.loop_flag); + vgmstream = allocate_vgmstream(fsb5.channels, fsb5.loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = fsb5.sample_rate; @@ -379,10 +384,10 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { #ifdef VGM_USE_CELT case 0x0C: { /* FMOD_SOUND_FORMAT_CELT [BIT.TRIP Presents Runner2 (PC), Full Bore (PC)] */ - int is_multistream = fsb5.channels > 2; + fsb5.layers = (fsb5.channels <= 2) ? 1 : (fsb5.channels+1) / 2; - if (is_multistream) { - vgmstream->layout_data = build_layered_fsb5_celt(sf, &fsb5); + if (fsb5.layers > 1) { + vgmstream->layout_data = build_layered_fsb5(sf, &fsb5); if (!vgmstream->layout_data) goto fail; vgmstream->coding_type = coding_CELT_FSB; vgmstream->layout_type = layout_layered; @@ -399,22 +404,18 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { #ifdef VGM_USE_ATRAC9 case 0x0D: {/* FMOD_SOUND_FORMAT_AT9 */ - int is_multistream; - off_t configs_offset = fsb5.extradata_offset; - size_t configs_size = fsb5.extradata_size; - - /* skip frame size in newer FSBs [Day of the Tentacle Remastered (Vita), Tearaway Unfolded (PS4)] */ - if (configs_size >= 0x08 && (uint8_t)read_8bit(configs_offset, sf) != 0xFE) { /* ATRAC9 sync */ - configs_offset += 0x04; - configs_size -= 0x04; + if (fsb5.extradata_size >= 0x08 + && read_u8(fsb5.extradata_offset, sf) != 0xFE) { /* not ATRAC9 sync */ + fsb5.extradata_offset += 0x04; + fsb5.extradata_size -= 0x04; } - is_multistream = (configs_size / 0x04) > 1; + fsb5.layers = (fsb5.extradata_size / 0x04); - if (is_multistream) { - /* multichannel made of various streams [Little Big Planet (Vita)] */ - vgmstream->layout_data = build_layered_fsb5_atrac9(sf, &fsb5, configs_offset, configs_size); + if (fsb5.layers > 1) { + /* multichannel made of various layers [Little Big Planet (Vita)] */ + vgmstream->layout_data = build_layered_fsb5(sf, &fsb5); if (!vgmstream->layout_data) goto fail; vgmstream->coding_type = coding_ATRAC9; vgmstream->layout_type = layout_layered; @@ -424,7 +425,7 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { atrac9_config cfg = {0}; cfg.channels = vgmstream->channels; - cfg.config_data = read_32bitBE(configs_offset,sf); + cfg.config_data = read_u32be(fsb5.extradata_offset,sf); //cfg.encoder_delay = 0x100; //todo not used? num_samples seems to count all data vgmstream->codec_data = init_atrac9(&cfg); @@ -441,9 +442,9 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { uint8_t buf[0x100]; int bytes, format, average_bps, block_align; - format = read_16bitBE(fsb5.extradata_offset+0x00,sf); - block_align = (uint16_t)read_16bitBE(fsb5.extradata_offset+0x02,sf); - average_bps = (uint32_t)read_32bitBE(fsb5.extradata_offset+0x04,sf); + format = read_u16be(fsb5.extradata_offset+0x00,sf); + block_align = read_u16be(fsb5.extradata_offset+0x02,sf); + average_bps = read_u32be(fsb5.extradata_offset+0x04,sf); /* rest: seek entries + mini seek table? */ /* XWMA encoder only does up to 6ch (doesn't use FSB multistreams for more) */ @@ -460,15 +461,14 @@ VGMSTREAM* init_vgmstream_fsb5(STREAMFILE* sf) { case 0x0F: {/* FMOD_SOUND_FORMAT_VORBIS [Shantae Half Genie Hero (PC), Pokemon Go (iOS)] */ vorbis_custom_config cfg = {0}; - cfg.channels = vgmstream->channels; - cfg.sample_rate = vgmstream->sample_rate; - cfg.setup_id = read_32bitLE(fsb5.extradata_offset,sf); + cfg.channels = fsb5.channels; + cfg.sample_rate = fsb5.sample_rate; + cfg.setup_id = read_u32le(fsb5.extradata_offset,sf); - vgmstream->layout_type = layout_none; - vgmstream->coding_type = coding_VORBIS_custom; vgmstream->codec_data = init_vorbis_custom(sf, fsb5.stream_offset, VORBIS_FSB, &cfg); if (!vgmstream->codec_data) goto fail; - + vgmstream->coding_type = coding_VORBIS_custom; + vgmstream->layout_type = layout_none; break; } #endif @@ -509,32 +509,63 @@ fail: } -static layered_layout_data* build_layered_fsb5_celt(STREAMFILE* sf, fsb5_header* fsb5) { +static layered_layout_data* build_layered_fsb5(STREAMFILE* sf, fsb5_header* fsb5) { layered_layout_data* data = NULL; STREAMFILE* temp_sf = NULL; - int i, layers = (fsb5->channels+1) / 2; - size_t interleave; - - if (read_32bitBE(fsb5->stream_offset+0x00,sf) != 0x17C30DF3) /* FSB CELT frame ID */ - goto fail; - interleave = 0x04+0x04+read_32bitLE(fsb5->stream_offset+0x04,sf); /* frame size */ - - //todo unknown interleave for max quality odd channel streams (found in test files) - /* FSB5 odd channels use 2ch+2ch...+1ch streams, and the last only goes up to 0x17a, and other - * streams only use that max (doesn't happen for smaller frames, even channels, or FSB4) - * however streams other than the last seem to be padded with 0s somehow and wont work */ - if (interleave > 0x17a && (fsb5->channels % 2 == 1)) - interleave = 0x17a; + size_t interleave, config = 0; + int i, layer_channels; /* init layout */ - data = init_layout_layered(layers); + data = init_layout_layered(fsb5->layers); if (!data) goto fail; - /* open each layer subfile (1/2ch CELT streams: 2ch+2ch..+1ch or 2ch+2ch..+2ch) */ - for (i = 0; i < layers; i++) { - int layer_channels = (i+1 == layers && fsb5->channels % 2 == 1) - ? 1 : 2; /* last layer can be 1/2ch */ + for (i = 0; i < fsb5->layers; i++) { + switch (fsb5->codec) { + case 0x0C: { /* CELT */ + /* 2ch+2ch..+1ch or 2ch+2ch..+2ch = check last layer */ + layer_channels = (i+1 == fsb5->layers && fsb5->channels % 2 == 1) ? 1 : 2; + + if (read_u32be(fsb5->stream_offset+0x00,sf) != 0x17C30DF3) /* FSB CELT frame ID */ + goto fail; + interleave = 0x04+0x04+read_u32le(fsb5->stream_offset+0x04,sf); /* frame size */ + + //todo unknown interleave for max quality odd channel streams (found in test files) + /* FSB5 odd channels use 2ch+2ch...+1ch streams, and the last only goes up to 0x17a, and other + * streams only use that max (doesn't happen for smaller frames, even channels, or FSB4) + * however streams other than the last seem to be padded with 0s somehow and wont work */ + if (interleave > 0x17a && (fsb5->channels % 2 == 1)) + interleave = 0x17a; + break; + } + + case 0x0D: { /* ATRAC9 */ + int channel_index; + size_t frame_size; + + /* 2ch+2ch..+1/2ch */ + config = read_u32be(fsb5->extradata_offset + 0x04*i, sf); /* ATRAC9 config */ + + channel_index = ((config >> 17) & 0x7); + frame_size = (((config >> 5) & 0x7FF) + 1) * (1 << ((config >> 3) & 0x2)); /* frame size * superframe index */ + if (channel_index > 2) + goto fail; /* only 1/2ch expected */ + + layer_channels = (channel_index==0) ? 1 : 2; + interleave = frame_size; + //todo in test files with 2ch+..+1ch interleave is off (uses some strange padding) + break; + } + + case 0x0F: { /* VORBIS */ + layer_channels = fsb5->channels; + interleave = 0; + break; + } + + default: + goto fail; + } /* build the layer VGMSTREAM */ data->layers[i] = allocate_vgmstream(layer_channels, fsb5->loop_flag); @@ -545,94 +576,40 @@ static layered_layout_data* build_layered_fsb5_celt(STREAMFILE* sf, fsb5_header* data->layers[i]->loop_start_sample = fsb5->loop_start; data->layers[i]->loop_end_sample = fsb5->loop_end; + + switch (fsb5->codec) { #ifdef VGM_USE_CELT - data->layers[i]->codec_data = init_celt_fsb(layer_channels, CELT_0_11_0); - if (!data->layers[i]->codec_data) goto fail; - data->layers[i]->coding_type = coding_CELT_FSB; - data->layers[i]->layout_type = layout_none; -#else - goto fail; + case 0x0C: { /* CELT */ + data->layers[i]->codec_data = init_celt_fsb(layer_channels, CELT_0_11_0); + if (!data->layers[i]->codec_data) goto fail; + data->layers[i]->coding_type = coding_CELT_FSB; + data->layers[i]->layout_type = layout_none; + break; + } #endif - temp_sf = setup_fsb5_streamfile(sf, fsb5->stream_offset, fsb5->stream_size, layers, i, interleave); - if (!temp_sf) goto fail; - - if (!vgmstream_open_stream(data->layers[i], temp_sf, 0x00)) - goto fail; - - close_streamfile(temp_sf); - temp_sf = NULL; - } - - /* setup layered VGMSTREAMs */ - if (!setup_layout_layered(data)) - goto fail; - return data; - -fail: - close_streamfile(temp_sf); - free_layout_layered(data); - return NULL; -} - - -static layered_layout_data* build_layered_fsb5_atrac9(STREAMFILE* sf, fsb5_header* fsb5, off_t configs_offset, size_t configs_size) { - layered_layout_data* data = NULL; - STREAMFILE* temp_sf = NULL; - int i, layers = (configs_size / 0x04); - size_t interleave = 0; - - - /* init layout */ - data = init_layout_layered(layers); - if (!data) goto fail; - - /* open each layer subfile (2ch+2ch..+1/2ch) */ - for (i = 0; i < layers; i++) { - uint32_t config = read_32bitBE(configs_offset + 0x04*i, sf); - int channel_index, layer_channels; - size_t frame_size; - - - /* parse ATRAC9 config (see VGAudio docs) */ - channel_index = ((config >> 17) & 0x7); - frame_size = (((config >> 5) & 0x7FF) + 1) * (1 << ((config >> 3) & 0x2)); /* frame size * superframe index */ - if (channel_index > 2) - goto fail; /* only 1/2ch expected */ - - layer_channels = (channel_index==0) ? 1 : 2; - if (interleave == 0) - interleave = frame_size; - //todo in test files with 2ch+..+1ch interleave is off (uses some strange padding) - - - /* build the layer VGMSTREAM */ - data->layers[i] = allocate_vgmstream(layer_channels, fsb5->loop_flag); - if (!data->layers[i]) goto fail; - - data->layers[i]->sample_rate = fsb5->sample_rate; - data->layers[i]->num_samples = fsb5->num_samples; - data->layers[i]->loop_start_sample = fsb5->loop_start; - data->layers[i]->loop_end_sample = fsb5->loop_end; - #ifdef VGM_USE_ATRAC9 - { - atrac9_config cfg = {0}; + case 0x0D: { /* ATRAC9 */ + atrac9_config cfg = {0}; - cfg.channels = layer_channels; - cfg.config_data = config; - //cfg.encoder_delay = 0x100; //todo not used? num_samples seems to count all data + cfg.channels = layer_channels; + cfg.config_data = config; + //cfg.encoder_delay = 0x100; //todo not used? num_samples seems to count all data - data->layers[i]->codec_data = init_atrac9(&cfg); - if (!data->layers[i]->codec_data) goto fail; - data->layers[i]->coding_type = coding_ATRAC9; - data->layers[i]->layout_type = layout_none; - } -#else - goto fail; + data->layers[i]->codec_data = init_atrac9(&cfg); + if (!data->layers[i]->codec_data) goto fail; + data->layers[i]->coding_type = coding_ATRAC9; + data->layers[i]->layout_type = layout_none; + break; + } #endif - temp_sf = setup_fsb5_streamfile(sf, fsb5->stream_offset, fsb5->stream_size, layers, i, interleave); + default: + goto fail; + } + + + temp_sf = setup_fsb5_streamfile(sf, fsb5->stream_offset, fsb5->stream_size, fsb5->layers, i, interleave); if (!temp_sf) goto fail; if (!vgmstream_open_stream(data->layers[i], temp_sf, 0x00)) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca.c b/Frameworks/vgmstream/vgmstream/src/meta/hca.c index b68724328..23f79066a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca.c @@ -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; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index 0f8aa0161..96c971aba 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -193,6 +193,7 @@ static const hcakey_info hcakey_list[] = { {4988006236073}, // 000004895C56FFA9 // Castle & Dragon (iOS/Android) + // Gunbit (Android) {20140528}, // 00000000013351F0 // Uta no Prince sama Shining Live (iOS/Android) @@ -373,9 +374,6 @@ static const hcakey_info hcakey_list[] = { /* Re:Zero - Lost in Memories (Android) */ {1611432018519751642}, // 165CF4E2138F7BDA - /* D4DJ Groovy Mix (Android) [base files] */ - {393410674916959300}, // 0575ACECA945A444 - /* Toji no Miko: Kizamishi Issen no Tomoshibi (Android) */ {62057514034227932}, // 00DC78FAEFA76ADC @@ -391,6 +389,224 @@ static const hcakey_info hcakey_list[] = { /* Sakura Kakumei (iOS/Android) */ {382759}, // 000000000005D727 + /* Uma Musume (Android) */ + {75923756697503}, // 0000450D608C479F + + /* D4DJ Groovy Mix (Android) [base files] */ + {393410674916959300}, // 0575ACECA945A444 + /* D4DJ Groovy Mix (Android) [music_* files, per-song later mixed with subkey] */ + {0x59f449354d063308}, + {0x33848be13a2884a3}, + {0xf7e53533d82d48dd}, + {0x244a92885ab77b7c}, + {0xce0796d2a956dc5a}, + {0x73667711348f833f}, + {0x8032f83cbf0076a1}, + {0x7a708e291692abb9}, + {0x9ebb560685327081}, + {0x065c2f8500bc12c8}, + {0x73621a0d321e60c2}, + {0xfcce3164db70522d}, + {0xf4093992cadd3708}, + {0xf965a1086b3179c3}, + {0x54aaada4a1b8deef}, + {0xd4d2a706a06377ef}, + {0x0de4959221bc2675}, + {0x2ecdf66c680f3a45}, + {0x24c0b49097e9ebff}, + {0xc28331aab2612584}, + {0x6750f4d05183bc01}, + {0xda65af760e02c6ee}, + {0x217782495c8b2972}, + {0xbf7712e175c0b265}, + {0x3804d53c43293080}, + {0xd0ed8e940d8ed705}, + {0xf74cf8d4a5d008ce}, + {0xeb8aac34dc178f65}, + {0xbf5902d516db6ed5}, + {0xad071dce0c070e65}, + {0x56ecfc7ef4c65be8}, + {0xf42d31b5ecd1aec1}, + {0x0a8ee7a3a20ce822}, + {0xb9cedc6d6738d481}, + {0xdc2680acfd1b9b64}, + {0x9fbd8a172d5ba3e3}, + {0xb65d86624a857788}, + {0x8f5f05c835f7280e}, + {0x55912db4388961ac}, + {0x4ba9a9471f49b74e}, + {0x6ba36cadf1e045cf}, + {0x230c9509bbc3df0d}, + {0x7148dda3afa76439}, + {0xa6cefd4472568948}, + {0xfe31517282d40690}, + {0x0a6a15cc9722257d}, + {0x447d08ca3148599d}, + {0xb30acd0a43754e5c}, + {0xc05f8e4ea8c3e487}, + {0x8463554672bfb716}, + {0x0d40ccba5e10385a}, + {0xeeaf8d2458ccdb36}, + {0x0d80d3dcc7c75cea}, + {0x8efa09c6df3991a4}, + {0x6867cc75639ee0c3}, + {0xdf30ed86c3d00ffb}, + {0xf7e11ec9c94402f1}, + {0xdb03ecca6a0151e2}, + {0x212bbee264be5b06}, + {0x87025d78a57af15b}, + {0x6139edfb8889032d}, + {0xe67f4da6012c5d24}, + {0x05e3eba376e0b3dd}, + {0xf7edc5d72fdd6ceb}, + {0x031e072678ad18a3}, + {0x290fbc93e184af1e}, + {0xfd3ea450350d666f}, + {0x037d1452c192b1e6}, + {0x15f82c1617013c36}, + {0x5d1f3fdbbb036f8d}, + {0x5089e16d7a676ab1}, + {0x15bb78c31db0a0b6}, + {0xe4a1737fa3d34ccb}, + {0xd2cb7692d690b3a7}, + {0x1bbad843d5971358}, + {0x7ed1fa0b6ec8f9b3}, + {0x529969b7e1e9ac18}, + {0x2f3528a4b9eaa0f7}, + {0x90fefcd350bd2cb8}, + {0xee8da2806a13eecf}, + {0xcd3fb92065d9f373}, + {0x67b89634319c1d36}, + {0x114245b98dcb75bf}, + {0xaff9df030e63e5ba}, + {0x0aebfdf85aae4424}, + {0x4a4462cb0375001e}, + {0xfd9fa5bcb347c01b}, + {0x7ce69eed81f01019}, + {0xcb3d9329d40490b2}, + {0xe0b8bb03c74bb3d0}, + {0xd9a00c9bc93014a8}, + {0x84dc42f5a05f77cf}, + {0x420d4dd413053980}, + {0xff7640b46d72b337}, + {0x711ef85045b8c26e}, + {0xe553dba6592293d8}, + {0x9ebbaf63ffe9d9ef}, + {0x00e978d394512cfd}, + {0x6f9735c02faf6aae}, + {0x8258ddd6a1d0849b}, + {0xe5e83d31e64273f8}, + {0x35128087963cd5be}, + {0x9de6ace9a0e62f44}, + {0xd4c36ab962153420}, + {0xe77aa2f3c90a4e84}, + {0x9f6881f6d7a91658}, + {0x6c6c1fd51e28a1e7}, + {0x867d47a7d8376402}, + {0x79c5f00d243e3097}, + {0x8f0e96b4f71f724f}, + {0xcccb5077d978def4}, + {0x8ad213dddedc9022}, + {0x6aa0ff881da270e7}, + {0xf616642579ba5850}, + {0x5205a666f976d42f}, + {0xa139c29e97fcefb4}, + {0xdb402bd08d522f34}, + {0x2f2c0ff3ff235bd6}, + {0xa0316b536c8b7540}, + {0x260a354b925afeaf}, + {0x567d295828f1b08a}, + {0xc24049b9f7ed3105}, + {0x8815d2dffd77a71e}, + {0x2b4a83e7d54d0554}, + {0xf6b0dc07ea8ebeb7}, + {0xbb7be9c7c620f504}, + {0x7465c7c473e53a40}, + {0xa4481f97a8d4d01c}, + {0x0046fd87a21859ac}, + {0xf1db3c1d9542063a}, + {0xaba147637d52efbe}, + {0x298a0fa05c3f355f}, + {0x465e30321a4091f2}, + {0xc40c398f7e80d184}, + {0xa76262c2557be76f}, + {0xaef2954dc3657336}, + {0xa3711cc06f9b86c2}, + {0xcb60232f2f27ace5}, + {0x4c7d7c251c6bfa95}, + {0xf877dea1180b9b90}, + {0x9ce13dcb2fb389cc}, + {0xcbf4f1324081e0a6}, + {0x444dda6d55d76095}, + {0xb2bd99fa559b9062}, + {0x1ed521f6dd691255}, + {0xdfad847a86a126bb}, + {0x2a47feac8dc3ca9c}, + {0xc352bbf3d519256e}, + {0xfdec74b23d8b494b}, + {0x1dd21a1244ca12f1}, + {0xaf9d7a05b0fc3d9e}, + {0xa662be1601e49476}, + {0xd2ce91dbfc209b10}, + {0x57bdc58e4c06fc76}, + {0xe350bffcdc9cb686}, + {0xc7da8e6f0e2fe399}, + {0x984363837811b08a}, + {0xdcd2a403fb01e164}, + {0xb2770dced3cfd9a7}, + {0x0df31e26a7b036a2}, + {0x3f25fe3395b3154c}, + {0x889d47adc9595ffa}, + {0xc04264e8f34ad5c0}, + {0xc222e70e4a79f7c3}, + {0x7c7dd6d9f3761102}, + {0x904f50c5ce8ec6e4}, + {0xb7bff4fbf66be43f}, + {0x776c4aded0bca5d1}, + {0x38ad99a045dc971f}, + {0xb921c3992807dadd}, + {0x68d576c631e61265}, + {0xcede847721873fc2}, + {0x40443974a0a86b8b}, + {0x57111c24801b44a1}, + {0x6a15a9610d10d210}, + {0xb18fb83ee356fb94}, + {0x59b1257242c40109}, + {0x22ef086d7d6ce520}, + {0x76254d1ef50c004c}, + {0x7678588b0adf59df}, + {0x4fffee4065d22bec}, + {0x0dc128f2fd48bf4b}, + {0xfb647d074e53fab6}, + {0x55b7b25821375a02}, + {0x5b877af6e52af19b}, + {0xba26e58923a5da5d}, + {0x52d065d9ccdb8696}, + {0xf0c624dc0385adae}, + {0xb7a5297198a73155}, + {0xda08e9d3961c93f2}, + {0x8328668369631cc1}, + {0xb140168a47d55b92}, + {0x6699616be2c50115}, + {0xcee66d585d689851}, + {0x5771a2c76f36c898}, + {0x5e91a3790c32e2b3}, + {0xe4e11a71fe620e3a}, + {0x1bb363adcf4eb3f8}, + {0xa691936caf4d91d0}, + {0x94466db0d3c10f4b}, + {0x47f52330df2ead11}, + {0x33848be13a2884a3}, + {0xc9f159f60b065f91}, + {0xdd9ca800a7123d6f}, + {0xa090c8ebf8463d05}, + {0xa5c1adeb7919845f}, + {0x58d97e6f3d1aee86}, + {0x71b5fa3761d6726d}, + {0x1980271cfe0da9bd}, + {0x945cdb3cf1f29e52}, + {0x7f0feac6be7def5b}, + /* Dragalia Lost (iOS/Android) */ {2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c index 969935a92..a7ea89a41 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c @@ -441,6 +441,7 @@ VGMSTREAM* init_vgmstream_ogg_vorbis_callbacks(STREAMFILE* sf, ov_callbacks* cal size_t stream_size = ovmi->stream_size ? ovmi->stream_size : get_streamfile_size(sf) - start; + int force_seek = 0; int disable_reordering = ovmi->disable_reordering; @@ -539,12 +540,20 @@ VGMSTREAM* init_vgmstream_ogg_vorbis_callbacks(STREAMFILE* sf, ov_callbacks* cal loop_end_found = 1; } } - else if (strstr(comment,"LOOPMS=") == comment) { /* Sonic Robo Blast 2 */ - /* Convert from milliseconds to samples. */ - /* (x ms) * (y samples/s) / (1000 ms/s) */ - loop_start = atol(strrchr(comment,'=')+1) * sample_rate / 1000; + else if (strstr(comment,"LOOPMS=") == comment) { /* Sonic Robo Blast 2 (PC) */ + loop_start = atol(strrchr(comment,'=')+1) * sample_rate / 1000; /* ms to samples */ loop_flag = (loop_start >= 0); } + else if (strstr(comment,"COMMENT=- loopTime ") == comment) { /* Aristear Remain (PC) */ + loop_start = atol(strrchr(comment,' ')+1) / 1000.0f * sample_rate; /* ms to samples */ + loop_flag = (loop_start >= 0); + + /* files have all page granule positions -1 except a few close to loop. This throws off + * libvorbis seeking (that uses granules), so we need manual fix = slower. Could be detected + * by checking granules in the first new OggS pages (other games from same dev don't use + * loopTime not have wrong granules though) */ + force_seek = 1; + } /* Hatsune Miku Project DIVA games, though only 'Arcade Future Tone' has >4ch files * ENCODER tag is common but ogg_vorbis_encode looks unique enough @@ -562,10 +571,11 @@ VGMSTREAM* init_vgmstream_ogg_vorbis_callbacks(STREAMFILE* sf, ov_callbacks* cal } ogg_vorbis_set_disable_reordering(data, disable_reordering); + ogg_vorbis_set_force_seek(data, force_seek); /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channels,loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; vgmstream->codec_data = data; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index 13b38930c..4d3b0dbdc 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -633,6 +633,22 @@ static VGMSTREAM* init_subfile(txth_header* txth) { if (txth->num_samples) vgmstream->num_samples = txth->num_samples; + /* load some fields for possible calcs */ + if (!txth->channels) + txth->channels = vgmstream->channels; + if (!txth->sample_rate) + txth->sample_rate = vgmstream->sample_rate; + if (!txth->interleave) + txth->interleave = vgmstream->interleave_block_size; + if (!txth->interleave_last) + txth->interleave_last = vgmstream->interleave_last_block_size; + //if (!txth->loop_flag) //? + // txth->loop_flag = vgmstream->loop_flag; + /* sometimes headers set loop start but getting loop_end before subfile init is hard */ + if (!txth->loop_end_sample && txth->loop_flag) + txth->loop_end_sample = vgmstream->num_samples; + + /* other init */ if (txth->loop_flag) { vgmstream_force_loop(vgmstream, txth->loop_flag, txth->loop_start_sample, txth->loop_end_sample); } @@ -647,19 +663,6 @@ static VGMSTREAM* init_subfile(txth_header* txth) { //todo: other combos with subsongs + subfile? - /* load some fields for possible calcs */ - if (!txth->channels) - txth->channels = vgmstream->channels; - if (!txth->sample_rate) - txth->sample_rate = vgmstream->sample_rate; - if (!txth->interleave) - txth->interleave = vgmstream->interleave_block_size; - if (!txth->interleave_last) - txth->interleave_last = vgmstream->interleave_last_block_size; - //if (!txth->loop_flag) //? - // txth->loop_flag = vgmstream->loop_flag; - - close_streamfile(sf_sub); return vgmstream; @@ -786,12 +789,12 @@ static void set_body_chunk(txth_header* txth) { } } -static int parse_keyval(STREAMFILE* sf, txth_header* txth, const char * key, char * val); -static int parse_num(STREAMFILE* sf, txth_header* txth, const char * val, uint32_t * out_value); -static int parse_string(STREAMFILE* sf, txth_header* txth, const char * val, char * str); -static int parse_coef_table(STREAMFILE* sf, txth_header* txth, const char * val, uint8_t * out_value, size_t out_size); -static int parse_name_table(txth_header* txth, char * val); -static int is_string(const char * val, const char * cmp); +static int parse_keyval(STREAMFILE* sf, txth_header* txth, const char* key, char* val); +static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_t* out_value); +static int parse_string(STREAMFILE* sf, txth_header* txth, const char* val, char* str); +static int parse_coef_table(STREAMFILE* sf, txth_header* txth, const char* val, uint8_t* out_value, size_t out_size); +static int parse_name_table(txth_header* txth, char* val); +static int is_string(const char* val, const char* cmp); static int get_bytes_to_samples(txth_header* txth, uint32_t bytes); static int get_padding_size(txth_header* txth, int discard_empty); @@ -818,24 +821,29 @@ static int parse_txth(txth_header* txth) { } /* read lines */ - while (txt_offset < file_size) { + { char line[TXT_LINE_MAX]; - char key[TXT_LINE_MAX] = {0}, val[TXT_LINE_MAX] = {0}; /* at least as big as a line to avoid overflows (I hope) */ - int ok, bytes_read, line_ok; + char key[TXT_LINE_MAX]; + char val[TXT_LINE_MAX]; + /* at least as big as a line to avoid overflows (I hope) */ - bytes_read = read_line(line, sizeof(line), txt_offset, txth->sf_text, &line_ok); - if (!line_ok) goto fail; - //;VGM_LOG("TXTH: line=%s\n",line); + while (txt_offset < file_size) { + int ok, bytes_read, line_ok; - txt_offset += bytes_read; + bytes_read = read_line(line, sizeof(line), txt_offset, txth->sf_text, &line_ok); + if (!line_ok) goto fail; + //;VGM_LOG("TXTH: line=%s\n",line); - /* get key/val (ignores lead spaces, stops at space/comment/separator) */ - ok = sscanf(line, " %[^ \t#=] = %[^\t#\r\n] ", key,val); - if (ok != 2) /* ignore line if no key=val (comment or garbage) */ - continue; + txt_offset += bytes_read; - if (!parse_keyval(txth->sf, txth, key, val)) /* read key/val */ - goto fail; + /* get key/val (ignores lead spaces, stops at space/comment/separator) */ + ok = sscanf(line, " %[^ \t#=] = %[^\t#\r\n] ", key,val); + if (ok != 2) /* ignore line if no key=val (comment or garbage) */ + continue; + + if (!parse_keyval(txth->sf, txth, key, val)) /* read key/val */ + goto fail; + } } if (!txth->loop_flag_set) @@ -852,7 +860,7 @@ fail: return 0; } -static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char * key, char * val) { +static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, char* val) { //;VGM_LOG("TXTH: key=%s, val=%s\n", key, val); /* CODEC */ @@ -1197,6 +1205,8 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char * key, ch /* HEADER/BODY CONFIG */ else if (is_string(key,"header_file")) { + + /* first remove old head if needed */ if (txth->sf_head_opened) { close_streamfile(txth->sf_head); txth->sf_head = NULL; @@ -1205,7 +1215,10 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char * key, ch if (is_string(val,"null")) { /* reset */ if (!txth->streamfile_is_txth) { - txth->sf_head = txth->sf; + txth->sf_head = txth->sf; /* base non-txth file */ + } + else { + goto fail; /* .txth, nothing to fall back */ } } else if (val[0]=='*' && val[1]=='.') { /* basename + extension */ @@ -1222,6 +1235,8 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char * key, ch } } else if (is_string(key,"body_file")) { + + /* first remove old body if needed */ if (txth->sf_body_opened) { close_streamfile(txth->sf_body); txth->sf_body = NULL; @@ -1230,7 +1245,10 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char * key, ch if (is_string(val,"null")) { /* reset */ if (!txth->streamfile_is_txth) { - txth->sf_body = txth->sf; + txth->sf_body = txth->sf; /* base non-txth file */ + } + else { + goto fail; /* .txth, nothing to fall back */ } } else if (val[0]=='*' && val[1]=='.') { /* basename + extension */ @@ -1316,10 +1334,11 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char * key, ch return 1; fail: + VGM_LOG("TXTH: error parsing key=%s, val=%s\n", key, val); return 0; } -static int is_substring(const char * val, const char * cmp, int inline_field) { +static int is_substring(const char* val, const char* cmp, int inline_field) { char chr; int len = strlen(cmp); /* "val" must contain "cmp" entirely */ @@ -1339,7 +1358,7 @@ static int is_substring(const char * val, const char * cmp, int inline_field) { return len; } -static int is_string(const char * val, const char * cmp) { +static int is_string(const char* val, const char* cmp) { int len = is_substring(val, cmp, 0); if (!len) return 0; @@ -1353,11 +1372,11 @@ static int is_string(const char * val, const char * cmp) { return len; } -static int is_string_field(const char * val, const char * cmp) { +static int is_string_field(const char* val, const char* cmp) { return is_substring(val, cmp, 1); } -static uint16_t get_string_wchar(const char * val, int pos, int *csize) { +static uint16_t get_string_wchar(const char* val, int pos, int* csize) { uint16_t wchar = 0; if ((val[pos] & 0x80) && val[pos+1] != '\0') { @@ -1379,10 +1398,11 @@ static uint16_t get_string_wchar(const char * val, int pos, int *csize) { return wchar; } -static int is_string_match(const char * text, const char * pattern) { - int t_pos = 0, p_pos = 0; +static int is_string_match(const char* text, const char* pattern) { + int t_pos = 0, p_pos = 0, t_len = 0, p_len = 0; int p_size, t_size; uint16_t p_char, t_char; + //;VGM_LOG("TXTH: match '%s' vs '%s'\n", text,pattern); /* compares 2 strings (case insensitive, to a point) allowing wildcards @@ -1390,7 +1410,7 @@ static int is_string_match(const char * text, const char * pattern) { * * does some bleh UTF-8 handling, consuming dual bytes if needed (codepages set char's eighth bit). * as such it's slower than standard funcs, but it's not like we need it to be ultra fast. - * */ + */ while (text[t_pos] != '\0' && pattern[p_pos] != '\0') { //;VGM_LOG("TXTH: compare '%s' vs '%s'\n", (text+t_pos), (pattern+p_pos)); @@ -1402,10 +1422,28 @@ static int is_string_match(const char * text, const char * pattern) { while(text[t_pos] != '\0') { t_char = get_string_wchar(text, t_pos, &t_size); - //;VGM_LOG("TXTH: consume %i '%s'\n", t_size, (text+t_pos) ); + //;VGM_LOG("TXTH: consume %i '%s'\n", t_size, (text+t_pos)); - if (t_char == p_char) - break; + /* break from this wildcard (AKA possible match = stop consuming) only if: + * - rest of string has the same length (=could be a match) + * - there are more wildcards (=would consume other parts) + * otherwise current wildcard must keep consuming text (without this, + * sound_1_1.adx vs *_1.adx wouldn't match since the _ would stop too early) + */ + if (t_char == p_char) { + if (strchr(&pattern[p_pos], '*')) + break; + + if (!t_len || !p_len) { /* lazy init helpful? */ + t_len = strlen(text); + p_len = strlen(pattern); + } + + //;VGM_LOG("TXTH: possible match '%s' vs '%s'\n", (text+t_pos), (pattern+p_pos)); + /* not strcmp to allow case insensitive-ness, handled below */ + if (t_len - t_pos == p_len - p_pos) + break; + } t_pos += t_size; } } @@ -1415,21 +1453,24 @@ static int is_string_match(const char * text, const char * pattern) { p_pos++; t_pos += t_size; } - else { /* must match 1:1 */ + else { /* must match char 1:1 */ + //;VGM_LOG("TXTH: test 1:1 '%s' vs '%s'\n", (text+t_pos), (pattern+p_pos)); t_char = get_string_wchar(text, t_pos, &t_size); p_char = get_string_wchar(pattern, p_pos, &p_size); if (p_char != t_char) break; - p_pos += p_size; t_pos += t_size; + p_pos += p_size; } } + //;VGM_LOG("TXTH: current '%s' vs '%s'\n", (text+t_pos), (pattern+p_pos)); //;VGM_LOG("TXTH: match '%s' vs '%s' = %s\n", text,pattern, (text[t_pos] == '\0' && pattern[p_pos] == '\0') ? "true" : "false"); + /* either all chars consumed/matched and both pos point to null, or one didn't so string didn't match */ return text[t_pos] == '\0' && pattern[p_pos] == '\0'; } -static int parse_string(STREAMFILE* sf, txth_header* txth, const char * val, char * str) { +static int parse_string(STREAMFILE* sf, txth_header* txth, const char* val, char* str) { int n = 0; /* read string without trailing spaces */ @@ -1438,7 +1479,7 @@ static int parse_string(STREAMFILE* sf, txth_header* txth, const char * val, cha return n; } -static int parse_coef_table(STREAMFILE* sf, txth_header* txth, const char * val, uint8_t * out_value, size_t out_size) { +static int parse_coef_table(STREAMFILE* sf, txth_header* txth, const char* val, uint8_t* out_value, size_t out_size) { uint32_t byte; int done = 0; @@ -1466,7 +1507,7 @@ fail: return 0; } -static int parse_name_table(txth_header* txth, char * name_list) { +static int parse_name_table(txth_header* txth, char* name_list) { STREAMFILE* nameFile = NULL; off_t txt_offset, file_size; char fullname[PATH_LIMIT]; @@ -1516,55 +1557,59 @@ static int parse_name_table(txth_header* txth, char * name_list) { txth->name_values_count = 0; /* read lines and find target filename, format is (filename): value1, ... valueN */ - while (txt_offset < file_size) { + { char line[TXT_LINE_MAX]; - char key[TXT_LINE_MAX] = {0}, val[TXT_LINE_MAX] = {0}; - int ok, bytes_read, line_ok; + char key[TXT_LINE_MAX]; + char val[TXT_LINE_MAX]; - bytes_read = read_line(line, sizeof(line), txt_offset, nameFile, &line_ok); - if (!line_ok) goto fail; - //;VGM_LOG("TXTH: line=%s\n",line); + while (txt_offset < file_size) { + int ok, bytes_read, line_ok; - txt_offset += bytes_read; + bytes_read = read_line(line, sizeof(line), txt_offset, nameFile, &line_ok); + if (!line_ok) goto fail; + //;VGM_LOG("TXTH: line=%s\n",line); - /* get key/val (ignores lead spaces, stops at space/comment/separator) */ - ok = sscanf(line, " %[^ \t#:] : %[^\t#\r\n] ", key,val); - if (ok != 2) { /* ignore line if no key=val (comment or garbage) */ - /* try again with " (empty): (val)) */ - key[0] = '\0'; - ok = sscanf(line, " : %[^\t#\r\n] ", val); - if (ok != 1) - continue; - } + txt_offset += bytes_read; - - //;VGM_LOG("TXTH: compare name '%s'\n", key); - /* parse values if key (name) matches default ("") or filename with/without extension */ - if (key[0]=='\0' - || is_string_match(filename, key) - || is_string_match(basename, key) - || is_string_match(fullname, key)) { - int n; - char subval[TXT_LINE_MAX]; - const char *current = val; - - while (current[0] != '\0') { - ok = sscanf(current, " %[^\t#\r\n,]%n ", subval, &n); + /* get key/val (ignores lead spaces, stops at space/comment/separator) */ + ok = sscanf(line, " %[^ \t#:] : %[^\t#\r\n] ", key,val); + if (ok != 2) { /* ignore line if no key=val (comment or garbage) */ + /* try again with " (empty): (val)) */ + key[0] = '\0'; + ok = sscanf(line, " : %[^\t#\r\n] ", val); if (ok != 1) - goto fail; - - current += n; - if (current[0] == ',') - current++; - - if (!parse_num(txth->sf_head,txth,subval, &txth->name_values[txth->name_values_count])) goto fail; - txth->name_values_count++; - if (txth->name_values_count >= 16) /* surely nobody needs that many */ - goto fail; + continue; } - //;VGM_LOG("TXTH: found name '%s'\n", key); - break; /* target found */ + + //;VGM_LOG("TXTH: compare name '%s'\n", key); + /* parse values if key (name) matches default ("") or filename with/without extension */ + if (key[0]=='\0' + || is_string_match(filename, key) + || is_string_match(basename, key) + || is_string_match(fullname, key)) { + int n; + char subval[TXT_LINE_MAX]; + const char *current = val; + + while (current[0] != '\0') { + ok = sscanf(current, " %[^\t#\r\n,]%n ", subval, &n); + if (ok != 1) + goto fail; + + current += n; + if (current[0] == ',') + current++; + + if (!parse_num(txth->sf_head,txth,subval, &txth->name_values[txth->name_values_count])) goto fail; + txth->name_values_count++; + if (txth->name_values_count >= 16) /* surely nobody needs that many */ + goto fail; + } + + //;VGM_LOG("TXTH: found name '%s'\n", key); + break; /* target found */ + } } } @@ -1578,7 +1623,7 @@ fail: } -static int parse_num(STREAMFILE* sf, txth_header* txth, const char * val, uint32_t * out_value) { +static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_t* out_value) { /* out_value can be these, save before modifying */ uint32_t value_mul = txth->value_mul; uint32_t value_div = txth->value_div; @@ -1624,8 +1669,10 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char * val, uint32 int hex = (val[1]=='0' && val[2]=='x'); /* can happen when loading .txth and not setting body/head */ - if (!sf) + if (!sf) { + VGM_LOG("TXTH: wrong header\n"); goto fail; + } /* read exactly N fields in the expected format */ if (strchr(val,':') && strchr(val,'$')) { @@ -1641,8 +1688,10 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char * val, uint32 /* adjust offset */ offset += txth->base_offset; - if (/*offset < 0 ||*/ offset > get_streamfile_size(sf)) + if (/*offset < 0 ||*/ offset > get_streamfile_size(sf)) { + VGM_LOG("TXTH: wrong offset %x + %x\n", offset - txth->base_offset, txth->base_offset); goto fail; + } if (ed1 == 'B' && ed2 == 'E') big_endian = 1; @@ -1652,11 +1701,12 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char * val, uint32 if (subsong_spacing) offset = offset + subsong_spacing * (txth->target_subsong - 1); + //;VGM_LOG("TXTH: read at offset %x + %x\n", offset - txth->base_offset, txth->base_offset); switch(size) { - case 1: value = (uint8_t)read_8bit(offset,sf); break; - case 2: value = big_endian ? (uint16_t)read_16bitBE(offset,sf) : (uint16_t)read_16bitLE(offset,sf); break; - case 3: value = (big_endian ? (uint32_t)read_32bitBE(offset,sf) : (uint32_t)read_32bitLE(offset,sf)) & 0x00FFFFFF; break; - case 4: value = big_endian ? (uint32_t)read_32bitBE(offset,sf) : (uint32_t)read_32bitLE(offset,sf); break; + case 1: value = read_u8(offset,sf); break; + case 2: value = big_endian ? read_u16be(offset,sf) : read_u16le(offset,sf); break; + case 3: value = (big_endian ? read_u32be(offset,sf) : read_u32le(offset,sf)) & 0x00FFFFFF; break; + case 4: value = big_endian ? read_u32be(offset,sf) : read_u32le(offset,sf); break; default: goto fail; } value_read = 1; @@ -1728,7 +1778,7 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char * val, uint32 /* move to next field (if any) */ val += n; - //;VGM_LOG("TXTH: val='%s', n=%i, brackets=%i, result=%i\n", val, n, brackets, result); + //;VGM_LOG("TXTH: val='%s', n=%i, brackets=%i, result=0x%x\n", val, n, brackets, result); } /* unbalanced brackets */ @@ -1750,6 +1800,7 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char * val, uint32 //;VGM_LOG("TXTH: final result %u (0x%x)\n", result, result); return 1; fail: + VGM_LOG("TXTH: error parsing num '%s'\n", val); return 0; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c index ea75dfdfe..4c85208e3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txtp.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txtp.c @@ -1262,7 +1262,7 @@ static inline int is_match(const char* str1, const char* str2) { static void parse_params(txtp_entry* entry, char* params) { /* parse params: #(commands) */ int n, nc, nm, mc; - char command[TXTP_LINE_MAX] = {0}; + char command[TXTP_LINE_MAX]; play_config_t* tcfg = &entry->config; entry->range_start = 0; @@ -1793,7 +1793,7 @@ fail: static int is_substring(const char* val, const char* cmp) { int n; - char subval[TXTP_LINE_MAX] = {0}; + char subval[TXTP_LINE_MAX]; /* read string without trailing spaces or comments/commands */ if (sscanf(val, " %s%n[^ #\t\r\n]%n", subval, &n, &n) != 1) @@ -1897,42 +1897,48 @@ static txtp_header* parse_txtp(STREAMFILE* sf) { /* read and parse lines */ - while (txt_offset < file_size) { + { char line[TXTP_LINE_MAX]; - char key[TXTP_LINE_MAX] = {0}, val[TXTP_LINE_MAX] = {0}; /* at least as big as a line to avoid overflows (I hope) */ - char filename[TXTP_LINE_MAX] = {0}; - int ok, bytes_read, line_ok; + char key[TXTP_LINE_MAX]; + char val[TXTP_LINE_MAX]; + char filename[TXTP_LINE_MAX]; + /* at least as big as a line to avoid overflows (I hope) */ - bytes_read = read_line(line, sizeof(line), txt_offset, sf, &line_ok); - if (!line_ok) goto fail; + while (txt_offset < file_size) { + int ok, bytes_read, line_ok; - txt_offset += bytes_read; + bytes_read = read_line(line, sizeof(line), txt_offset, sf, &line_ok); + if (!line_ok) goto fail; - /* get key/val (ignores lead/trail spaces, # may be commands or comments) */ - ok = sscanf(line, " %[^ \t#=] = %[^\t\r\n] ", key,val); - if (ok == 2) { /* key=val */ - if (!parse_keyval(txtp, key, val)) /* read key/val */ + txt_offset += bytes_read; + + /* try key/val (ignores lead/trail spaces, # may be commands or comments) */ + ok = sscanf(line, " %[^ \t#=] = %[^\t\r\n] ", key,val); + if (ok == 2) { /* key=val */ + if (!parse_keyval(txtp, key, val)) /* read key/val */ + goto fail; + continue; + } + + /* must be a filename (only remove spaces from start/end, as filenames con contain mid spaces/#/etc) */ + ok = sscanf(line, " %[^\t\r\n] ", filename); + if (ok != 1) /* not a filename either */ + continue; + if (filename[0] == '#') + continue; /* simple comment */ + + /* filename with settings */ + if (!add_entry(txtp, filename, 0)) goto fail; - continue; } - - /* must be a filename (only remove spaces from start/end, as filenames con contain mid spaces/#/etc) */ - ok = sscanf(line, " %[^\t\r\n] ", filename); - if (ok != 1) /* not a filename either */ - continue; - if (filename[0] == '#') - continue; /* simple comment */ - - /* filename with settings */ - if (!add_entry(txtp, filename, 0)) - goto fail; } /* mini-txth: if no entries are set try with filename, ex. from "song.ext#3.txtp" use "song.ext#3" * (it's possible to have default "commands" inside the .txtp plus filename+settings) */ if (txtp->entry_count == 0) { - char filename[PATH_LIMIT] = {0}; + char filename[PATH_LIMIT]; + filename[0] = '\0'; get_streamfile_basename(sf, filename, sizeof(filename)); add_entry(txtp, filename, 0);