From ab6429b62b0fad9b895d08a158dfdad95b176c4b Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Thu, 4 Mar 2021 15:32:52 -0800 Subject: [PATCH] Updated VGMStream to r1050-3618-gfbf7bcaa --- .../libvgmstream.xcodeproj/project.pbxproj | 24 + .../vgmstream/vgmstream/src/coding/coding.h | 14 +- .../vgmstream/src/coding/mpeg_custom_utils.c | 16 +- .../vgmstream/src/coding/tac_decoder.c | 187 +++ .../vgmstream/src/coding/tac_decoder_lib.c | 1282 +++++++++++++++++ .../vgmstream/src/coding/tac_decoder_lib.h | 55 + .../src/coding/tac_decoder_lib_data.h | 1002 +++++++++++++ .../src/coding/tac_decoder_lib_ops.h | 376 +++++ .../vgmstream/src/coding/xa_decoder.c | 131 +- Frameworks/vgmstream/vgmstream/src/decode.c | 56 +- Frameworks/vgmstream/vgmstream/src/formats.c | 7 +- .../vgmstream/src/layout/blocked_xa.c | 56 +- Frameworks/vgmstream/vgmstream/src/meta/adx.c | 90 +- .../vgmstream/vgmstream/src/meta/adx_keys.h | 2 +- Frameworks/vgmstream/vgmstream/src/meta/awb.c | 2 +- .../vgmstream/vgmstream/src/meta/hca_keys.h | 3 + .../vgmstream/vgmstream/src/meta/ktss.c | 63 +- .../vgmstream/vgmstream/src/meta/meta.h | 5 +- Frameworks/vgmstream/vgmstream/src/meta/nub.c | 207 +-- .../vgmstream/vgmstream/src/meta/ogg_vorbis.c | 47 +- .../vgmstream/vgmstream/src/meta/opus.c | 42 +- .../vgmstream/vgmstream/src/meta/ps2_2pfs.c | 48 +- Frameworks/vgmstream/vgmstream/src/meta/rsd.c | 458 +++--- Frameworks/vgmstream/vgmstream/src/meta/tac.c | 69 + .../vgmstream/vgmstream/src/meta/ubi_bao.c | 75 +- .../vgmstream/vgmstream/src/meta/ubi_hx.c | 241 ++-- .../vgmstream/vgmstream/src/meta/ubi_raki.c | 537 +++---- .../vgmstream/vgmstream/src/meta/ubi_sb.c | 1 + Frameworks/vgmstream/vgmstream/src/meta/xa.c | 74 +- Frameworks/vgmstream/vgmstream/src/meta/xwb.c | 9 +- .../vgmstream/vgmstream/src/vgmstream.c | 1 + .../vgmstream/vgmstream/src/vgmstream.h | 11 +- 32 files changed, 4164 insertions(+), 1027 deletions(-) create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/tac_decoder.c create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.c create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.h create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib_data.h create mode 100644 Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib_ops.h create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/tac.c diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index fc7473348..b1fa62246 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -619,6 +619,12 @@ 83D7318A1A749D2200CA1366 /* g719.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83D7313E1A74968A00CA1366 /* g719.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 83D7318C1A749EEE00CA1366 /* g719_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83D7318B1A749EEE00CA1366 /* g719_decoder.c */; }; 83E56BA51F2EE3520026BC60 /* vorbis_custom_utils_ogl.c in Sources */ = {isa = PBXBuildFile; fileRef = 83E56BA01F2EE3500026BC60 /* vorbis_custom_utils_ogl.c */; }; + 83E7FD5F25EF2B0C00683FD2 /* tac_decoder.c in Sources */ = {isa = PBXBuildFile; fileRef = 83E7FD5725EF2B0C00683FD2 /* tac_decoder.c */; }; + 83E7FD6025EF2B0C00683FD2 /* tac_decoder_lib.c in Sources */ = {isa = PBXBuildFile; fileRef = 83E7FD5B25EF2B0C00683FD2 /* tac_decoder_lib.c */; }; + 83E7FD6125EF2B0C00683FD2 /* tac_decoder_lib.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E7FD5C25EF2B0C00683FD2 /* tac_decoder_lib.h */; }; + 83E7FD6225EF2B0C00683FD2 /* tac_decoder_lib_ops.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E7FD5D25EF2B0C00683FD2 /* tac_decoder_lib_ops.h */; }; + 83E7FD6325EF2B0C00683FD2 /* tac_decoder_lib_data.h in Headers */ = {isa = PBXBuildFile; fileRef = 83E7FD5E25EF2B0C00683FD2 /* tac_decoder_lib_data.h */; }; + 83E7FD6525EF2B2400683FD2 /* tac.c in Sources */ = {isa = PBXBuildFile; fileRef = 83E7FD6425EF2B2400683FD2 /* tac.c */; }; 83EDE5D81A70951A005F5D84 /* mca.c in Sources */ = {isa = PBXBuildFile; fileRef = 83EDE5D61A70951A005F5D84 /* mca.c */; }; 83EDE5D91A70951A005F5D84 /* btsnd.c in Sources */ = {isa = PBXBuildFile; fileRef = 83EDE5D71A70951A005F5D84 /* btsnd.c */; }; 83EED5D3203A8BC7008BEB45 /* ea_swvr.c in Sources */ = {isa = PBXBuildFile; fileRef = 83EED5D1203A8BC7008BEB45 /* ea_swvr.c */; }; @@ -1369,6 +1375,12 @@ 83D731381A74968900CA1366 /* g719.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = g719.xcodeproj; path = ../g719/g719.xcodeproj; sourceTree = ""; }; 83D7318B1A749EEE00CA1366 /* g719_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = g719_decoder.c; sourceTree = ""; }; 83E56BA01F2EE3500026BC60 /* vorbis_custom_utils_ogl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = vorbis_custom_utils_ogl.c; sourceTree = ""; }; + 83E7FD5725EF2B0C00683FD2 /* tac_decoder.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tac_decoder.c; sourceTree = ""; }; + 83E7FD5B25EF2B0C00683FD2 /* tac_decoder_lib.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tac_decoder_lib.c; sourceTree = ""; }; + 83E7FD5C25EF2B0C00683FD2 /* tac_decoder_lib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tac_decoder_lib.h; sourceTree = ""; }; + 83E7FD5D25EF2B0C00683FD2 /* tac_decoder_lib_ops.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tac_decoder_lib_ops.h; sourceTree = ""; }; + 83E7FD5E25EF2B0C00683FD2 /* tac_decoder_lib_data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tac_decoder_lib_data.h; sourceTree = ""; }; + 83E7FD6425EF2B2400683FD2 /* tac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tac.c; sourceTree = ""; }; 83EDE5D61A70951A005F5D84 /* mca.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mca.c; sourceTree = ""; }; 83EDE5D71A70951A005F5D84 /* btsnd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = btsnd.c; sourceTree = ""; }; 83EED5D1203A8BC7008BEB45 /* ea_swvr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ea_swvr.c; sourceTree = ""; }; @@ -1621,6 +1633,11 @@ 836F6DFB18BDC2180095E648 /* SASSC_decoder.c */, 836F6DFC18BDC2180095E648 /* sdx2_decoder.c */, 8346D97E25BF83B200D1A8B0 /* speex_decoder.c */, + 83E7FD5E25EF2B0C00683FD2 /* tac_decoder_lib_data.h */, + 83E7FD5D25EF2B0C00683FD2 /* tac_decoder_lib_ops.h */, + 83E7FD5B25EF2B0C00683FD2 /* tac_decoder_lib.c */, + 83E7FD5C25EF2B0C00683FD2 /* tac_decoder_lib.h */, + 83E7FD5725EF2B0C00683FD2 /* tac_decoder.c */, 8373341023F60C7A00DE14DC /* tgcadpcm_decoder.c */, 837CEA7623487E2400E62A4A /* ubi_adpcm_decoder.c */, 83F1EE2C245D4FB20076E182 /* vadpcm_decoder.c */, @@ -2055,6 +2072,7 @@ 83D0381724A4129A004CF90F /* swav.c */, 831BA6121EAC61A500CF89B0 /* sxd.c */, 83709E031ECBC1A4005C03D3 /* ta_aac.c */, + 83E7FD6425EF2B2400683FD2 /* tac.c */, 8373342E23F60D4100DE14DC /* tgc.c */, 836F6EFA18BDC2190095E648 /* thp.c */, 836F6EFB18BDC2190095E648 /* tun.c */, @@ -2185,7 +2203,10 @@ 83C7282222BC893D00678B4A /* mta2_streamfile.h in Headers */, 83AA7F802519C042004C5298 /* sab_streamfile.h in Headers */, 83A21F87201D8981000F04B9 /* fsb_keys.h in Headers */, + 83E7FD6325EF2B0C00683FD2 /* tac_decoder_lib_data.h in Headers */, + 83E7FD6125EF2B0C00683FD2 /* tac_decoder_lib.h in Headers */, 834FE0EC215C79ED000A5D3D /* kma9_streamfile.h in Headers */, + 83E7FD6225EF2B0C00683FD2 /* tac_decoder_lib_ops.h in Headers */, 8349A90F1FE6258200E26435 /* aix_streamfile.h in Headers */, 836F705718BDC2190095E648 /* util.h in Headers */, 83C7282722BC8C1500678B4A /* plugins.h in Headers */, @@ -2468,6 +2489,7 @@ 832BF82821E0514B006F50F1 /* xwma.c in Sources */, 83FC176E23AC58D100E1025F /* csb.c in Sources */, 8306B0EB20984590000302D4 /* wave_segmented.c in Sources */, + 83E7FD5F25EF2B0C00683FD2 /* tac_decoder.c in Sources */, 836F6F9F18BDC2190095E648 /* musc.c in Sources */, 8349A9121FE6258200E26435 /* vsf_tta.c in Sources */, 836F6FCA18BDC2190095E648 /* ps2_adm.c in Sources */, @@ -2490,6 +2512,7 @@ 8346D97C25BF838C00D1A8B0 /* mjb_mjh.c in Sources */, 8373341823F60C7B00DE14DC /* tgcadpcm_decoder.c in Sources */, 836F6F3318BDC2190095E648 /* ngc_dtk_decoder.c in Sources */, + 83E7FD6525EF2B2400683FD2 /* tac.c in Sources */, 83C7281322BC893D00678B4A /* mta2.c in Sources */, 8306B0EF20984590000302D4 /* ubi_bao.c in Sources */, 836F6FBB18BDC2190095E648 /* ngca.c in Sources */, @@ -2701,6 +2724,7 @@ 836F700E18BDC2190095E648 /* ps2_xa2.c in Sources */, 837CEB0023487F2C00E62A4A /* smk.c in Sources */, 83C7281022BC893D00678B4A /* nps.c in Sources */, + 83E7FD6025EF2B0C00683FD2 /* tac_decoder_lib.c in Sources */, 83C7281E22BC893D00678B4A /* msf.c in Sources */, 836F6FF718BDC2190095E648 /* ps2_sl3.c in Sources */, 8351F32D2212B57000A606E4 /* 208.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index f9ec5b3b1..42fb19e70 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -110,8 +110,8 @@ void decode_hevag(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing /* xa_decoder */ -void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); -size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2); +void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_xa8); +size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2, int bps); /* ea_xa_decoder */ @@ -317,6 +317,16 @@ clHCA_stInfo* hca_get_info(hca_codec_data* data); STREAMFILE* hca_get_streamfile(hca_codec_data* data); +/* tac_decoder */ +typedef struct tac_codec_data tac_codec_data; + +tac_codec_data* init_tac(STREAMFILE* sf); +void decode_tac(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do); +void reset_tac(tac_codec_data* data); +void seek_tac(tac_codec_data* data, int32_t num_sample); +void free_tac(tac_codec_data* data); + + #ifdef VGM_USE_VORBIS /* ogg_vorbis_decoder */ typedef struct ogg_vorbis_codec_data ogg_vorbis_codec_data; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c index f066ab4fd..451b1d1cc 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils.c @@ -442,8 +442,10 @@ int32_t mpeg_get_samples_clean(STREAMFILE *sf, off_t start, size_t size, size_t* goto fail; num_samples = size / info.frame_size * info.frame_samples; - loop_start = *p_loop_start / info.frame_size * info.frame_samples; - loop_end = *p_loop_end / info.frame_size * info.frame_samples; + if (p_loop_start) + loop_start = *p_loop_start / info.frame_size * info.frame_samples; + if (p_loop_end) + loop_end = *p_loop_end / info.frame_size * info.frame_samples; } else { /* VBR (or unknown) = count frames */ @@ -451,20 +453,22 @@ int32_t mpeg_get_samples_clean(STREAMFILE *sf, off_t start, size_t size, size_t* if (!mpeg_get_frame_info(sf, offset, &info)) goto fail; - if (*p_loop_start + start == offset) + if (p_loop_start && *p_loop_start + start == offset) loop_start = num_samples; num_samples += info.frame_samples; offset += info.frame_size; - if (*p_loop_end + start == offset) + if (p_loop_end && *p_loop_end + start == offset) loop_end = num_samples; } } - *p_loop_start = loop_start; - *p_loop_end = loop_end; + if (p_loop_start) + *p_loop_start = loop_start; + if (p_loop_end) + *p_loop_end = loop_end; return num_samples; fail: diff --git a/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder.c new file mode 100644 index 000000000..2cce8df6b --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder.c @@ -0,0 +1,187 @@ +#include "coding.h" +#include "coding_utils_samples.h" + +#include "tac_decoder_lib.h" + + +/* opaque struct */ +struct tac_codec_data { + /* config */ + int channels; + int samples_discard; + int encoder_delay; + + uint8_t buf[TAC_BLOCK_SIZE]; + int feed_block; + off_t offset; + + int16_t* samples; + int frame_samples; + + /* frame state */ + s16buf_t sbuf; + + void* handle; +}; + + +/* raw SPEEX */ +tac_codec_data* init_tac(STREAMFILE* sf) { + tac_codec_data* data = NULL; + int bytes; + + + data = calloc(1, sizeof(tac_codec_data)); + if (!data) goto fail; + + bytes = read_streamfile(data->buf, 0x00, sizeof(data->buf), sf); + data->handle = tac_init(data->buf, bytes); + if (!data->handle) goto fail; + + data->feed_block = 0; /* ok to use current block */ + data->offset = bytes; + data->channels = TAC_CHANNELS; + data->frame_samples = TAC_FRAME_SAMPLES; + + data->encoder_delay = 0; + data->samples_discard = data->encoder_delay; + + data->samples = malloc(data->channels * data->frame_samples * sizeof(int16_t)); + if (!data->samples) goto fail; + + return data; +fail: + free_tac(data); + return NULL; +} + + +static int decode_frame(tac_codec_data* data) { + int err; + + data->sbuf.samples = data->samples; + data->sbuf.channels = 2; + data->sbuf.filled = 0; + + err = tac_decode_frame(data->handle, data->buf); + + if (err == TAC_PROCESS_NEXT_BLOCK) { + data->feed_block = 1; + return 1; + } + + if (err == TAC_PROCESS_DONE) { + VGM_LOG("TAC: process done (EOF) %i\n", err); + goto fail; /* shouldn't reach this */ + } + + if (err != TAC_PROCESS_OK) { + VGM_LOG("TAC: process error %i\n", err); + goto fail; + } + + + tac_get_samples_pcm16(data->handle, data->sbuf.samples); + data->sbuf.filled = data->frame_samples; + + return 1; +fail: + return 0; +} + +static int read_frame(tac_codec_data* data, STREAMFILE* sf) { + + /* new block must be read only when signaled by lib */ + if (data->feed_block) { + int bytes = read_streamfile(data->buf, data->offset, sizeof(data->buf), sf); + data->offset += bytes; + data->feed_block = 0; + if (bytes <= 0) goto fail; /* can read less that buf near EOF */ + } + + return 1; +fail: + return 0; +} + +void decode_tac(VGMSTREAM* vgmstream, sample_t* outbuf, int32_t samples_to_do) { + VGMSTREAMCHANNEL* stream = &vgmstream->ch[0]; + tac_codec_data* data = vgmstream->codec_data; + int ok; + + + while (samples_to_do > 0) { + s16buf_t* sbuf = &data->sbuf; + + if (sbuf->filled <= 0) { + ok = read_frame(data, stream->streamfile); + if (!ok) goto fail; + + ok = decode_frame(data); + if (!ok) goto fail; + } + + if (data->samples_discard) + s16buf_discard(&outbuf, sbuf, &data->samples_discard); + else + s16buf_consume(&outbuf, sbuf, &samples_to_do); + } + + return; + +fail: + /* on error just put some 0 samples */ + VGM_LOG("TAC: decode fail at %x, missing %i samples\n", (uint32_t)data->offset, samples_to_do); + s16buf_silence(&outbuf, &samples_to_do, data->channels); +} + + +void reset_tac(tac_codec_data* data) { + if (!data) return; + + tac_reset(data->handle); + + data->offset = 0; + data->feed_block = 1; + data->sbuf.filled = 0; + data->samples_discard = data->encoder_delay; + + return; +} + +void seek_tac(tac_codec_data* data, int32_t num_sample) { + int32_t loop_sample; + const tac_header_t* hdr; + + if (!data) + return; + + hdr = tac_get_header(data->handle); + + loop_sample = (hdr->loop_frame - 1) * TAC_FRAME_SAMPLES + hdr->loop_discard; + if (loop_sample == num_sample) { + tac_set_loop(data->handle); /* direct looping */ + + data->samples_discard = hdr->loop_discard; + data->offset = hdr->loop_offset; + data->feed_block = 1; + data->sbuf.filled = 0; + } + else { + tac_reset(data->handle); + + data->samples_discard = num_sample; + data->offset = 0; + data->feed_block = 1; + data->sbuf.filled = 0; + } +} + +void free_tac(tac_codec_data* data) { + if (!data) + return; + + tac_free(data->handle); + free(data->samples); + free(data); +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.c b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.c new file mode 100644 index 000000000..5b06f5f08 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.c @@ -0,0 +1,1282 @@ +#include +#include +#include +#include +#include + +/* tri-Ace PS2 DCT-style codec. + * + * Adapted from Nisto's decoder w/ VU1 emulation: + * - https://github.com/Nisto/pk3dec + * Info: + * - https://psi-rockin.github.io/ps2tek/ + * - 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). + * + * 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 + * was tailored to do multiple ops at once. This code tries to simplify it into standard C to a point, + * but keeps this vector style in main decoding to ease porting (since tables are made with SIMD in + * mind it would need some transposing around) and for PS2 float simulation. + * + * Codec returns float samples then converted to PCM16. Output samples are +-1 vs Nisto's/PCSX2's + * results, due to various quirks: + * - simplified PS2 float handling (PS2 VU floats don't map 1:1 to PC IEEE floats), can be re-enabled (slow) + * - various heisenbugs (PC 80b register floats <> 32b memory floats conversions, see transform()). + * + * Files are divided into blocks (size 0x4E000). At file start is a simple header and huffman codebook + * then N VBR frames (of size around 0x200~300) containing huffman codes of spectral data. A frame has + * codes for 2 channels, decoded separatedly (first all L then all R), then handle joint stereo. + * Channel spectrum coefs are processeed, then MDCT(?) + window overlap to get final samples. When a + * "block end frame" is found, handler must get next block and resume decoding (blocks may be pre/post + * padded, for looping porposes). Game reads a couple of blocks at once though. + */ + +/**********************************************************************************/ +/* DEFINITIONS */ +/**********************************************************************************/ +#include "tac_decoder_lib_data.h" +#include "tac_decoder_lib_ops.h" +#include "tac_decoder_lib.h" + +//#define TAC_MAX_FRAME_SIZE 0x300 /* typically around ~0x1d0, observed max is ~0x2e2 */ +#define TAC_CODED_BANDS 27 +#define TAC_CODED_COEFS 32 +#define TAC_TOTAL_POINTS 32 /* not sure about this term */ +#define TAC_SCALE_TABLE_MAX_INDEX 511 + + +struct tac_handle_t { + /* base header */ + tac_header_t header; + + /* general state */ + int data_start; /* first frame after huffman tables, within first block */ + int frame_offset; /* current position within block */ + int frame_number; /* frames must be sequential */ + + /* decoding huffman tree state */ + int16_t huff_table_1[257]; /* init once */ + int16_t huff_table_2[TAC_CHANNELS][32]; /* saved between (some) frames */ + int16_t huff_table_3[258]; /* init once */ + uint8_t huff_table_4[16383]; /* init once */ + + int16_t codes[TAC_CHANNELS][TAC_FRAME_SAMPLES]; + + /* decoding vector state */ + REG_VF spectrum[TAC_CHANNELS][TAC_FRAME_SAMPLES / 4]; /* temp huffman-to-coefs */ + REG_VF wave[TAC_CHANNELS][TAC_FRAME_SAMPLES / 4]; /* final samples, in vector form */ + REG_VF hist[TAC_CHANNELS][TAC_FRAME_SAMPLES / 4]; /* saved between frames */ +}; + + +/**********************************************************************************/ +/* MAIN DECODE */ +/**********************************************************************************/ + +/* similar to MP3's alias reduction step with pre-made SIMD tables: + * lo_out.M = (lo_in.M * AT[0x0+j].N) - (AT[0xF-j].M * lo_hi.N) + * hi_out.N = (lo_hi.N * AT[0x7-j].M) + (AT[0x8+j].N * lo_in.M) */ +static void unpack_antialias(REG_VF* spectrum) { + const REG_VF* AT = ANTIALIASING_TABLE; + int i; + int pos_lo = 0x7; + int pos_hi = 0x8; + + for (i = 0; i < TAC_CODED_BANDS; i++) { + for (int j = 0; j < 4; j++) { + REG_VF lo_in, hi_in, lo_out, hi_out; + + LOAD (_xyzw, &lo_in, spectrum, pos_lo - j); + LOAD (_xyzw, &hi_in, spectrum, pos_hi + j); + + MULx (____w, &lo_out, &lo_in, &AT[0x0+j]); + MSUBx(____w, &lo_out, &AT[0xF-j], &hi_in); + MULw (_x___, &hi_out, &hi_in, &AT[0x7-j]); + MADDw(_x___, &hi_out, &AT[0x8+j], &lo_in); + + MULy (___z_, &lo_out, &lo_in, &AT[0x0+j]); + MSUBy(___z_, &lo_out, &AT[0xF-j], &hi_in); + MULz (__y__, &hi_out, &hi_in, &AT[0x7-j]); + MADDz(__y__, &hi_out, &AT[0x8+j], &lo_in); + + MULz (__y__, &lo_out, &lo_in, &AT[0x0+j]); + MSUBz(__y__, &lo_out, &AT[0xF-j], &hi_in); + MULy (___z_, &hi_out, &hi_in, &AT[0x7-j]); + MADDy(___z_, &hi_out, &AT[0x8+j], &lo_in); + + MULw (_x___, &lo_out, &lo_in, &AT[0x0+j]); + MSUBw(_x___, &lo_out, &AT[0xF-j], &hi_in); + MULx (____w, &hi_out, &hi_in, &AT[0x7-j]); + MADDx(____w, &hi_out, &AT[0x8+j], &lo_in); + + STORE(_xyzw, spectrum, &lo_out, pos_lo - j); + STORE(_xyzw, spectrum, &hi_out, pos_hi + j); + } + + pos_lo += 0x8; + pos_hi += 0x8; + } +} + + +static inline int16_t clamp_s16(int16_t value, int16_t min, int16_t max) { + if (value < min) + return min; + else if (value > max) + return max; + else + return value; +} + + +/* converts 4 huffman codes to 4 spectrums coefs */ +//SUB_1188 +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; + + /* copy table coefs .N, unless huffman code was 0 */ + if (code->f.x != 0) { + MOVEx(_x___, &tbc1, &ST[idx->i.x + 0]); + MOVEx(_x___, &tbc2, &ST[idx->i.x + 1]); + } else { + MOVEx(_x___, &tbc1, &VECTOR_ZERO); + MOVEx(_x___, &tbc2, &VECTOR_ZERO); + } + + if (code->f.y != 0) { + MOVEx(__y__, &tbc1, &ST[idx->i.y + 0]); + MOVEx(__y__, &tbc2, &ST[idx->i.y + 1]); + } else { + MOVEx(__y__, &tbc1, &VECTOR_ZERO); + MOVEx(__y__, &tbc2, &VECTOR_ZERO); + } + + if (code->f.z != 0) { + MOVEx(___z_, &tbc1, &ST[idx->i.z + 0]); + MOVEx(___z_, &tbc2, &ST[idx->i.z + 1]); + } else { + MOVEx(___z_, &tbc1, &VECTOR_ZERO); + MOVEx(___z_, &tbc2, &VECTOR_ZERO); + } + + if (code->f.w != 0) { + MOVEx(____w, &tbc1, &ST[idx->i.w + 0]); + MOVEx(____w, &tbc2, &ST[idx->i.w + 1]); + } else { + MOVEx(____w, &tbc1, &VECTOR_ZERO); + MOVEx(____w, &tbc2, &VECTOR_ZERO); + } + + /* out = [signed] (scp1/scp2) * (tbc2 - tbc1) + tbc1 */ + DIV (_xyzw, &out, spc1, spc2); + SUB (_xyzw, &tbc2, &tbc2, &tbc1); + MUL (_xyzw, &out, &out, &tbc2); + ADD (_xyzw, &out, &out, &tbc1); + SIGN (_xyzw, &out, code); + + STORE(_xyzw, spectrum, &out, out_pos); +} + + +/* Unpacks huffman codes in one band into 32 spectrum coefs, using selected scales for that band. */ +// SUB_C88 +static void unpack_band(REG_VF* spectrum, const int16_t* codes, int band_pos, int* code_pos, int out_pos) { + const REG_VF* ST = SCALE_TABLE; + int i; + int16_t base_index = codes[0]; /* table index, max ~35 */ + int16_t band_index = codes[band_pos]; /* table too */ + REG_VF scale; + + /* bad values should be caught by CRC check but for completeness */ + base_index = clamp_s16(base_index, 0, TAC_SCALE_TABLE_MAX_INDEX); + band_index = clamp_s16(band_index, 0, TAC_SCALE_TABLE_MAX_INDEX-128); + + + /* index zero = band is not coded and all of its coefs are 0 */ + if (band_index == 0) { + for (i = 0; i < (TAC_CODED_COEFS / 4); i++) { + STORE(_xyzw, spectrum, &VECTOR_ZERO, out_pos+i); + } + return; + } + + /* put final band scale at .y */ + MULy (__y__, &scale, &ST[128 + band_index], &ST[base_index]); + + /* unpack coefs */ + for (i = 0; i < 8; i++) { + REG_VF code, idx, tm01, tm02, tm03; + REG_VF spc1, spc2; + + COPY (_xyzw, &code, &codes[(*code_pos)]); + (*code_pos) += 4; + + /* scale coef then round down to int to get table indexes (!!!) */ + ABS (_xyzw, &tm01, &code); + MULy (_xyzw, &tm01, &tm01, &scale); + FMUL (_xyzw, &tm02, &tm01, 512.0); /* 512 = SCALE_TABLE max */ + ADD (_xyzw, &tm03, &tm02, &VECTOR_ONE); + + FTOI0(_xyzw, &idx, &tm02); /* keep idx as int for later (probably could use (int)f.N too) */ + ITOF0(_xyzw, &tm02, &idx); + FMULf(_xyzw, &tm02, 0.00195313); + + FTOI0(_xyzw, &tm03, &tm03); + ITOF0(_xyzw, &tm03, &tm03); + FMULf(_xyzw, &tm03, 0.00195313); + + SUB (_xyzw, &spc1, &tm01, &tm02); + SUB (_xyzw, &spc2, &tm03, &tm02); + + /* Also just in case. In rare cases index may access 511+1 but table takes this into account */ + idx.i.x = clamp_s16(idx.i.x, 0, TAC_SCALE_TABLE_MAX_INDEX); + idx.i.y = clamp_s16(idx.i.y, 0, TAC_SCALE_TABLE_MAX_INDEX); + idx.i.z = clamp_s16(idx.i.z, 0, TAC_SCALE_TABLE_MAX_INDEX); + idx.i.w = clamp_s16(idx.i.w, 0, TAC_SCALE_TABLE_MAX_INDEX); + + unpack_code4(spectrum, &spc1, &spc2, &code, &idx, out_pos + i); + } +} + +/* Unpacks channel's huffman codes to spectrum coefs. Also done in the VU1 (uses VIFcode UNPACK V4-16 + * to copy 16b huffman codes to VU1 memory as 32b first) but it's simplified a bit here. */ +// SUB_6E0 +static void unpack_channel(REG_VF* spectrum, const int16_t* codes) { + int i; + + /* Huffman codes has 1 base scale + 27 bands scales + N coefs (up to 27*32). + * Not all bands store codes so an index is needed, after scales */ + int code_pos = TAC_CODED_BANDS + 1; + int out_pos = 0x00; + + /* unpack bands */ + for (i = 1; i < TAC_CODED_BANDS + 1; i++) { + unpack_band(spectrum, codes, i, &code_pos, out_pos); + out_pos += (TAC_CODED_COEFS / 4); /* 8 vectors of 4 coefs, per band */ + } + + /* memset rest up to max (27*32/4)..(32*32/4) */ + for (i = 0xD8; i < 0x100; i++) { + STORE (_xyzw, spectrum, &VECTOR_ZERO, i); + } + + /* tweak spectrum */ + unpack_antialias(spectrum); +} + + +/* in GCC this function seems to cause heisenbugs, copy x4 below to get original results */ +static void transform_dot_product(REG_VF* mac, const REG_VF* spectrum, const REG_VF* TT, int pos_i, int pos_t) { + MUL (_xyzw, mac, &spectrum[pos_i+0], &TT[pos_t+0]); /* resets mac */ + MADD (_xyzw, mac, &spectrum[pos_i+1], &TT[pos_t+1]); + MADD (_xyzw, mac, &spectrum[pos_i+2], &TT[pos_t+2]); + MADD (_xyzw, mac, &spectrum[pos_i+3], &TT[pos_t+3]); + MADD (_xyzw, mac, &spectrum[pos_i+4], &TT[pos_t+4]); + MADD (_xyzw, mac, &spectrum[pos_i+5], &TT[pos_t+5]); + MADD (_xyzw, mac, &spectrum[pos_i+6], &TT[pos_t+6]); + 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 +static void transform(REG_VF* wave, const REG_VF* spectrum) { + const REG_VF* TT = TRANSFORM_TABLE; + int i, j; + + int pos_t = 0; + int pos_o = 0; + + for (i = 0; i < TAC_TOTAL_POINTS; i++) { + int pos_i = 0; + REG_VF mac, ror, out; + + for (j = 0; j < 8; j++) { + transform_dot_product(&mac, spectrum, TT, pos_i, pos_t); + pos_i += 8; + MR32 (_xyzw, &ror, &mac); + ADD (_x_z_, &ror, &ror, &mac); + ADDz (_x___, &out, &ror, &ror); + + transform_dot_product(&mac, spectrum, TT, pos_i, pos_t); + pos_i += 8; + MR32 (_xyzw, &ror, &mac); + ADD (__y_w, &ror, &ror, &mac); + ADDw (__y__, &out, &ror, &ror); + + transform_dot_product(&mac, spectrum, TT, pos_i, pos_t); + pos_i += 8; + MR32 (_xyzw, &ror, &mac); + ADD (_x_z_, &ror, &ror, &mac); + ADDx (___z_, &out, &ror, &ror); + + transform_dot_product(&mac, spectrum, TT, pos_i, pos_t); + pos_i += 8; + MR32 (_xyzw, &ror, &mac); + ADD (__y_w, &ror, &ror, &mac); + ADDy (____w, &out, &ror, &ror); + + FMULf(_xyzw, &out, 0.25); + STORE(_xyzw, wave, &out, pos_o++); + } + + pos_t += 0x08; + } +} + + +/* process and apply window/overlap. Similar to MP3's synth granule function. */ +//SUB_1690 +static void process(REG_VF* wave, REG_VF* hist) { + const REG_VF* ST = SYNTH_TABLE; + int i, j; + + int pos_o = 0; + int pos_w = 0; + int pos_h; + int pos_r = 0x200; /* rolls down to 0, becoming 0x10 steps (0x00, 0xF0, 0xE0, ..., 0x00, 0xF0, ...) */ + + for (i = 0; i < TAC_TOTAL_POINTS; i++) { + REG_VF zero, neg1; + /* Sorry... hopefully compiler optimizes. Probably could be simplified with some rearranging below */ + REG_VF tm00, tm01, tm02, tm03, tm04, tm05, tm06, tm07, + tm10, tm11, tm12, tm13, tm14, tm15, tm16, tm17, + tm20, tm21, tm22, tm23, tm24, tm25, tm26, tm27, + tm30, tm31, tm32, tm33, tm34; + /* tmp calcs, meant to be used nearby */ + REG_VF tmpZ; + /* output temps, could STORE as calc'd (like VU1) but thought would be easier to read at the end */ + REG_VF out0, out1, out2, out3, out4, out5, out6, out7, + out8, out9, outA, outB, outC, outD, outE, outF; + + pos_h = pos_r & 0xFF; + pos_r = pos_r - 0x10; + + LOAD (_xyzw, &tm00, wave, pos_w+0); + LOAD (_xyzw, &tm01, wave, pos_w+1); + LOAD (_xyzw, &tm02, wave, pos_w+2); + LOAD (_xyzw, &tm03, wave, pos_w+3); + LOAD (_xyzw, &tm04, wave, pos_w+4); + LOAD (_xyzw, &tm05, wave, pos_w+5); + LOAD (_xyzw, &tm06, wave, pos_w+6); + LOAD (_xyzw, &tm07, wave, pos_w+7); + pos_w += 8; + + MOVE (_xyzw, &zero, &VECTOR_ZERO); /* always 0, used for copying */ + MOVE (_xyzw, &neg1, &VECTOR_M_ONE); /* always -1, used for negating */ + + + /* WTF is going on here? Yeah, no clue. Probably some multi-step FFT/DCT twiddle thing. + * Remember all those separate ops are left as-is to allow PS2 float simulation (disabled though). + * Tried cleaning up some more but... */ + ADDw (_x___, &tm10, &tm01, &tm00); + ADDx (____w, &tm10, &tm01, &tm02); + ADDx (____w, &tm11, &tm02, &tm03); + ADDw (_x___, &tm12, &tm04, &tm03); + ADDw (_x___, &tm13, &tm05, &tm04); + ADDx (____w, &tm13, &tm05, &tm06); + ADDx (____w, &tm12, &tm06, &tm07); + ADDx (__y__, &tm11, &zero, &tm10); + ADDw (___z_, &tm11, &zero, &tm10); + ADDx (__y__, &tm12, &zero, &tm13); + ADDw (___z_, &tm12, &zero, &tm13); + ADDw (_x___, &tm14, &tm00, &tm07); + SUBw (_x___, &tm15, &tm00, &tm07); + ADDz (___z_, &tm16, &tm11, &tm12); + ADDx (___z_, &tm17, &zero, &tm14); + ADDx (____w, &tm16, &zero, &tm15); + SUBz (___z_, &tm10, &tm11, &tm12); + ADDz (_x___, &tm16, &tm12, &tm17); + MULx (___z_, &tm13, &tm10, &ST[0x4]); + ADDz (_x___, &tm17, &tm16, &tm16); + ADDz (__y__, &tm16, &zero, &tm13); + SUBx (___z_, &tm17, &tm17, &tm12); + ADDy (____w, &tm14, &tm11, &tm12); + ADDw (__y__, &tm17, &tm16, &tm16); + SUBy (____w, &tm17, &tm16, &tm16); + SUBz (_x___, &tm20, &tm16, &tm16); + ADDw (__y__, &tm16, &tm11, &tm11); + ADDw (___z_, &tm16, &zero, &tm14); + ADDy (____w, &tm16, &tm12, &tm12); + ADDw (__y__, &tm10, &tm11, &tm12); + SUBw (__y__, &tm13, &tm11, &tm12); + ADDw (__y__, &tm21, &tm11, &tm12); + SUBw (__y__, &tm14, &tm16, &tm16); + + ADDy (___z_, &tm22, &tm16, &tm10); + ADDy (____w, &tm22, &zero, &tm13); + SUBz (__y__, &tm21, &tm21, &tm16); + MULx (__y__, &tm16, &tm14, &ST[0x4]); + ADDz (_x___, &tm23, &tm17, &tm22); + ADDx (_x___, &tm24, &zero, &tm20); + MULx (__y__, &tm21, &tm21, &ST[0x4]); + ADDy (____w, &tm10, &tm22, &tm16); + SUBy (____w, &tm14, &tm22, &tm16); + SUBz (_x___, &tm25, &tm17, &tm22); + ADDy (___z_, &tm23, &tm17, &tm21); + MULw (_x___, &tm21, &ST[0x2], &tm10); + MULx (____w, &tm14, &tm14, &ST[0x6]); + + ADDz (___z_, &tm20, &zero, &tm23); + ADDx (__y__, &tm23, &tm17, &tm21); + ADDw (___z_, &tm21, &zero, &tm14); + ADDy (____w, &tm20, &zero, &tm23); + ADDz (____w, &tm23, &tm17, &tm21); + SUBz (____w, &tm25, &tm17, &tm21); + SUBy (___z_, &tm24, &tm17, &tm21); + SUBx (__y__, &tm25, &tm17, &tm21); + ADDw (__y__, &tm20, &zero, &tm23); + ADDw (__y__, &tm24, &zero, &tm25); + ADDz (___z_, &tm25, &zero, &tm24); + ADDy (____w, &tm24, &zero, &tm25); + ADDz (__y__, &tm10, &tm00, &tm00); + ADDz (__y__, &tm13, &tm03, &tm03); + ADDz (__y__, &tm11, &tm01, &tm01); + ADDy (___z_, &tm11, &tm02, &tm02); + ADDy (_x___, &tm11, &zero, &tm10); + ADDy (____w, &tm11, &zero, &tm13); + ADDz (__y__, &tm10, &tm04, &tm04); + ADDz (__y__, &tm13, &tm07, &tm07); + ADDz (__y__, &tm12, &tm05, &tm05); + ADDy (___z_, &tm12, &tm06, &tm06); + ADDy (_x___, &tm12, &zero, &tm10); + ADDy (____w, &tm12, &zero, &tm13); + ADDz (__y__, &tm26, &tm11, &tm11); + ADDz (__y__, &tm15, &tm12, &tm12); + ADDx (____w, &tm14, &tm11, &tm12); + ADDw (_x___, &tm27, &tm11, &tm12); + SUBw (_x___, &tm10, &tm11, &tm12); + ADDy (____w, &tm26, &zero, &tm15); + ADDw (___z_, &tm26, &zero, &tm14); + ADDy (_x___, &tm16, &tm11, &tm11); + ADDx (____w, &tm16, &zero, &tm10); + ADDw (__y__, &tm22, &tm26, &tm26); + ADDz (_x___, &tm22, &tm27, &tm26); + SUBw (__y__, &tm16, &tm26, &tm26); + SUBz (_x___, &tm14, &tm27, &tm26); + ADDw (___z_, &tm15, &tm11, &tm11); + ADDy (_x___, &tm17, &tm22, &tm22); + MULx (__y__, &tm16, &tm16, &ST[0x4]); + ADDx (___z_, &tm17, &zero, &tm14); + + ADDy (_x___, &tm10, &tm12, &tm12); + ADDw (__y__, &tm17, &tm16, &tm16); + SUBy (____w, &tm17, &tm16, &tm16); + ADDz (____w, &tm16, &tm12, &tm12); + ADDx (___z_, &tm16, &zero, &tm10); + ADDz (__y__, &tm16, &zero, &tm15); + ADDx (_x___, &tm30, &tm23, &tm17); + FMULf(_x___, &tm30, -1.0); + MOVE (_x___, &outC, &tm30); + ADDw (_x___, &tm27, &tm16, &tm16); + ADDz (__y__, &tm27, &tm16, &tm16); + SUBw (_x___, &tm13, &tm16, &tm16); + ADDy (_x___, &tm14, &tm16, &tm16); + ADDz (____w, &tm27, &tm16, &tm16); + ADDx (____w, &tm22, &zero, &tm13); + ADDx (___z_, &tm27, &zero, &tm14); + SUBy (_x___, &tm13, &tm27, &tm27); + SUBw (___z_, &tm15, &tm27, &tm27); + MULx (_x___, &tm13, &tm13, &ST[0x4]); + MULx (___z_, &tm15, &tm15, &ST[0x4]); + ADDx (__y__, &tm21, &zero, &tm13); + ADDz (____w, &tm27, &zero, &tm15); + ADDw (____w, &tm14, &tm22, &tm27); + SUBw (____w, &tm13, &tm22, &tm27); + MULx (____w, &tm14, &tm14, &ST[0x2]); + MULx (____w, &tm13, &tm13, &ST[0x6]); + ADDw (_x___, &tm21, &zero, &tm14); + ADDw (___z_, &tm21, &zero, &tm13); + + ADDx (__y__, &tmpZ, &tm17, &tm21); + ADDy (___z_, &tmpZ, &tm17, &tm21); + ADDz (____w, &tmpZ, &tm17, &tm21); + SUBy (_x___, &tmpZ, &tm22, &tm22); + MULx (__y__, &tm10, &tmpZ, &ST[0x1]); + MULx (___z_, &tm10, &tmpZ, &ST[0x2]); + MULx (____w, &tm10, &tmpZ, &ST[0x3]); + MULx (_x___, &tm10, &tmpZ, &ST[0x4]); + + SUBx (_x___, &tm23, &tm23, &tm17); /* .x not used after this */ + MULx (_x___, &out0, &tm23, &ST[0x4]); + + SUBy (__y__, &tm23, &tm23, &tm10); + SUBz (___z_, &tm23, &tm23, &tm10); + SUBw (____w, &tm23, &tm23, &tm10); + ADDw (__y__, &tm20, &tm20, &tm10); + ADDz (___z_, &tm20, &tm20, &tm10); + ADDy (____w, &tm20, &tm20, &tm10); + ADDx (_x___, &tm20, &tm20, &tm10); + SUBx (_x___, &tm24, &tm24, &tm10); + + SUBx (__y__, &tmpZ, &tm17, &tm21); + SUBy (___z_, &tmpZ, &tm17, &tm21); + SUBz (____w, &tmpZ, &tm17, &tm21); + MULx (__y__, &tm10, &tmpZ, &ST[0x7]); + MULx (___z_, &tm10, &tmpZ, &ST[0x6]); + MULx (____w, &tm10, &tmpZ, &ST[0x5]); + + SUBw (__y__, &tm24, &tm24, &tm10); + SUBz (___z_, &tm24, &tm24, &tm10); + SUBy (____w, &tm24, &tm24, &tm10); + + ADDy (__y__, &tm25, &tm25, &tm10); + ADDz (___z_, &tm25, &tm25, &tm10); + ADDw (____w, &tm25, &tm25, &tm10); + + ADDy (_x___, &tm00, &tm00, &tm00); + FMULf(_x___, &tm00, -1.0); + ADDw (___z_, &tm10, &tm00, &tm00); + ADDy (_x___, &tm10, &tm01, &tm01); + FMULf(_x___, &tm10, -1.0); + ADDz (____w, &tm00, &tm01, &tm01); + ADDz (__y__, &tm00, &zero, &tm10); + ADDy (_x___, &tm01, &tm02, &tm02); + FMULf(_x___, &tm01, -1.0); + ADDw (___z_, &tm10, &tm02, &tm02); + ADDy (_x___, &tm13, &tm03, &tm03); + FMULf(_x___, &tm13, -1.0); + ADDx (___z_, &tm00, &zero, &tm10); + ADDz (__y__, &tm01, &zero, &tm10); + ADDz (____w, &tm01, &tm03, &tm03); + ADDy (_x___, &tm02, &tm04, &tm04); + FMULf(_x___, &tm02, -1.0); + ADDw (___z_, &tm10, &tm04, &tm04); + ADDx (___z_, &tm01, &zero, &tm13); + ADDy (_x___, &tm13, &tm05, &tm05); + FMULf(_x___, &tm13, -1.0); + ADDz (__y__, &tm02, &zero, &tm10); + ADDz (____w, &tm02, &tm05, &tm05); + ADDy (_x___, &tm03, &tm06, &tm06); + FMULf(_x___, &tm03, -1.0); + ADDw (___z_, &tm10, &tm06, &tm06); + ADDy (_x___, &tm14, &tm07, &tm07); + FMULf(_x___, &tm14, -1.0); + ADDx (___z_, &tm02, &zero, &tm13); + ADDz (__y__, &tm03, &zero, &tm10); + ADDz (____w, &tm03, &tm07, &tm07); + + ADDx (___z_, &tm03, &zero, &tm14); + ADDz (__y__, &tm11, &tm00, &tm00); + ADDx (____w, &tm10, &tm00, &tm01); + ADDz (__y__, &tm10, &tm01, &tm01); + ADDw (_x___, &tm12, &tm02, &tm01); + ADDz (__y__, &tm12, &tm02, &tm02); + ADDw (___z_, &tm11, &zero, &tm10); + ADDy (____w, &tm11, &zero, &tm10); + ADDx (____w, &tm10, &tm02, &tm03); + ADDz (__y__, &tm10, &tm03, &tm03); + ADDy (__y__, &tm13, &tm11, &tm12); + ADDw (__y__, &tm26, &tm11, &tm11); + ADDw (___z_, &tm12, &zero, &tm10); + ADDy (____w, &tm12, &zero, &tm10); + SUBw (__y__, &tm13, &tm13, &tm11); + ADDy (____w, &tm10, &tm11, &tm12); + ADDy (____w, &tm26, &tm12, &tm12); + SUBz (___z_, &tm14, &tm11, &tm12); + SUBw (__y__, &tm13, &tm13, &tm12); + ADDw (___z_, &tm26, &zero, &tm10); + ADDw (__y__, &tm10, &tm26, &tm26); + MULx (__y__, &tm10, &tm10, &ST[0x4]); + ADDz (__y__, &tm21, &zero, &tm14); + ADDy (_x___, &tm31, &zero, &tm13); + SUBw (__y__, &tm13, &tm26, &tm26); + MULx (__y__, &tm13, &tm13, &ST[0x4]); + + ADDy (___z_, &tm21, &zero, &tm10); + ADDy (___z_, &tm17, &zero, &tm13); + ADDz (___z_, &tm10, &tm21, &tm26); + SUBz (___z_, &tm13, &tm21, &tm26); + ADDz (___z_, &tm30, &tm11, &tm12); + MULx (___z_, &tm10, &tm10, &ST[0x2]); + MULx (___z_, &tm13, &tm13, &ST[0x6]); + MULx (___z_, &tm30, &tm30, &ST[0x4]); + ADDz (__y__, &tm17, &zero, &tm10); + ADDz (____w, &tm17, &zero, &tm13); + ADDz (____w, &tm22, &zero, &tm30); + SUBx (____w, &tm10, &tm22, &tm12); + ADDw (_x___, &tm21, &tm12, &tm22); + + ADDw (___z_, &tm21, &zero, &tm10); + ADDx (__y__, &tm32, &tm17, &tm21); + ADDy (___z_, &tm32, &tm17, &tm21); + FMUL (_x___, &tm33, &tm31, -1.0); + ADDz (____w, &tm32, &tm17, &tm21); + FMUL (__y__, &tm10, &tm32, -1.0); + FMUL (___z_, &tm33, &tm32, -1.0); + SUBz (____w, &tm14, &tm17, &tm21); + FMUL (____w, &tm13, &tm32, -1.0); + ADDy (____w, &tm33, &zero, &tm10); + SUBy (___z_, &tm31, &tm17, &tm21); + ADDw (__y__, &tm31, &zero, &tm14); + ADDw (__y__, &tm33, &zero, &tm13); + SUBx (__y__, &tm13, &tm17, &tm21); + FMUL (___z_, &tm34, &tm31, -1.0); + FMUL (__y__, &tm10, &tm31, -1.0); + ADDy (____w, &tm31, &zero, &tm13); + ADDy (____w, &tm34, &zero, &tm10); + FMUL (____w, &tm10, &tm31, -1.0); + ADDw (__y__, &tm34, &zero, &tm10); + ADDy (_x___, &tm11, &tm00, &tm00); + ADDw (___z_, &tm10, &tm00, &tm00); + ADDy (_x___, &tm13, &tm01, &tm01); + ADDz (____w, &tm11, &tm01, &tm01); + ADDy (_x___, &tm12, &tm02, &tm02); + ADDz (__y__, &tm11, &zero, &tm10); + ADDx (___z_, &tm11, &zero, &tm13); + ADDw (___z_, &tm10, &tm02, &tm02); + ADDy (_x___, &tm13, &tm03, &tm03); + ADDz (____w, &tm12, &tm03, &tm03); + + ADDz (__y__, &tm12, &zero, &tm10); + ADDx (___z_, &tm12, &zero, &tm13); + SUBy (_x___, &tm34, &tm11, &tm11); + SUBw (___z_, &tm10, &tm11, &tm11); + SUBy (_x___, &tm13, &tm12, &tm12); + SUBw (___z_, &tm14, &tm12, &tm12); + ADDz (__y__, &tm15, &tm12, &tm12); + ADDz (_x___, &tm34, &tm34, &tm10); + ADDx (____w, &tm10, &tm11, &tm12); + ADDz (_x___, &tm13, &tm13, &tm14); + ADDy (____w, &tm16, &zero, &tm15); + ADDz (__y__, &tm16, &tm11, &tm11); + ADDw (___z_, &tm16, &zero, &tm10); + ADDx (_x___, &tm34, &tm34, &tm13); + ADDz (____w, &tm26, &tm12, &tm12); + ADDy (_x___, &tm14, &tm12, &tm12); + ADDw (___z_, &tm15, &tm11, &tm11); + ADDy (_x___, &tm26, &tm11, &tm11); + ADDx (___z_, &tm26, &zero, &tm14); + ADDz (__y__, &tm26, &zero, &tm15); + ADDw (___z_, &tm13, &tm26, &tm26); + ADDy (_x___, &tm27, &tm26, &tm26); + ADDz (__y__, &tm10, &tm26, &tm26); + ADDz (__y__, &tm27, &zero, &tm13); + ADDy (____w, &tm27, &zero, &tm10); + ADDy (_x___, &tm10, &tm27, &tm27); + SUBy (_x___, &tm14, &tm27, &tm27); + ADDy (____w, &tm22, &tm16, &tm16); + SUBw (__y__, &tm21, &tm16, &tm16); + MULx (_x___, &tm10, &tm10, &ST[0x4]); + MULx (_x___, &tm14, &tm14, &ST[0x4]); + MULx (____w, &tm22, &tm22, &ST[0x4]); + ADDx (___z_, &tm21, &zero, &tm10); + ADDx (___z_, &tm17, &zero, &tm14); + SUBz (____w, &tm30, &tm22, &tm16); + ADDw (___z_, &tm10, &tm21, &tm27); + SUBw (___z_, &tm14, &tm21, &tm27); + ADDz (____w, &tmpZ, &tm22, &tm16); + ADDw (_x___, &tm21, &zero, &tmpZ); + ADDw (___z_, &tm21, &zero, &tm30); + MULx (___z_, &tm10, &tm10, &ST[0x2]); + MULx (___z_, &tm14, &tm14, &ST[0x6]); + ADDz (__y__, &tm17, &zero, &tm10); + ADDz (____w, &tm17, &zero, &tm14); + SUBy (_x___, &tm30, &tm26, &tm26); + ADDx (__y__, &tm10, &tm17, &tm21); + ADDy (___z_, &tm10, &tm17, &tm21); + ADDz (____w, &tm10, &tm17, &tm21); + ADDz (_x___, &tm30, &tm30, &tm26); + MULx (__y__, &tm10, &tm10, &ST[0x1]); + MULx (___z_, &tm10, &tm10, &ST[0x2]); + MULx (____w, &tm10, &tm10, &ST[0x3]); + SUBw (_x___, &tm30, &tm30, &tm26); + MULx (_x___, &tm30, &tm30, &ST[0x4]); + + ADDy (__y__, &tmpZ, &tm32, &tm10); + ADDz (___z_, &tmpZ, &tm32, &tm10); + ADDw (____w, &tmpZ, &tm32, &tm10); + MULz (__y__, &tm32, &tmpZ, &ST[0x0]); + MULx (___z_, &tm32, &tmpZ, &ST[0x1]); + MULz (____w, &tm32, &tmpZ, &ST[0x1]); + MOVE (_x___, &tm32, &zero); + + ADDy (____w, &tm33, &tm33, &tm10); + ADDz (___z_, &tm33, &tm33, &tm10); + ADDw (__y__, &tm33, &tm33, &tm10); + ADDx (_x___, &tm33, &tm33, &tm30); + MULx (_x___, &tm33, &tm33, &ST[0x6]); + + SUBz (____w, &tmpZ, &tm17, &tm21); + SUBy (___z_, &tmpZ, &tm17, &tm21); + SUBx (__y__, &tmpZ, &tm17, &tm21); + MULx (____w, &tm10, &tmpZ, &ST[0x5]); + MULx (___z_, &tm10, &tmpZ, &ST[0x6]); + MULx (__y__, &tm10, &tmpZ, &ST[0x7]); + + ADDx (_x___, &tmpZ, &tm31, &tm30); + ADDw (__y__, &tmpZ, &tm31, &tm10); + ADDz (___z_, &tmpZ, &tm31, &tm10); + ADDy (____w, &tmpZ, &tm31, &tm10); + MULx (_x___, &tm31, &tmpZ, &ST[0x2]); + MULz (__y__, &tm31, &tmpZ, &ST[0x2]); + MULx (___z_, &tm31, &tmpZ, &ST[0x3]); + MULz (____w, &tm31, &tmpZ, &ST[0x3]); + + ADDy (__y__, &tmpZ, &tm23, &tm32); + ADDz (___z_, &tmpZ, &tm23, &tm32); + ADDw (____w, &tmpZ, &tm23, &tm32); + ADDx (_x___, &tmpZ, &tm24, &tm31); + MULy (__y__, &out0, &tmpZ, &ST[0x4]); + MULz (___z_, &out0, &tmpZ, &ST[0x4]); + MULw (____w, &out0, &tmpZ, &ST[0x4]); + MULx (_x___, &out1, &tmpZ, &ST[0x5]); + MULy (____w, &out7, &neg1, &out0); + MULz (___z_, &out7, &neg1, &out0); + MULw (__y__, &out7, &neg1, &out0); + MULx (_x___, &out7, &neg1, &out1); + + SUBy (__y__, &tmpZ, &tm32, &tm23); + SUBz (___z_, &tmpZ, &tm32, &tm23); + SUBw (____w, &tmpZ, &tm32, &tm23); + SUBx (_x___, &tmpZ, &tm31, &tm24); + MULy (____w, &outF, &ST[0x3], &tmpZ); + MULz (___z_, &outF, &ST[0x3], &tmpZ); + MULw (__y__, &outF, &ST[0x3], &tmpZ); + MULx (_x___, &outF, &ST[0x3], &tmpZ); + ADDw (__y__, &out8, &zero, &outF); + ADDz (___z_, &out8, &zero, &outF); + ADDy (____w, &out8, &zero, &outF); + ADDx (_x___, &out9, &zero, &outF); + + ADDw (____w, &tmpZ, &tm34, &tm10); + ADDz (___z_, &tmpZ, &tm34, &tm10); + ADDy (__y__, &tmpZ, &tm34, &tm10); + MULz (__y__, &tm34, &tmpZ, &ST[0x4]); + MULx (___z_, &tm34, &tmpZ, &ST[0x5]); + MULz (____w, &tm34, &tmpZ, &ST[0x5]); + MULx (_x___, &tm34, &tm34, &ST[0x4]); + + ADDy (__y__, &tmpZ, &tm24, &tm31); + ADDz (___z_, &tmpZ, &tm24, &tm31); + ADDw (____w, &tmpZ, &tm24, &tm31); + ADDx (_x___, &tmpZ, &tm25, &tm34); + MULy (__y__, &out1, &tmpZ, &ST[0x5]); + MULz (___z_, &out1, &tmpZ, &ST[0x5]); + MULw (____w, &out1, &tmpZ, &ST[0x5]); + MULx (_x___, &out2, &tmpZ, &ST[0x6]); + MULy (____w, &out6, &neg1, &out1); + MULz (___z_, &out6, &neg1, &out1); + MULw (__y__, &out6, &neg1, &out1); + MULx (_x___, &out6, &neg1, &out2); + + SUBy (__y__, &tmpZ, &tm31, &tm24); + SUBz (___z_, &tmpZ, &tm31, &tm24); + SUBw (____w, &tmpZ, &tm31, &tm24); + SUBx (_x___, &tmpZ, &tm34, &tm25); + MULw (__y__, &outE, &ST[0x2], &tmpZ); + MULz (___z_, &outE, &ST[0x2], &tmpZ); + MULy (____w, &outE, &ST[0x2], &tmpZ); + MULx (_x___, &outE, &ST[0x2], &tmpZ); + ADDw (__y__, &out9, &zero, &outE); + ADDz (___z_, &out9, &zero, &outE); + ADDy (____w, &out9, &zero, &outE); + ADDx (_x___, &outA, &zero, &outE); + + ADDy (__y__, &tmpZ, &tm25, &tm34); + ADDz (___z_, &tmpZ, &tm25, &tm34); + ADDw (____w, &tmpZ, &tm25, &tm34); + ADDx (_x___, &tmpZ, &tm20, &tm33); + MULy (__y__, &out2, &tmpZ, &ST[0x6]); + MULz (___z_, &out2, &tmpZ, &ST[0x6]); + MULw (____w, &out2, &tmpZ, &ST[0x6]); + MULx (_x___, &out3, &tmpZ, &ST[0x7]); + MULy (____w, &out5, &neg1, &out2); + MULz (___z_, &out5, &neg1, &out2); + MULw (__y__, &out5, &neg1, &out2); + MULx (_x___, &out5, &neg1, &out3); + + SUBy (__y__, &tmpZ, &tm34, &tm25); + SUBz (___z_, &tmpZ, &tm34, &tm25); + SUBw (____w, &tmpZ, &tm34, &tm25); + SUBx (_x___, &tmpZ, &tm33, &tm20); + MULw (__y__, &outD, &ST[0x1], &tmpZ); + MULz (___z_, &outD, &ST[0x1], &tmpZ); + MULy (____w, &outD, &ST[0x1], &tmpZ); + MULx (_x___, &outD, &ST[0x1], &tmpZ); + ADDw (__y__, &outA, &zero, &outD); + ADDz (___z_, &outA, &zero, &outD); + ADDy (____w, &outA, &zero, &outD); + ADDx (_x___, &outB, &zero, &outD); + + MULz (__y__, &tm33, &tm33, &ST[0x6]); + MULx (___z_, &tm33, &tm33, &ST[0x7]); + MULz (____w, &tm33, &tm33, &ST[0x7]); + + ADDy (__y__, &tmpZ, &tm20, &tm33); + ADDz (___z_, &tmpZ, &tm20, &tm33); + ADDw (____w, &tmpZ, &tm20, &tm33); + MULw (____w, &out3, &tmpZ, &ST[0x7]); + MULz (___z_, &out3, &tmpZ, &ST[0x7]); + MULy (__y__, &out3, &tmpZ, &ST[0x7]); + MULw (__y__, &out4, &neg1, &out3); + MULz (___z_, &out4, &neg1, &out3); + MULy (____w, &out4, &neg1, &out3); + MOVE (_x___, &out4, &zero); + + SUBy (__y__, &tmpZ, &tm33, &tm20); + SUBz (___z_, &tmpZ, &tm33, &tm20); + SUBw (____w, &tmpZ, &tm33, &tm20); + MULw (__y__, &outC, &ST[0x0], &tmpZ); + MULz (___z_, &outC, &ST[0x0], &tmpZ); + MULy (____w, &outC, &ST[0x0], &tmpZ); + ADDw (__y__, &outB, &zero, &outC); + ADDz (___z_, &outB, &zero, &outC); + ADDy (____w, &outB, &zero, &outC); + MULx (_x___, &out8, &neg1, &out0); + + /* current output */ + STORE(_xyzw, hist, &out0, pos_h + 0x0); + STORE(_xyzw, hist, &out1, pos_h + 0x1); + STORE(_xyzw, hist, &out2, pos_h + 0x2); + STORE(_xyzw, hist, &out3, pos_h + 0x3); + STORE(_xyzw, hist, &out4, pos_h + 0x4); + STORE(_xyzw, hist, &out5, pos_h + 0x5); + STORE(_xyzw, hist, &out6, pos_h + 0x6); + STORE(_xyzw, hist, &out7, pos_h + 0x7); + STORE(_xyzw, hist, &out8, pos_h + 0x8); + STORE(_xyzw, hist, &out9, pos_h + 0x9); + STORE(_xyzw, hist, &outA, pos_h + 0xA); + STORE(_xyzw, hist, &outB, pos_h + 0xB); + STORE(_xyzw, hist, &outC, pos_h + 0xC); + STORE(_xyzw, hist, &outD, pos_h + 0xD); + STORE(_xyzw, hist, &outE, pos_h + 0xE); + STORE(_xyzw, hist, &outF, pos_h + 0xF); + + /* hist/window overlap and update final wave */ + for (j = 0; j < 8; j++) { + const REG_VF* WT = WINDOW_TABLE; + REG_VF out, rnd; + + MUL (_xyzw, &out, &hist[(pos_h + 0x00) & 0xFF], &WT[0x00+j]); + MADD (_xyzw, &out, &hist[(pos_h + 0x18) & 0xFF], &WT[0x08+j]); + MADD (_xyzw, &out, &hist[(pos_h + 0x20) & 0xFF], &WT[0x10+j]); + MADD (_xyzw, &out, &hist[(pos_h + 0x38) & 0xFF], &WT[0x18+j]); + MADD (_xyzw, &out, &hist[(pos_h + 0x40) & 0xFF], &WT[0x20+j]); + MADD (_xyzw, &out, &hist[(pos_h + 0x58) & 0xFF], &WT[0x28+j]); + MADD (_xyzw, &out, &hist[(pos_h + 0x60) & 0xFF], &WT[0x30+j]); + MADD (_xyzw, &out, &hist[(pos_h + 0x78) & 0xFF], &WT[0x38+j]); + MADD (_xyzw, &out, &hist[(pos_h + 0x80) & 0xFF], &WT[0x40+j]); + MADD (_xyzw, &out, &hist[(pos_h + 0x98) & 0xFF], &WT[0x48+j]); + MADD (_xyzw, &out, &hist[(pos_h + 0xA0) & 0xFF], &WT[0x50+j]); + MADD (_xyzw, &out, &hist[(pos_h + 0xB8) & 0xFF], &WT[0x58+j]); + MADD (_xyzw, &out, &hist[(pos_h + 0xC0) & 0xFF], &WT[0x60+j]); + MADD (_xyzw, &out, &hist[(pos_h + 0xD8) & 0xFF], &WT[0x68+j]); + MADD (_xyzw, &out, &hist[(pos_h + 0xE0) & 0xFF], &WT[0x70+j]); + MADD (_xyzw, &out, &hist[(pos_h + 0xF8) & 0xFF], &WT[0x78+j]); + + pos_h++; + + /* base volume and +-0.5 to final sample (+-32767.0) */ + MUL (_xyzw, &out, &out, &VECTOR_VOLUME); + + MOVE (_xyzw, &rnd, &VECTOR_ROUND); + SIGN (_xyzw, &rnd, &out); + ADD (_xyzw, &out, &out, &rnd); + + STORE(_xyzw, wave, &out, pos_o++); + } + } +} + + +/////////////////////////////////////////////////////////////////////////////// + +/* main decoding in the VU1 coprocessor */ +static void decode_vu1(tac_handle_t* h) { + int ch; + + for (ch = 0; ch < TAC_CHANNELS; ch++) { + unpack_channel(h->spectrum[ch], h->codes[ch]); + + transform(h->wave[ch], h->spectrum[ch]); + + process(h->wave[ch], h->hist[ch]); + } + + /* Decoded data is originally stored in VUMem1 as clamped ints, though final step + * seems may be done done externally (StMakeFinalOut/StFlushWriteBuffer) */ +} + +/* Create final output samples */ +// StMakeFinalOut +static void finalize_output(tac_handle_t* h) { + int i; + + /* original code copies + clamps to PCM buffer here instead of modifying wave, + * but we do it later to potentially allow float output. It also sets total output: + * - type 1 (at loop frame): start_sample = loop_discard, frame_samples = 1024 - loop_discard + * - type 2 (at last frame): start_sample = 0, frame_samples = frame_last + 1 + * - other: start_sample = 0, frame_samples = 1024 + * (only copies or does joint stereo from start_sample) */ + + if (h->header.joint_stereo) { + 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 */ + /* 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; + + ADD (_xyzw, &samples_l, &wave_l[i], &wave_r[i]); /* L = L + R */ + SUB (_xyzw, &samples_r, &wave_l[i], &wave_r[i]); /* R = L - R */ + MOVE (_xyzw, &wave_l[i], &samples_l); + MOVE (_xyzw, &wave_r[i], &samples_r); + } + } +} + +/* read huffman codes for all channels (max per channel 27*32 = 864 + 27 + 1 = 892) */ +static int read_codes(tac_handle_t* h, const uint8_t* ptr, uint16_t huff_flag, uint32_t huff_cfg) { + int huff_count = 0; + int ch; + uint32_t unkA = 0; + uint32_t unkB = huff_cfg; + uint32_t unkC = 0; + uint32_t unkD = 0xFFFFFFFF; + + for (ch = 0; ch < TAC_CHANNELS; ch++) { + int huff_done = 0; + int huff_todo = 28; + int16_t huff_val = 0; + + for (; huff_done < huff_todo; huff_done++) { + unkD = unkD >> 14; + unkA = h->huff_table_4[(unkB - unkC) / unkD]; + unkC += h->huff_table_3[unkA] * unkD; + unkD *= h->huff_table_1[unkA]; + + while (0xFFFFFF >= (unkC ^ (unkC + unkD))) { + unkB = (unkB << 8) | (*ptr++); + unkD = (unkD << 8); + unkC = (unkC << 8); + } + + while (0xFFFF >= unkD) { + unkD = (((~unkC) + 1) & 0xFFFF) << 8; + unkB = (unkB << 8) | (*ptr++); + unkC = (unkC << 8); + } + + if (unkA >= 0xFE) { + uint32_t unkT; + unkT = unkA == 0xFE; + unkD = unkD >> (unkT ? 8 : 13); + unkA = (unkB - unkC) / unkD; + unkC = unkC + (unkA * unkD); + if (unkT) + unkA += 0xFE; + + while (0xFFFFFF >= (unkC ^ (unkC + unkD))) { + unkB = (unkB << 8) | (*ptr++); + unkD = (unkD << 8); + unkC = (unkC << 8); + } + + while (0xFFFF >= unkD) { + unkD = (((~unkC) + 1) & 0xFFFF) << 8; + unkB = (unkB << 8) | (*ptr++); + unkC = (unkC << 8); + } + } + + if (unkA & 1) { + huff_val = -((((int16_t)unkA) + 1) / 2); + } else { + huff_val = unkA / 2; + } + + if (huff_done < 28) { + if (huff_flag) { + huff_val += h->huff_table_2[ch][huff_done]; + } + + h->huff_table_2[ch][huff_done] = huff_val; + + if (huff_done != 0 && (huff_val << 16) != 0) { + huff_todo += 32; + } + } + + h->codes[ch][huff_done] = huff_val; + } + + huff_count += huff_done; + } + + return huff_count; +} + + +/* CRC-16/GENIBUS implementation */ +/* https://reveng.sourceforge.io/crc-catalogue/16.htm#crc.cat.crc-16-genibus */ + +#define CRC16_INIT 0xFFFF +#define CRC16_POLY 0x1021 +#define CRC16_XOR_OUT 0xFFFF + +static uint16_t crc16(const uint8_t* data, int length) { + uint16_t i, crc = CRC16_INIT; + + while (length--) { + for (crc ^= *data++ << 8, i = 0; i < 8; i++) { + crc = (crc & 0x8000) ? crc << 1 ^ CRC16_POLY : crc << 1; + } + } + + return crc ^ CRC16_XOR_OUT; +} + +/* ************************************************************************* */ +/* SETUP */ +/* ************************************************************************* */ + +static uint32_t get_u32be(const uint8_t* mem) { + return (mem[0] << 24) | (mem[1] << 16) | (mem[2] << 8) | mem[3]; +} + +static uint32_t get_u32le(const uint8_t* mem) { + return (mem[3] << 24) | (mem[2] << 16) | (mem[1] << 8) | mem[0]; +} + +static uint16_t get_u16le(const uint8_t* mem) { + return (mem[1] << 8) | mem[0]; +} + +static int init_header(tac_header_t* header, const uint8_t* buf) { + header->huffman_offset = get_u32le(buf+0x00); + header->unknown = get_u32le(buf+0x04); + header->loop_frame = get_u16le(buf+0x08); + header->loop_discard = get_u16le(buf+0x0A); + header->frame_count = get_u16le(buf+0x0C); + header->frame_last = get_u16le(buf+0x0E); + header->loop_offset = get_u32le(buf+0x10); + header->file_size = get_u32le(buf+0x14); + header->joint_stereo = get_u32le(buf+0x18); + header->empty = get_u32le(buf+0x1c); + + /* huffman table offset should make sense */ + if (header->huffman_offset < 0x20 || header->huffman_offset > TAC_BLOCK_SIZE) + return TAC_PROCESS_HEADER_ERROR; + /* header size ia block-aligned (but actual size can be smaller, ex. VP 00000715) */ + if (header->file_size % TAC_BLOCK_SIZE != 0) + return TAC_PROCESS_HEADER_ERROR; + /* loop_discard over max makes game crash, while frame_last seems to ignore it */ + if (header->loop_discard > TAC_FRAME_SAMPLES || header->frame_last + 1 > TAC_FRAME_SAMPLES) + return TAC_PROCESS_HEADER_ERROR; + /* looping makes sense */ + if (header->loop_frame > header->frame_count || header->loop_offset > header->file_size) + return TAC_PROCESS_HEADER_ERROR; + /* just in case */ + if ((header->joint_stereo != 0 && header->joint_stereo != 1) || header->empty != 0) + return TAC_PROCESS_HEADER_ERROR; + + return TAC_PROCESS_OK; +} + + +/* AKA RangeDecodeInit (Csd module) */ +static int init_huffman(tac_handle_t* h, const uint8_t* buf) { + uint8_t idx = 0; + int offset = 0; + int i, j; + + /* initialize huff_table_1 with values from header */ + for (i = 0; i < 256; i++) { + int16_t n = buf[offset++]; + + if (n & 0x80) { + n &= 0x7F; + n |= buf[offset++] << 7; + } + + h->huff_table_1[i] = n; + } + + /* zero-initialize huff_table_2 */ + for (i = 0; i < TAC_CHANNELS; i++) { + for (j = 0; j < 32; j++) { + h->huff_table_2[i][j] = 0; + } + } + + /* initialize huff_table_3 */ + h->huff_table_1[256] = 1; + h->huff_table_3[0] = 0; + for (i = 1, j = 0; i < 258; i++, j++) { + h->huff_table_3[i] = h->huff_table_3[j] + h->huff_table_1[j]; + } + + /* initialize huff_table_4 */ + for (idx = 0; !h->huff_table_1[idx]; idx++) { + ; + } + + for (i = 0; i < 16383; i++) { + if (i >= h->huff_table_3[idx+1]) { + while (!h->huff_table_1[++idx]) { + ; + } + } + h->huff_table_4[i] = idx; + } + + return offset; +} + +/* ************************************************************************* */ +/* API */ +/* ************************************************************************* */ + +tac_handle_t* tac_init(const uint8_t* buf, int buf_size) { + tac_handle_t* handle = NULL; + + /* assumes 1 block */ + if (buf_size < TAC_BLOCK_SIZE) + goto fail; + + handle = malloc(sizeof(tac_handle_t)); + if (!handle) goto fail; + + { + int res, pos; + + res = init_header(&handle->header, buf); + if (res != TAC_PROCESS_OK) goto fail; + + pos = init_huffman(handle, &buf[handle->header.huffman_offset]); + if (pos <= 0) goto fail; + + handle->data_start = handle->header.huffman_offset + pos; + if (handle->data_start > TAC_BLOCK_SIZE) + goto fail; + } + + tac_reset(handle); + + return handle; +fail: + tac_free(handle); + return NULL; +} + +const tac_header_t* tac_get_header(tac_handle_t* handle) { + if (!handle) + return NULL; + return &handle->header; +} + +void tac_free(tac_handle_t* handle) { + if (!handle) + return; + free(handle); +} + +void tac_reset(tac_handle_t* handle) { + if (!handle) + return; + + handle->frame_offset = handle->data_start; + handle->frame_number = 1; + + memset(handle->hist, 0, sizeof(REG_VF) * TAC_CHANNELS * (TAC_FRAME_SAMPLES / 4)); + memset(handle->huff_table_2, 0, sizeof(int16_t) * TAC_CHANNELS * 32); +} + + +int tac_decode_frame(tac_handle_t* handle, const uint8_t* block) { + int pos = handle->frame_offset; + + if (handle->frame_number > handle->header.frame_count) + return TAC_PROCESS_DONE; + + if (pos + 0x04 > TAC_BLOCK_SIZE) + return TAC_PROCESS_ERROR_SIZE; + + /* new block marker (may be right at block's end) */ + if (get_u32le(block + pos) == 0xFFFFFFFF) { + handle->frame_offset = 0; /* start at the beginning of next block */ + return TAC_PROCESS_NEXT_BLOCK; + } + + if (pos + 0x0C > TAC_BLOCK_SIZE) + return TAC_PROCESS_ERROR_SIZE; + + + /* read new frame */ + { + const uint8_t* buf = &block[pos]; /* current frame */ + uint16_t frame_crc = get_u16le(buf + 0x00); /* checksum of data starting at 0x04 */ + uint16_t huff_flag = get_u16le(buf + 0x02) >> 15; /* 0 every 64th frame, 1 for others */ + uint16_t frame_size = get_u16le(buf + 0x02) & 0x7FFF; /* not including base header (0x08) */ + uint16_t frame_id = get_u16le(buf + 0x04); /* current number */ + uint16_t huff_count = get_u16le(buf + 0x06); /* huffman decoded length */ + + uint32_t huff_cfg = get_u32be(buf + 0x08); /* huffman table setup */ + uint16_t crc_calc, huff_read; + + if (frame_id != handle->frame_number) + return TAC_PROCESS_ERROR_ID; + + if (pos + 0x08 + frame_size > TAC_BLOCK_SIZE) + return TAC_PROCESS_ERROR_SIZE; + + /* from tests seems CRC errors cause current frame to be skipped, so change values before validations */ + handle->frame_number++; + handle->frame_offset += 0x08 + frame_size; + + crc_calc = crc16(buf + 0x04, 0x04 + frame_size); + if (frame_crc != crc_calc) + return TAC_PROCESS_ERROR_CRC; + + /* extract huffman frame codes */ + huff_read = read_codes(handle, buf + 0x0C, huff_flag, huff_cfg); + if (huff_read != huff_count) + return TAC_PROCESS_ERROR_HUFFMAN; + + /* main decode */ + decode_vu1(handle); + + /* post process */ + finalize_output(handle); + } + + /* current frame decoded and samples can be requested */ + return TAC_PROCESS_OK; +} + + +static inline int16_t clamp16f(float sample) { + if (sample > 32767.0) + return 32767; + else if (sample < -32768.0) + return -32768; + return (int16_t)sample; +} + +void tac_get_samples_pcm16(tac_handle_t* handle, int16_t* dst) { + int ch, i; + int chs = TAC_CHANNELS; + + for (ch = 0; ch < chs; ch++) { + int s = 0; + for (i = 0; i < TAC_FRAME_SAMPLES / 4; i++) { + dst[(s+0)*chs + ch] = clamp16f(handle->wave[ch][i].f.x); + dst[(s+1)*chs + ch] = clamp16f(handle->wave[ch][i].f.y); + dst[(s+2)*chs + ch] = clamp16f(handle->wave[ch][i].f.z); + dst[(s+3)*chs + ch] = clamp16f(handle->wave[ch][i].f.w); + s += 4; + } + } +} + + +void tac_set_loop(tac_handle_t* handle) { + handle->frame_number = handle->header.loop_frame; + handle->frame_offset = 0; +} diff --git a/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.h b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.h new file mode 100644 index 000000000..aa28289d7 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib.h @@ -0,0 +1,55 @@ +#ifndef _TAC_DECODER_LIB_H_ +#define _TAC_DECODER_LIB_H_ + +/* tri-Ace Codec (TAC) lib, found in PS2 games */ + +#include + +#define TAC_SAMPLE_RATE 48000 +#define TAC_CHANNELS 2 +#define TAC_FRAME_SAMPLES 1024 +#define TAC_BLOCK_SIZE 0x4E000 /* size of a single block with N VBR frames */ + +#define TAC_PROCESS_OK 0 /* frame decoded correctly */ +#define TAC_PROCESS_NEXT_BLOCK 1 /* must pass next block (didn't decode) */ +#define TAC_PROCESS_DONE 2 /* no more frames to do (didn't decode) */ +#define TAC_PROCESS_HEADER_ERROR -1 /* file doesn't match expected header */ +#define TAC_PROCESS_ERROR_SIZE -2 /* buffer is smaller than needed */ +#define TAC_PROCESS_ERROR_ID -3 /* expected frame id mismatch */ +#define TAC_PROCESS_ERROR_CRC -4 /* expected frame crc mismatch */ +#define TAC_PROCESS_ERROR_HUFFMAN -5 /* expected huffman count mismatch */ + +typedef struct tac_handle_t tac_handle_t; + +typedef struct { + /* 0x20 header config */ + uint32_t huffman_offset; /* setup */ + uint32_t unknown; /* ignored? (may be CDVD stuff, divided/multiplied during PS2 process, not size related) */ + uint16_t loop_frame; /* aligned to block start */ + uint16_t loop_discard; /* discarded start samples in loop frame (lower = outputs more) */ + uint16_t frame_count; /* number of valid frames ("block end" frames not included) */ + 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 empty; /* always null */ +} tac_header_t; + + +/* inits codec with data from at least one block */ +tac_handle_t* tac_init(const uint8_t* buf, int buf_size); + +const tac_header_t* tac_get_header(tac_handle_t* handle); + +void tac_reset(tac_handle_t* handle); + +void tac_free(tac_handle_t* handle); + +/* decodes a frame from current block (of TAC_BLOCK_SIZE), returning TAC_PROCESS_* codes */ +int tac_decode_frame(tac_handle_t* handle, const uint8_t* block); + +void tac_get_samples_pcm16(tac_handle_t* handle, int16_t* dst); + +void tac_set_loop(tac_handle_t* handle); + +#endif /* _TAC_DECODER_LIB_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib_data.h b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib_data.h new file mode 100644 index 000000000..150a9bbe0 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib_data.h @@ -0,0 +1,1002 @@ +#ifndef _TAC_DECODER_LIB_DATA_H_ +#define _TAC_DECODER_LIB_DATA_H_ + +/* VU1 register simulation, needs type conversion at times (should be optimized out by compiler). */ +typedef union { + struct { + float x,y,z,w; + } f; + + struct { + uint32_t x,y,z,w; + } i; + + float F[4]; + uint32_t UL[4]; + int32_t SL[4]; + /* can access as US/SS/etc but not needed by current code */ +} _REG_VF; +typedef _REG_VF REG_VF; + + static const int _xyzw = 0xF; +//static const int _xyz_ = 0xE; +//static const int _xy_w = 0xD; +//static const int _xy__ = 0xC; +//static const int _x_zw = 0xB; + static const int _x_z_ = 0xA; +//static const int _x__w = 0x9; + static const int _x___ = 0x8; + static const int __yzw = 0x7; +//static const int __yz_ = 0x6; + static const int __y_w = 0x5; + static const int __y__ = 0x4; +//static const int ___zw = 0x3; + static const int ___z_ = 0x2; + static const int ____w = 0x1; + + +/* "Sac" master volume. Originally an int array (volume=16256, unused=0, unused=16397, unused=0) + * loaded into memory then copied to float register via code. May depend on global volume settings + * rather than fixed though (this value is for the opening). */ +static REG_VF VECTOR_VOLUME = { .f = {16256.0f, 16256.0f, 16256.0f, 16256.0f} }; +static REG_VF VECTOR_ZERO = { .f = {0.0f, 0.0f, 0.0f, 0.0f} }; +static REG_VF VECTOR_ONE = { .f = {1.0f, 1.0f, 1.0f, 1.0f} }; +static REG_VF VECTOR_ROUND = { .f = {0.5f, 0.5f, 0.5f, 0.5f} }; +static REG_VF VECTOR_M_ONE = { .f = {-1.0f, -1.0f, -1.0f, -1.0f} }; + + +/* Tables found in the main ELF, then loaded to VU1 memory. + * Floats are an approximate representation of the hex value, but should end up correctly using that hex + * (ex. 0.857492924 and 0.857492925 both are 0x3F5B84A8). 9 decimals should be enough for all? tables. */ + +/* MPEG antialiasing table */ +static const REG_VF ANTIALIASING_TABLE[64/4] = { + { .i = {0x3F5B84A8,0x3F5E682F,0x3F61B9D7,0x3F6B2DE7} }, //+0.857492924f, +0.868777215f, +0.881741941f, +0.918669164f + { .i = {0x3F731ADE,0x3F77C337,0x3F7BBA82,0x3F7D8708} }, //+0.949628711f, +0.967822492f, +0.983314633f, +0.990341663f + { .i = {0x3F7EDA43,0x3F51B92F,0x3F7FC8FC,0x3F7B0756} }, //+0.995517910f, +0.819231927f, +0.999160528f, +0.980580688f + { .i = {0x3F7FF966,0x3F7FFE66,0x3F7FFF8E,0x3F800000} }, //+0.999899268f, +0.999975562f, +0.999993205f, +1.000000000f + { .i = {0x3F800000,0x3F7FFF8E,0x3F7FFE66,0x3F7FF966} }, //+1.000000000f, +0.999993205f, +0.999975562f, +0.999899268f + { .i = {0x3F7B0756,0x3F7FC8FC,0x3F51B92F,0x3F7EDA43} }, //+0.980580688f, +0.999160528f, +0.819231927f, +0.995517910f + { .i = {0x3F7D8708,0x3F7BBA82,0x3F77C337,0x3F731ADE} }, //+0.990341663f, +0.983314633f, +0.967822492f, +0.949628711f + { .i = {0x3F6B2DE7,0x3F61B9D7,0x3F5E682F,0x3F5B84A8} }, //+0.918669164f, +0.881741941f, +0.868777215f, +0.857492924f + { .i = {0xBF03B5FF,0xBEFD8B40,0xBEF186DA,0xBECA4114} }, //-0.514495790f, -0.495203018f, -0.471731961f, -0.395027757f + { .i = {0xBEA07304,0xBE80D626,0xBE3A4775,0xBE0DF9B3} }, //-0.313377500f, -0.251633823f, -0.181913212f, -0.138647839f + { .i = {0xBDC1B01E,0xBF12CE6D,0xBD27CB87,0xBE48D2AC} }, //-0.094574198f, -0.573462307f, -0.040965583f, -0.196116149f + { .i = {0xBC68A11E,0xBBE55ED3,0xBB727B47,0x00000000} }, //-0.014198570f, -0.006999829f, -0.003699975f, +0.000000000f + { .i = {0x00000000,0xBB727B47,0xBBE55ED3,0xBC68A11E} }, //+0.000000000f, -0.003699975f, -0.006999829f, -0.014198570f + { .i = {0xBE48D2AC,0xBD27CB87,0xBF12CE6D,0xBDC1B01E} }, //-0.196116149f, -0.040965583f, -0.573462307f, -0.094574198f + { .i = {0xBE0DF9B3,0xBE3A4775,0xBE80D626,0xBEA07304} }, //-0.138647839f, -0.181913212f, -0.251633823f, -0.313377500f + { .i = {0xBECA4114,0xBEF186DA,0xBEFD8B40,0xBF03B5FF} }, //-0.395027757f, -0.471731961f, -0.495203018f, -0.514495790f +}; + +/* 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] = { + /* 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 + { .i = {0x3AA14518,0x3F3504F3,0x3AA14518,0x3F3504F3} }, //+0.001230392f, +0.707106769f, +0.001230392f, +0.707106769f + { .i = {0x3B0A74BA,0x3F1837F0,0x3B0A74BA,0x3F1837F0} }, //+0.002112670f, +0.594603539f, +0.002112670f, +0.594603539f + { .i = {0x3B4B2FF5,0x3F000000,0x3B4B2FF5,0x3F000000} }, //+0.003100393f, +0.500000000f, +0.003100393f, +0.500000000f + { .i = {0x3B88CC4F,0x3ED744FD,0x3B88CC4F,0x3ED744FD} }, //+0.004174746f, +0.420448214f, +0.004174746f, +0.420448214f + { .i = {0x3BAE718E,0x3EB504F3,0x3BAE718E,0x3EB504F3} }, //+0.005323595f, +0.353553385f, +0.005323595f, +0.353553385f + { .i = {0x3BD63F90,0x3E9837F0,0x3BD63F90,0x3E9837F0} }, //+0.006538339f, +0.297301769f, +0.006538339f, +0.297301769f + { .i = {0x3C000000,0x3E800000,0x3C000000,0x3E800000} }, //+0.007812500f, +0.250000000f, +0.007812500f, +0.250000000f + { .i = {0x3C15C41B,0x3E5744FD,0x3C15C41B,0x3E5744FD} }, //+0.009140993f, +0.210224107f, +0.009140993f, +0.210224107f + { .i = {0x3C2C5AD3,0x3E3504F3,0x3C2C5AD3,0x3E3504F3} }, //+0.010519701f, +0.176776692f, +0.010519701f, +0.176776692f + { .i = {0x3C43B5D3,0x3E1837F0,0x3C43B5D3,0x3E1837F0} }, //+0.011945206f, +0.148650885f, +0.011945206f, +0.148650885f + { .i = {0x3C5BC8FF,0x3E000000,0x3C5BC8FF,0x3E000000} }, //+0.013414620f, +0.125000000f, +0.013414620f, +0.125000000f + { .i = {0x3C7489EF,0x3DD744FD,0x3C7489EF,0x3DD744FD} }, //+0.014925464f, +0.105112053f, +0.014925464f, +0.105112053f + { .i = {0x3C86F7CD,0x3DB504F3,0x3C86F7CD,0x3DB504F3} }, //+0.016475582f, +0.088388346f, +0.016475582f, +0.088388346f + { .i = {0x3C93F904,0x3D9837F0,0x3C93F904,0x3D9837F0} }, //+0.018063076f, +0.074325442f, +0.018063076f, +0.074325442f + { .i = {0x3CA14518,0x3D800000,0x3CA14518,0x3D800000} }, //+0.019686267f, +0.062500000f, +0.019686267f, +0.062500000f + { .i = {0x3CAED8DF,0x3D5744FD,0x3CAED8DF,0x3D5744FD} }, //+0.021343647f, +0.052556027f, +0.021343647f, +0.052556027f + { .i = {0x3CBCB181,0x3D3504F3,0x3CBCB181,0x3D3504F3} }, //+0.023033859f, +0.044194173f, +0.023033859f, +0.044194173f + { .i = {0x3CCACC6C,0x3D1837F0,0x3CCACC6C,0x3D1837F0} }, //+0.024755679f, +0.037162721f, +0.024755679f, +0.037162721f + { .i = {0x3CD92746,0x3D000000,0x3CD92746,0x3D000000} }, //+0.026507985f, +0.031250000f, +0.026507985f, +0.031250000f + { .i = {0x3CE7BFE8,0x3CD744FD,0x3CE7BFE8,0x3CD744FD} }, //+0.028289750f, +0.026278013f, +0.028289750f, +0.026278013f + { .i = {0x3CF69458,0x3CB504F3,0x3CF69458,0x3CB504F3} }, //+0.030100033f, +0.022097087f, +0.030100033f, +0.022097087f + { .i = {0x3D02D161,0x3C9837F0,0x3D02D161,0x3C9837F0} }, //+0.031937961f, +0.018581361f, +0.031937961f, +0.018581361f + { .i = {0x3D0A74BA,0x3C800000,0x3D0A74BA,0x3C800000} }, //+0.033802725f, +0.015625000f, +0.033802725f, +0.015625000f + { .i = {0x3D12336D,0x3C5744FD,0x3D12336D,0x3C5744FD} }, //+0.035693575f, +0.013139007f, +0.035693575f, +0.013139007f + { .i = {0x3D1A0CBF,0x3C3504F3,0x3D1A0CBF,0x3C3504F3} }, //+0.037609812f, +0.011048543f, +0.037609812f, +0.011048543f + { .i = {0x3D220000,0x3C1837F0,0x3D220000,0x3C1837F0} }, //+0.039550781f, +0.009290680f, +0.039550781f, +0.009290680f + { .i = {0x3D2A0C8A,0x3C000000,0x3D2A0C8A,0x3C000000} }, //+0.041515864f, +0.007812500f, +0.041515864f, +0.007812500f + { .i = {0x3D3231C3,0x3BD744FD,0x3D3231C3,0x3BD744FD} }, //+0.043504488f, +0.006569503f, +0.043504488f, +0.006569503f + { .i = {0x3D3A6F17,0x3BB504F3,0x3D3A6F17,0x3BB504F3} }, //+0.045516100f, +0.005524272f, +0.045516100f, +0.005524272f + { .i = {0x3D42C3FE,0x3B9837F0,0x3D42C3FE,0x3B9837F0} }, //+0.047550194f, +0.004645340f, +0.047550194f, +0.004645340f + { .i = {0x3D4B2FF5,0x3B800000,0x3D4B2FF5,0x3B800000} }, //+0.049606282f, +0.003906250f, +0.049606282f, +0.003906250f + { .i = {0x3D53B280,0x3B5744FD,0x3D53B280,0x3B5744FD} }, //+0.051683903f, +0.003284752f, +0.051683903f, +0.003284752f + { .i = {0x3D5C4B2A,0x3B3504F3,0x3D5C4B2A,0x3B3504F3} }, //+0.053782620f, +0.002762136f, +0.053782620f, +0.002762136f + { .i = {0x3D64F982,0x3B1837F0,0x3D64F982,0x3B1837F0} }, //+0.055902012f, +0.002322670f, +0.055902012f, +0.002322670f + { .i = {0x3D6DBD20,0x3B000000,0x3D6DBD20,0x3B000000} }, //+0.058041692f, +0.001953125f, +0.058041692f, +0.001953125f + { .i = {0x3D76959C,0x3AD744FD,0x3D76959C,0x3AD744FD} }, //+0.060201272f, +0.001642376f, +0.060201272f, +0.001642376f + { .i = {0x3D7F8298,0x3AB504F3,0x3D7F8298,0x3AB504F3} }, //+0.062380403f, +0.001381068f, +0.062380403f, +0.001381068f + { .i = {0x3D8441DB,0x3A9837F0,0x3D8441DB,0x3A9837F0} }, //+0.064578734f, +0.001161335f, +0.064578734f, +0.001161335f + { .i = {0x3D88CC4F,0x3A800000,0x3D88CC4F,0x3A800000} }, //+0.066795938f, +0.000976563f, +0.066795938f, +0.000976563f + { .i = {0x3D8D607D,0x3A5744FD,0x3D8D607D,0x3A5744FD} }, //+0.069031693f, +0.000821188f, +0.069031693f, +0.000821188f + { .i = {0x3D91FE3D,0x3A3504F3,0x3D91FE3D,0x3A3504F3} }, //+0.071285702f, +0.000690534f, +0.071285702f, +0.000690534f + { .i = {0x3D96A568,0x3A1837F0,0x3D96A568,0x3A1837F0} }, //+0.073557675f, +0.000580668f, +0.073557675f, +0.000580668f + { .i = {0x3D9B55D8,0x3A000000,0x3D9B55D8,0x3A000000} }, //+0.075847328f, +0.000488281f, +0.075847328f, +0.000488281f + { .i = {0x3DA00F69,0x39D744FD,0x3DA00F69,0x39D744FD} }, //+0.078154393f, +0.000410594f, +0.078154393f, +0.000410594f + { .i = {0x3DA4D1F9,0x39B504F3,0x3DA4D1F9,0x39B504F3} }, //+0.080478616f, +0.000345267f, +0.080478616f, +0.000345267f + { .i = {0x3DA99D65,0x399837F0,0x3DA99D65,0x399837F0} }, //+0.082819737f, +0.000290334f, +0.082819737f, +0.000290334f + { .i = {0x3DAE718E,0x39800000,0x3DAE718E,0x39800000} }, //+0.085177526f, +0.000244141f, +0.085177526f, +0.000244141f + { .i = {0x3DB34E55,0x395744FD,0x3DB34E55,0x395744FD} }, //+0.087551750f, +0.000205297f, +0.087551750f, +0.000205297f + { .i = {0x3DB8339A,0x393504F3,0x3DB8339A,0x393504F3} }, //+0.089942172f, +0.000172633f, +0.089942172f, +0.000172633f + { .i = {0x3DBD2142,0x391837F0,0x3DBD2142,0x391837F0} }, //+0.092348590f, +0.000145167f, +0.092348590f, +0.000145167f + { .i = {0x3DC21730,0x39000000,0x3DC21730,0x39000000} }, //+0.094770789f, +0.000122070f, +0.094770789f, +0.000122070f + { .i = {0x3DC71549,0x38D744FD,0x3DC71549,0x38D744FD} }, //+0.097208567f, +0.000102648f, +0.097208567f, +0.000102648f + { .i = {0x3DCC1B72,0x38B504F3,0x3DCC1B72,0x38B504F3} }, //+0.099661723f, +0.000086317f, +0.099661723f, +0.000086317f + { .i = {0x3DD12992,0x389837F0,0x3DD12992,0x389837F0} }, //+0.102130070f, +0.000072583f, +0.102130070f, +0.000072583f + { .i = {0x3DD63F90,0x38800000,0x3DD63F90,0x38800000} }, //+0.104613423f, +0.000061035f, +0.104613423f, +0.000061035f + { .i = {0x3DDB5D54,0x385744FD,0x3DDB5D54,0x385744FD} }, //+0.107111603f, +0.000051324f, +0.107111603f, +0.000051324f + { .i = {0x3DE082C7,0x383504F3,0x3DE082C7,0x383504F3} }, //+0.109624438f, +0.000043158f, +0.109624438f, +0.000043158f + { .i = {0x3DE5AFD1,0x381837F0,0x3DE5AFD1,0x381837F0} }, //+0.112151749f, +0.000036292f, +0.112151749f, +0.000036292f + { .i = {0x3DEAE45E,0x38000000,0x3DEAE45E,0x38000000} }, //+0.114693388f, +0.000030518f, +0.114693388f, +0.000030518f + { .i = {0x3DF02057,0x37D744FD,0x3DF02057,0x37D744FD} }, //+0.117249183f, +0.000025662f, +0.117249183f, +0.000025662f + { .i = {0x3DF563A8,0x37B504F3,0x3DF563A8,0x37B504F3} }, //+0.119818985f, +0.000021579f, +0.119818985f, +0.000021579f + { .i = {0x3DFAAE3C,0x379837F0,0x3DFAAE3C,0x379837F0} }, //+0.122402638f, +0.000018146f, +0.122402638f, +0.000018146f + { .i = {0x3E000000,0x37800000,0x3E000000,0x37800000} }, //+0.125000000f, +0.000015259f, +0.125000000f, +0.000015259f + { .i = {0x3E02AC70,0x375744FD,0x3E02AC70,0x375744FD} }, //+0.127610922f, +0.000012831f, +0.127610922f, +0.000012831f + { .i = {0x3E055C65,0x373504F3,0x3E055C65,0x373504F3} }, //+0.130235270f, +0.000010790f, +0.130235270f, +0.000010790f + { .i = {0x3E080FD6,0x371837F0,0x3E080FD6,0x371837F0} }, //+0.132872909f, +0.000009073f, +0.132872909f, +0.000009073f + { .i = {0x3E0AC6BA,0x37000000,0x3E0AC6BA,0x37000000} }, //+0.135523707f, +0.000007629f, +0.135523707f, +0.000007629f + { .i = {0x3E0D8108,0x36D744FD,0x3E0D8108,0x36D744FD} }, //+0.138187528f, +0.000006416f, +0.138187528f, +0.000006416f + { .i = {0x3E103EB7,0x36B504F3,0x3E103EB7,0x36B504F3} }, //+0.140864238f, +0.000005395f, +0.140864238f, +0.000005395f + { .i = {0x3E12FFC0,0x369837F0,0x3E12FFC0,0x369837F0} }, //+0.143553734f, +0.000004536f, +0.143553734f, +0.000004536f + { .i = {0x3E15C41B,0x36800000,0x3E15C41B,0x36800000} }, //+0.146255895f, +0.000003815f, +0.146255895f, +0.000003815f + { .i = {0x3E188BBF,0x365744FD,0x3E188BBF,0x365744FD} }, //+0.148970589f, +0.000003208f, +0.148970589f, +0.000003208f + { .i = {0x3E1B56A5,0x363504F3,0x3E1B56A5,0x363504F3} }, //+0.151697710f, +0.000002697f, +0.151697710f, +0.000002697f + { .i = {0x3E1E24C5,0x361837F0,0x3E1E24C5,0x361837F0} }, //+0.154437140f, +0.000002268f, +0.154437140f, +0.000002268f + { .i = {0x3E20F618,0x36000000,0x3E20F618,0x36000000} }, //+0.157188773f, +0.000001907f, +0.157188773f, +0.000001907f + { .i = {0x3E23CA96,0x35D744FD,0x3E23CA96,0x35D744FD} }, //+0.159952492f, +0.000001604f, +0.159952492f, +0.000001604f + { .i = {0x3E26A239,0x35B504F3,0x3E26A239,0x35B504F3} }, //+0.162728205f, +0.000001349f, +0.162728205f, +0.000001349f + { .i = {0x3E297CFA,0x359837F0,0x3E297CFA,0x359837F0} }, //+0.165515810f, +0.000001134f, +0.165515810f, +0.000001134f + { .i = {0x3E2C5AD3,0x35800000,0x3E2C5AD3,0x35800000} }, //+0.168315217f, +0.000000954f, +0.168315217f, +0.000000954f + { .i = {0x3E2F3BBB,0x355744FD,0x3E2F3BBB,0x355744FD} }, //+0.171126291f, +0.000000802f, +0.171126291f, +0.000000802f + { .i = {0x3E321FAD,0x353504F3,0x3E321FAD,0x353504F3} }, //+0.173948959f, +0.000000674f, +0.173948959f, +0.000000674f + { .i = {0x3E3506A4,0x351837F0,0x3E3506A4,0x351837F0} }, //+0.176783144f, +0.000000567f, +0.176783144f, +0.000000567f + { .i = {0x3E37F097,0x35000000,0x3E37F097,0x35000000} }, //+0.179628715f, +0.000000477f, +0.179628715f, +0.000000477f + { .i = {0x3E3ADD82,0x34D744FD,0x3E3ADD82,0x34D744FD} }, //+0.182485610f, +0.000000401f, +0.182485610f, +0.000000401f + { .i = {0x3E3DCD5E,0x34B504F3,0x3E3DCD5E,0x34B504F3} }, //+0.185353726f, +0.000000337f, +0.185353726f, +0.000000337f + { .i = {0x3E40C025,0x349837F0,0x3E40C025,0x349837F0} }, //+0.188232973f, +0.000000284f, +0.188232973f, +0.000000284f + { .i = {0x3E43B5D3,0x34800000,0x3E43B5D3,0x34800000} }, //+0.191123292f, +0.000000238f, +0.191123292f, +0.000000238f + { .i = {0x3E46AE60,0x345744FD,0x3E46AE60,0x345744FD} }, //+0.194024563f, +0.000000200f, +0.194024563f, +0.000000200f + { .i = {0x3E49A9C8,0x343504F3,0x3E49A9C8,0x343504F3} }, //+0.196936727f, +0.000000169f, +0.196936727f, +0.000000169f + { .i = {0x3E4CA806,0x341837F0,0x3E4CA806,0x341837F0} }, //+0.199859709f, +0.000000142f, +0.199859709f, +0.000000142f + { .i = {0x3E4FA913,0x34000000,0x3E4FA913,0x34000000} }, //+0.202793404f, +0.000000119f, +0.202793404f, +0.000000119f + { .i = {0x3E52ACEA,0x33D744FD,0x3E52ACEA,0x33D744FD} }, //+0.205737740f, +0.000000100f, +0.205737740f, +0.000000100f + { .i = {0x3E55B388,0x33B504F3,0x3E55B388,0x33B504F3} }, //+0.208692670f, +0.000000084f, +0.208692670f, +0.000000084f + { .i = {0x3E58BCE5,0x339837F0,0x3E58BCE5,0x339837F0} }, //+0.211658075f, +0.000000071f, +0.211658075f, +0.000000071f + { .i = {0x3E5BC8FF,0x33800000,0x3E5BC8FF,0x33800000} }, //+0.214633927f, +0.000000060f, +0.214633927f, +0.000000060f + { .i = {0x3E5ED7CE,0x335744FD,0x3E5ED7CE,0x335744FD} }, //+0.217620105f, +0.000000050f, +0.217620105f, +0.000000050f + { .i = {0x3E61E950,0x333504F3,0x3E61E950,0x333504F3} }, //+0.220616579f, +0.000000042f, +0.220616579f, +0.000000042f + { .i = {0x3E64FD7F,0x331837F0,0x3E64FD7F,0x331837F0} }, //+0.223623261f, +0.000000035f, +0.223623261f, +0.000000035f + { .i = {0x3E681456,0x33000000,0x3E681456,0x33000000} }, //+0.226640075f, +0.000000030f, +0.226640075f, +0.000000030f + { .i = {0x3E6B2DD2,0x32D744FD,0x3E6B2DD2,0x32D744FD} }, //+0.229666978f, +0.000000025f, +0.229666978f, +0.000000025f + { .i = {0x3E6E49ED,0x32B504F3,0x3E6E49ED,0x32B504F3} }, //+0.232703879f, +0.000000021f, +0.232703879f, +0.000000021f + { .i = {0x3E7168A3,0x329837F0,0x3E7168A3,0x329837F0} }, //+0.235750720f, +0.000000018f, +0.235750720f, +0.000000018f + { .i = {0x3E7489EF,0x32800000,0x3E7489EF,0x32800000} }, //+0.238807425f, +0.000000015f, +0.238807425f, +0.000000015f + { .i = {0x3E77ADCF,0x325744FD,0x3E77ADCF,0x325744FD} }, //+0.241873965f, +0.000000013f, +0.241873965f, +0.000000013f + { .i = {0x3E7AD43C,0x323504F3,0x3E7AD43C,0x323504F3} }, //+0.244950235f, +0.000000011f, +0.244950235f, +0.000000011f + { .i = {0x3E7DFD34,0x321837F0,0x3E7DFD34,0x321837F0} }, //+0.248036206f, +0.000000009f, +0.248036206f, +0.000000009f + { .i = {0x3E809459,0x32000000,0x3E809459,0x32000000} }, //+0.251131803f, +0.000000007f, +0.251131803f, +0.000000007f + { .i = {0x3E822B59,0x31D744FD,0x3E822B59,0x31D744FD} }, //+0.254236966f, +0.000000006f, +0.254236966f, +0.000000006f + { .i = {0x3E83C399,0x31B504F3,0x3E83C399,0x31B504F3} }, //+0.257351667f, +0.000000005f, +0.257351667f, +0.000000005f + { .i = {0x3E855D15,0x319837F0,0x3E855D15,0x319837F0} }, //+0.260475785f, +0.000000004f, +0.260475785f, +0.000000004f + { .i = {0x3E86F7CD,0x31800000,0x3E86F7CD,0x31800000} }, //+0.263609320f, +0.000000004f, +0.263609320f, +0.000000004f + { .i = {0x3E8893BE,0x315744FD,0x3E8893BE,0x315744FD} }, //+0.266752183f, +0.000000003f, +0.266752183f, +0.000000003f + { .i = {0x3E8A30E6,0x313504F3,0x3E8A30E6,0x313504F3} }, //+0.269904315f, +0.000000003f, +0.269904315f, +0.000000003f + { .i = {0x3E8BCF45,0x311837F0,0x3E8BCF45,0x311837F0} }, //+0.273065716f, +0.000000002f, +0.273065716f, +0.000000002f + { .i = {0x3E8D6ED7,0x31000000,0x3E8D6ED7,0x31000000} }, //+0.276236266f, +0.000000002f, +0.276236266f, +0.000000002f + { .i = {0x3E8F0F9C,0x30D744FD,0x3E8F0F9C,0x30D744FD} }, //+0.279415965f, +0.000000002f, +0.279415965f, +0.000000002f + { .i = {0x3E90B190,0x30B504F3,0x3E90B190,0x30B504F3} }, //+0.282604694f, +0.000000001f, +0.282604694f, +0.000000001f + { .i = {0x3E9254B4,0x309837F0,0x3E9254B4,0x309837F0} }, //+0.285802484f, +0.000000001f, +0.285802484f, +0.000000001f + { .i = {0x3E93F904,0x30800000,0x3E93F904,0x30800000} }, //+0.289009213f, +0.000000001f, +0.289009213f, +0.000000001f + { .i = {0x3E959E80,0x305744FD,0x3E959E80,0x305744FD} }, //+0.292224884f, +0.000000001f, +0.292224884f, +0.000000001f + { .i = {0x3E974526,0x303504F3,0x3E974526,0x303504F3} }, //+0.295449436f, +0.000000001f, +0.295449436f, +0.000000001f + { .i = {0x3E98ECF3,0x301837F0,0x3E98ECF3,0x301837F0} }, //+0.298682779f, +0.000000001f, +0.298682779f, +0.000000001f + { .i = {0x3E9A95E7,0x30000000,0x3E9A95E7,0x30000000} }, //+0.301924914f, +0.000000000f, +0.301924914f, +0.000000000f + { .i = {0x3E9C4000,0x2FD744FD,0x3E9C4000,0x2FD744FD} }, //+0.305175781f, +0.000000000f, +0.305175781f, +0.000000000f + { .i = {0x3E9DEB3C,0x2FB504F3,0x3E9DEB3C,0x2FB504F3} }, //+0.308435321f, +0.000000000f, +0.308435321f, +0.000000000f + { .i = {0x3E9F979A,0x2F9837F0,0x3E9F979A,0x2F9837F0} }, //+0.311703503f, +0.000000000f, +0.311703503f, +0.000000000f + /* part 2 */ + { .i = {0x3EA14518,0x3F800000,0x3EA14518,0x3F800000} }, //+0.314980268f, +1.000000000f, +0.314980268f, +1.000000000f + { .i = {0x3EA2F3B4,0x3F000000,0x3EA2F3B4,0x3F000000} }, //+0.318265557f, +0.500000000f, +0.318265557f, +0.500000000f + { .i = {0x3EA4A36E,0x3E800000,0x3EA4A36E,0x3E800000} }, //+0.321559370f, +0.250000000f, +0.321559370f, +0.250000000f + { .i = {0x3EA65444,0x3E000000,0x3EA65444,0x3E000000} }, //+0.324861646f, +0.125000000f, +0.324861646f, +0.125000000f + { .i = {0x3EA80634,0x3D800000,0x3EA80634,0x3D800000} }, //+0.328172326f, +0.062500000f, +0.328172326f, +0.062500000f + { .i = {0x3EA9B93D,0x3D000000,0x3EA9B93D,0x3D000000} }, //+0.331491381f, +0.031250000f, +0.331491381f, +0.031250000f + { .i = {0x3EAB6D5D,0x3C800000,0x3EAB6D5D,0x3C800000} }, //+0.334818751f, +0.015625000f, +0.334818751f, +0.015625000f + { .i = {0x3EAD2294,0x3C000000,0x3EAD2294,0x3C000000} }, //+0.338154435f, +0.007812500f, +0.338154435f, +0.007812500f + { .i = {0x3EAED8DF,0x3B800000,0x3EAED8DF,0x3B800000} }, //+0.341498345f, +0.003906250f, +0.341498345f, +0.003906250f + { .i = {0x3EB0903D,0x3B000000,0x3EB0903D,0x3B000000} }, //+0.344850451f, +0.001953125f, +0.344850451f, +0.001953125f + { .i = {0x3EB248AE,0x3A800000,0x3EB248AE,0x3A800000} }, //+0.348210752f, +0.000976563f, +0.348210752f, +0.000976563f + { .i = {0x3EB4022F,0x3A000000,0x3EB4022F,0x3A000000} }, //+0.351579159f, +0.000488281f, +0.351579159f, +0.000488281f + { .i = {0x3EB5BCBF,0x39800000,0x3EB5BCBF,0x39800000} }, //+0.354955643f, +0.000244141f, +0.354955643f, +0.000244141f + { .i = {0x3EB7785E,0x39000000,0x3EB7785E,0x39000000} }, //+0.358340204f, +0.000122070f, +0.358340204f, +0.000122070f + { .i = {0x3EB93509,0x38800000,0x3EB93509,0x38800000} }, //+0.361732751f, +0.000061035f, +0.361732751f, +0.000061035f + { .i = {0x3EBAF2C0,0x38000000,0x3EBAF2C0,0x38000000} }, //+0.365133286f, +0.000030518f, +0.365133286f, +0.000030518f + { .i = {0x3EBCB181,0x00000000,0x3EBCB181,0x00000000} }, //+0.368541747f, +0.000000000f, +0.368541747f, +0.000000000f + { .i = {0x3EBE714C,0x00000000,0x3EBE714C,0x00000000} }, //+0.371958137f, +0.000000000f, +0.371958137f, +0.000000000f + { .i = {0x3EC0321E,0x00000000,0x3EC0321E,0x00000000} }, //+0.375382364f, +0.000000000f, +0.375382364f, +0.000000000f + { .i = {0x3EC1F3F6,0x00000000,0x3EC1F3F6,0x00000000} }, //+0.378814399f, +0.000000000f, +0.378814399f, +0.000000000f + { .i = {0x3EC3B6D5,0x00000000,0x3EC3B6D5,0x00000000} }, //+0.382254273f, +0.000000000f, +0.382254273f, +0.000000000f + { .i = {0x3EC57AB7,0x00000000,0x3EC57AB7,0x00000000} }, //+0.385701865f, +0.000000000f, +0.385701865f, +0.000000000f + { .i = {0x3EC73F9C,0x00000000,0x3EC73F9C,0x00000000} }, //+0.389157176f, +0.000000000f, +0.389157176f, +0.000000000f + { .i = {0x3EC90584,0x00000000,0x3EC90584,0x00000000} }, //+0.392620206f, +0.000000000f, +0.392620206f, +0.000000000f + { .i = {0x3ECACC6C,0x00000000,0x3ECACC6C,0x00000000} }, //+0.396090865f, +0.000000000f, +0.396090865f, +0.000000000f + { .i = {0x3ECC9454,0x00000000,0x3ECC9454,0x00000000} }, //+0.399569154f, +0.000000000f, +0.399569154f, +0.000000000f + { .i = {0x3ECE5D3A,0x00000000,0x3ECE5D3A,0x00000000} }, //+0.403055012f, +0.000000000f, +0.403055012f, +0.000000000f + { .i = {0x3ED0271E,0x00000000,0x3ED0271E,0x00000000} }, //+0.406548440f, +0.000000000f, +0.406548440f, +0.000000000f + { .i = {0x3ED1F1FF,0x00000000,0x3ED1F1FF,0x00000000} }, //+0.410049409f, +0.000000000f, +0.410049409f, +0.000000000f + { .i = {0x3ED3BDDA,0x00000000,0x3ED3BDDA,0x00000000} }, //+0.413557827f, +0.000000000f, +0.413557827f, +0.000000000f + { .i = {0x3ED58AB0,0x00000000,0x3ED58AB0,0x00000000} }, //+0.417073727f, +0.000000000f, +0.417073727f, +0.000000000f + { .i = {0x3ED7587F,0x00000000,0x3ED7587F,0x00000000} }, //+0.420597047f, +0.000000000f, +0.420597047f, +0.000000000f + { .i = {0x3ED92746,0x00000000,0x3ED92746,0x00000000} }, //+0.424127758f, +0.000000000f, +0.424127758f, +0.000000000f + { .i = {0x3EDAF704,0x00000000,0x3EDAF704,0x00000000} }, //+0.427665830f, +0.000000000f, +0.427665830f, +0.000000000f + { .i = {0x3EDCC7B8,0x00000000,0x3EDCC7B8,0x00000000} }, //+0.431211233f, +0.000000000f, +0.431211233f, +0.000000000f + { .i = {0x3EDE9961,0x00000000,0x3EDE9961,0x00000000} }, //+0.434763938f, +0.000000000f, +0.434763938f, +0.000000000f + { .i = {0x3EE06BFE,0x00000000,0x3EE06BFE,0x00000000} }, //+0.438323915f, +0.000000000f, +0.438323915f, +0.000000000f + { .i = {0x3EE23F8F,0x00000000,0x3EE23F8F,0x00000000} }, //+0.441891164f, +0.000000000f, +0.441891164f, +0.000000000f + { .i = {0x3EE41411,0x00000000,0x3EE41411,0x00000000} }, //+0.445465595f, +0.000000000f, +0.445465595f, +0.000000000f + { .i = {0x3EE5E984,0x00000000,0x3EE5E984,0x00000000} }, //+0.449047208f, +0.000000000f, +0.449047208f, +0.000000000f + { .i = {0x3EE7BFE8,0x00000000,0x3EE7BFE8,0x00000000} }, //+0.452636003f, +0.000000000f, +0.452636003f, +0.000000000f + { .i = {0x3EE9973A,0x00000000,0x3EE9973A,0x00000000} }, //+0.456231892f, +0.000000000f, +0.456231892f, +0.000000000f + { .i = {0x3EEB6F7B,0x00000000,0x3EEB6F7B,0x00000000} }, //+0.459834903f, +0.000000000f, +0.459834903f, +0.000000000f + { .i = {0x3EED48AA,0x00000000,0x3EED48AA,0x00000000} }, //+0.463445008f, +0.000000000f, +0.463445008f, +0.000000000f + { .i = {0x3EEF22C4,0x00000000,0x3EEF22C4,0x00000000} }, //+0.467062116f, +0.000000000f, +0.467062116f, +0.000000000f + { .i = {0x3EF0FDCA,0x00000000,0x3EF0FDCA,0x00000000} }, //+0.470686257f, +0.000000000f, +0.470686257f, +0.000000000f + { .i = {0x3EF2D9BB,0x00000000,0x3EF2D9BB,0x00000000} }, //+0.474317402f, +0.000000000f, +0.474317402f, +0.000000000f + { .i = {0x3EF4B695,0x00000000,0x3EF4B695,0x00000000} }, //+0.477955490f, +0.000000000f, +0.477955490f, +0.000000000f + { .i = {0x3EF69458,0x00000000,0x3EF69458,0x00000000} }, //+0.481600523f, +0.000000000f, +0.481600523f, +0.000000000f + { .i = {0x3EF87302,0x00000000,0x3EF87302,0x00000000} }, //+0.485252440f, +0.000000000f, +0.485252440f, +0.000000000f + { .i = {0x3EFA5294,0x00000000,0x3EFA5294,0x00000000} }, //+0.488911271f, +0.000000000f, +0.488911271f, +0.000000000f + { .i = {0x3EFC330C,0x00000000,0x3EFC330C,0x00000000} }, //+0.492576957f, +0.000000000f, +0.492576957f, +0.000000000f + { .i = {0x3EFE1469,0x00000000,0x3EFE1469,0x00000000} }, //+0.496249467f, +0.000000000f, +0.496249467f, +0.000000000f + { .i = {0x3EFFF6AB,0x00000000,0x3EFFF6AB,0x00000000} }, //+0.499928802f, +0.000000000f, +0.499928802f, +0.000000000f + { .i = {0x3F00ECE8,0x00000000,0x3F00ECE8,0x00000000} }, //+0.503614902f, +0.000000000f, +0.503614902f, +0.000000000f + { .i = {0x3F01DEEC,0x00000000,0x3F01DEEC,0x00000000} }, //+0.507307768f, +0.000000000f, +0.507307768f, +0.000000000f + { .i = {0x3F02D161,0x00000000,0x3F02D161,0x00000000} }, //+0.511007369f, +0.000000000f, +0.511007369f, +0.000000000f + { .i = {0x3F03C446,0x00000000,0x3F03C446,0x00000000} }, //+0.514713645f, +0.000000000f, +0.514713645f, +0.000000000f + { .i = {0x3F04B79C,0x00000000,0x3F04B79C,0x00000000} }, //+0.518426657f, +0.000000000f, +0.518426657f, +0.000000000f + { .i = {0x3F05AB61,0x00000000,0x3F05AB61,0x00000000} }, //+0.522146285f, +0.000000000f, +0.522146285f, +0.000000000f + { .i = {0x3F069F96,0x00000000,0x3F069F96,0x00000000} }, //+0.525872588f, +0.000000000f, +0.525872588f, +0.000000000f + { .i = {0x3F079439,0x00000000,0x3F079439,0x00000000} }, //+0.529605448f, +0.000000000f, +0.529605448f, +0.000000000f + { .i = {0x3F08894B,0x00000000,0x3F08894B,0x00000000} }, //+0.533344924f, +0.000000000f, +0.533344924f, +0.000000000f + { .i = {0x3F097ECC,0x00000000,0x3F097ECC,0x00000000} }, //+0.537091017f, +0.000000000f, +0.537091017f, +0.000000000f + { .i = {0x3F0A74BA,0x00000000,0x3F0A74BA,0x00000000} }, //+0.540843606f, +0.000000000f, +0.540843606f, +0.000000000f + { .i = {0x3F0B6B15,0x00000000,0x3F0B6B15,0x00000000} }, //+0.544602692f, +0.000000000f, +0.544602692f, +0.000000000f + { .i = {0x3F0C61DE,0x00000000,0x3F0C61DE,0x00000000} }, //+0.548368335f, +0.000000000f, +0.548368335f, +0.000000000f + { .i = {0x3F0D5913,0x00000000,0x3F0D5913,0x00000000} }, //+0.552140415f, +0.000000000f, +0.552140415f, +0.000000000f + { .i = {0x3F0E50B4,0x00000000,0x3F0E50B4,0x00000000} }, //+0.555918932f, +0.000000000f, +0.555918932f, +0.000000000f + { .i = {0x3F0F48C2,0x00000000,0x3F0F48C2,0x00000000} }, //+0.559703946f, +0.000000000f, +0.559703946f, +0.000000000f + { .i = {0x3F10413A,0x00000000,0x3F10413A,0x00000000} }, //+0.563495278f, +0.000000000f, +0.563495278f, +0.000000000f + { .i = {0x3F113A1E,0x00000000,0x3F113A1E,0x00000000} }, //+0.567293048f, +0.000000000f, +0.567293048f, +0.000000000f + { .i = {0x3F12336D,0x00000000,0x3F12336D,0x00000000} }, //+0.571097195f, +0.000000000f, +0.571097195f, +0.000000000f + { .i = {0x3F132D27,0x00000000,0x3F132D27,0x00000000} }, //+0.574907720f, +0.000000000f, +0.574907720f, +0.000000000f + { .i = {0x3F14274A,0x00000000,0x3F14274A,0x00000000} }, //+0.578724504f, +0.000000000f, +0.578724504f, +0.000000000f + { .i = {0x3F1521D7,0x00000000,0x3F1521D7,0x00000000} }, //+0.582547605f, +0.000000000f, +0.582547605f, +0.000000000f + { .i = {0x3F161CCE,0x00000000,0x3F161CCE,0x00000000} }, //+0.586377025f, +0.000000000f, +0.586377025f, +0.000000000f + { .i = {0x3F17182D,0x00000000,0x3F17182D,0x00000000} }, //+0.590212643f, +0.000000000f, +0.590212643f, +0.000000000f + { .i = {0x3F1813F6,0x00000000,0x3F1813F6,0x00000000} }, //+0.594054580f, +0.000000000f, +0.594054580f, +0.000000000f + { .i = {0x3F191027,0x00000000,0x3F191027,0x00000000} }, //+0.597902715f, +0.000000000f, +0.597902715f, +0.000000000f + { .i = {0x3F1A0CBF,0x00000000,0x3F1A0CBF,0x00000000} }, //+0.601756990f, +0.000000000f, +0.601756990f, +0.000000000f + { .i = {0x3F1B09C0,0x00000000,0x3F1B09C0,0x00000000} }, //+0.605617523f, +0.000000000f, +0.605617523f, +0.000000000f + { .i = {0x3F1C0728,0x00000000,0x3F1C0728,0x00000000} }, //+0.609484196f, +0.000000000f, +0.609484196f, +0.000000000f + { .i = {0x3F1D04F7,0x00000000,0x3F1D04F7,0x00000000} }, //+0.613357008f, +0.000000000f, +0.613357008f, +0.000000000f + { .i = {0x3F1E032C,0x00000000,0x3F1E032C,0x00000000} }, //+0.617235899f, +0.000000000f, +0.617235899f, +0.000000000f + { .i = {0x3F1F01C9,0x00000000,0x3F1F01C9,0x00000000} }, //+0.621120989f, +0.000000000f, +0.621120989f, +0.000000000f + { .i = {0x3F2000CB,0x00000000,0x3F2000CB,0x00000000} }, //+0.625012100f, +0.000000000f, +0.625012100f, +0.000000000f + { .i = {0x3F210033,0x00000000,0x3F210033,0x00000000} }, //+0.628909290f, +0.000000000f, +0.628909290f, +0.000000000f + { .i = {0x3F220000,0x00000000,0x3F220000,0x00000000} }, //+0.632812500f, +0.000000000f, +0.632812500f, +0.000000000f + { .i = {0x3F230033,0x00000000,0x3F230033,0x00000000} }, //+0.636721790f, +0.000000000f, +0.636721790f, +0.000000000f + { .i = {0x3F2400CA,0x00000000,0x3F2400CA,0x00000000} }, //+0.640637040f, +0.000000000f, +0.640637040f, +0.000000000f + { .i = {0x3F2501C6,0x00000000,0x3F2501C6,0x00000000} }, //+0.644558311f, +0.000000000f, +0.644558311f, +0.000000000f + { .i = {0x3F260326,0x00000000,0x3F260326,0x00000000} }, //+0.648485541f, +0.000000000f, +0.648485541f, +0.000000000f + { .i = {0x3F2704EA,0x00000000,0x3F2704EA,0x00000000} }, //+0.652418733f, +0.000000000f, +0.652418733f, +0.000000000f + { .i = {0x3F280711,0x00000000,0x3F280711,0x00000000} }, //+0.656357825f, +0.000000000f, +0.656357825f, +0.000000000f + { .i = {0x3F29099C,0x00000000,0x3F29099C,0x00000000} }, //+0.660302877f, +0.000000000f, +0.660302877f, +0.000000000f + { .i = {0x3F2A0C8A,0x00000000,0x3F2A0C8A,0x00000000} }, //+0.664253831f, +0.000000000f, +0.664253831f, +0.000000000f + { .i = {0x3F2B0FDB,0x00000000,0x3F2B0FDB,0x00000000} }, //+0.668210685f, +0.000000000f, +0.668210685f, +0.000000000f + { .i = {0x3F2C138E,0x00000000,0x3F2C138E,0x00000000} }, //+0.672173381f, +0.000000000f, +0.672173381f, +0.000000000f + { .i = {0x3F2D17A3,0x00000000,0x3F2D17A3,0x00000000} }, //+0.676141918f, +0.000000000f, +0.676141918f, +0.000000000f + { .i = {0x3F2E1C1A,0x00000000,0x3F2E1C1A,0x00000000} }, //+0.680116296f, +0.000000000f, +0.680116296f, +0.000000000f + { .i = {0x3F2F20F2,0x00000000,0x3F2F20F2,0x00000000} }, //+0.684096456f, +0.000000000f, +0.684096456f, +0.000000000f + { .i = {0x3F30262C,0x00000000,0x3F30262C,0x00000000} }, //+0.688082457f, +0.000000000f, +0.688082457f, +0.000000000f + { .i = {0x3F312BC7,0x00000000,0x3F312BC7,0x00000000} }, //+0.692074239f, +0.000000000f, +0.692074239f, +0.000000000f + { .i = {0x3F3231C3,0x00000000,0x3F3231C3,0x00000000} }, //+0.696071804f, +0.000000000f, +0.696071804f, +0.000000000f + { .i = {0x3F33381F,0x00000000,0x3F33381F,0x00000000} }, //+0.700075090f, +0.000000000f, +0.700075090f, +0.000000000f + { .i = {0x3F343EDB,0x00000000,0x3F343EDB,0x00000000} }, //+0.704084098f, +0.000000000f, +0.704084098f, +0.000000000f + { .i = {0x3F3545F7,0x00000000,0x3F3545F7,0x00000000} }, //+0.708098829f, +0.000000000f, +0.708098829f, +0.000000000f + { .i = {0x3F364D72,0x00000000,0x3F364D72,0x00000000} }, //+0.712119222f, +0.000000000f, +0.712119222f, +0.000000000f + { .i = {0x3F37554D,0x00000000,0x3F37554D,0x00000000} }, //+0.716145337f, +0.000000000f, +0.716145337f, +0.000000000f + { .i = {0x3F385D87,0x00000000,0x3F385D87,0x00000000} }, //+0.720177114f, +0.000000000f, +0.720177114f, +0.000000000f + { .i = {0x3F396620,0x00000000,0x3F396620,0x00000000} }, //+0.724214554f, +0.000000000f, +0.724214554f, +0.000000000f + { .i = {0x3F3A6F17,0x00000000,0x3F3A6F17,0x00000000} }, //+0.728257596f, +0.000000000f, +0.728257596f, +0.000000000f + { .i = {0x3F3B786D,0x00000000,0x3F3B786D,0x00000000} }, //+0.732306302f, +0.000000000f, +0.732306302f, +0.000000000f + { .i = {0x3F3C8221,0x00000000,0x3F3C8221,0x00000000} }, //+0.736360610f, +0.000000000f, +0.736360610f, +0.000000000f + { .i = {0x3F3D8C32,0x00000000,0x3F3D8C32,0x00000000} }, //+0.740420461f, +0.000000000f, +0.740420461f, +0.000000000f + { .i = {0x3F3E96A1,0x00000000,0x3F3E96A1,0x00000000} }, //+0.744485915f, +0.000000000f, +0.744485915f, +0.000000000f + { .i = {0x3F3FA16D,0x00000000,0x3F3FA16D,0x00000000} }, //+0.748556912f, +0.000000000f, +0.748556912f, +0.000000000f + { .i = {0x3F40AC96,0x00000000,0x3F40AC96,0x00000000} }, //+0.752633452f, +0.000000000f, +0.752633452f, +0.000000000f + { .i = {0x3F41B81C,0x00000000,0x3F41B81C,0x00000000} }, //+0.756715536f, +0.000000000f, +0.756715536f, +0.000000000f + { .i = {0x3F42C3FE,0x00000000,0x3F42C3FE,0x00000000} }, //+0.760803103f, +0.000000000f, +0.760803103f, +0.000000000f + { .i = {0x3F43D03D,0x00000000,0x3F43D03D,0x00000000} }, //+0.764896214f, +0.000000000f, +0.764896214f, +0.000000000f + { .i = {0x3F44DCD8,0x00000000,0x3F44DCD8,0x00000000} }, //+0.768994808f, +0.000000000f, +0.768994808f, +0.000000000f + { .i = {0x3F45E9CE,0x00000000,0x3F45E9CE,0x00000000} }, //+0.773098826f, +0.000000000f, +0.773098826f, +0.000000000f + { .i = {0x3F46F720,0x00000000,0x3F46F720,0x00000000} }, //+0.777208328f, +0.000000000f, +0.777208328f, +0.000000000f + { .i = {0x3F4804CD,0x00000000,0x3F4804CD,0x00000000} }, //+0.781323254f, +0.000000000f, +0.781323254f, +0.000000000f + { .i = {0x3F4912D5,0x00000000,0x3F4912D5,0x00000000} }, //+0.785443604f, +0.000000000f, +0.785443604f, +0.000000000f + { .i = {0x3F4A2138,0x00000000,0x3F4A2138,0x00000000} }, //+0.789569378f, +0.000000000f, +0.789569378f, +0.000000000f + { .i = {0x3F4B2FF5,0x00000000,0x3F4B2FF5,0x00000000} }, //+0.793700516f, +0.000000000f, +0.793700516f, +0.000000000f + { .i = {0x3F4C3F0D,0x00000000,0x3F4C3F0D,0x00000000} }, //+0.797837079f, +0.000000000f, +0.797837079f, +0.000000000f + { .i = {0x3F4D4E7F,0x00000000,0x3F4D4E7F,0x00000000} }, //+0.801979005f, +0.000000000f, +0.801979005f, +0.000000000f + { .i = {0x3F4E5E4A,0x00000000,0x3F4E5E4A,0x00000000} }, //+0.806126237f, +0.000000000f, +0.806126237f, +0.000000000f + { .i = {0x3F4F6E70,0x00000000,0x3F4F6E70,0x00000000} }, //+0.810278893f, +0.000000000f, +0.810278893f, +0.000000000f + { .i = {0x3F507EEE,0x00000000,0x3F507EEE,0x00000000} }, //+0.814436793f, +0.000000000f, +0.814436793f, +0.000000000f + { .i = {0x3F518FC6,0x00000000,0x3F518FC6,0x00000000} }, //+0.818600059f, +0.000000000f, +0.818600059f, +0.000000000f + { .i = {0x3F52A0F7,0x00000000,0x3F52A0F7,0x00000000} }, //+0.822768629f, +0.000000000f, +0.822768629f, +0.000000000f + { .i = {0x3F53B280,0x00000000,0x3F53B280,0x00000000} }, //+0.826942444f, +0.000000000f, +0.826942444f, +0.000000000f + { .i = {0x3F54C462,0x00000000,0x3F54C462,0x00000000} }, //+0.831121564f, +0.000000000f, +0.831121564f, +0.000000000f + { .i = {0x3F55D69C,0x00000000,0x3F55D69C,0x00000000} }, //+0.835305929f, +0.000000000f, +0.835305929f, +0.000000000f + { .i = {0x3F56E92E,0x00000000,0x3F56E92E,0x00000000} }, //+0.839495540f, +0.000000000f, +0.839495540f, +0.000000000f + { .i = {0x3F57FC18,0x00000000,0x3F57FC18,0x00000000} }, //+0.843690395f, +0.000000000f, +0.843690395f, +0.000000000f + { .i = {0x3F590F5A,0x00000000,0x3F590F5A,0x00000000} }, //+0.847890496f, +0.000000000f, +0.847890496f, +0.000000000f + { .i = {0x3F5A22F2,0x00000000,0x3F5A22F2,0x00000000} }, //+0.852095723f, +0.000000000f, +0.852095723f, +0.000000000f + { .i = {0x3F5B36E3,0x00000000,0x3F5B36E3,0x00000000} }, //+0.856306255f, +0.000000000f, +0.856306255f, +0.000000000f + { .i = {0x3F5C4B2A,0x00000000,0x3F5C4B2A,0x00000000} }, //+0.860521913f, +0.000000000f, +0.860521913f, +0.000000000f + { .i = {0x3F5D5FC7,0x00000000,0x3F5D5FC7,0x00000000} }, //+0.864742696f, +0.000000000f, +0.864742696f, +0.000000000f + { .i = {0x3F5E74BC,0x00000000,0x3F5E74BC,0x00000000} }, //+0.868968725f, +0.000000000f, +0.868968725f, +0.000000000f + { .i = {0x3F5F8A06,0x00000000,0x3F5F8A06,0x00000000} }, //+0.873199821f, +0.000000000f, +0.873199821f, +0.000000000f + { .i = {0x3F609FA7,0x00000000,0x3F609FA7,0x00000000} }, //+0.877436101f, +0.000000000f, +0.877436101f, +0.000000000f + { .i = {0x3F61B59D,0x00000000,0x3F61B59D,0x00000000} }, //+0.881677449f, +0.000000000f, +0.881677449f, +0.000000000f + { .i = {0x3F62CBE9,0x00000000,0x3F62CBE9,0x00000000} }, //+0.885923922f, +0.000000000f, +0.885923922f, +0.000000000f + { .i = {0x3F63E28B,0x00000000,0x3F63E28B,0x00000000} }, //+0.890175521f, +0.000000000f, +0.890175521f, +0.000000000f + { .i = {0x3F64F982,0x00000000,0x3F64F982,0x00000000} }, //+0.894432187f, +0.000000000f, +0.894432187f, +0.000000000f + { .i = {0x3F6610CE,0x00000000,0x3F6610CE,0x00000000} }, //+0.898693919f, +0.000000000f, +0.898693919f, +0.000000000f + { .i = {0x3F67286F,0x00000000,0x3F67286F,0x00000000} }, //+0.902960718f, +0.000000000f, +0.902960718f, +0.000000000f + { .i = {0x3F684065,0x00000000,0x3F684065,0x00000000} }, //+0.907232583f, +0.000000000f, +0.907232583f, +0.000000000f + { .i = {0x3F6958AF,0x00000000,0x3F6958AF,0x00000000} }, //+0.911509454f, +0.000000000f, +0.911509454f, +0.000000000f + { .i = {0x3F6A714D,0x00000000,0x3F6A714D,0x00000000} }, //+0.915791333f, +0.000000000f, +0.915791333f, +0.000000000f + { .i = {0x3F6B8A3F,0x00000000,0x3F6B8A3F,0x00000000} }, //+0.920078218f, +0.000000000f, +0.920078218f, +0.000000000f + { .i = {0x3F6CA386,0x00000000,0x3F6CA386,0x00000000} }, //+0.924370170f, +0.000000000f, +0.924370170f, +0.000000000f + { .i = {0x3F6DBD20,0x00000000,0x3F6DBD20,0x00000000} }, //+0.928667068f, +0.000000000f, +0.928667068f, +0.000000000f + { .i = {0x3F6ED70D,0x00000000,0x3F6ED70D,0x00000000} }, //+0.932968915f, +0.000000000f, +0.932968915f, +0.000000000f + { .i = {0x3F6FF14E,0x00000000,0x3F6FF14E,0x00000000} }, //+0.937275767f, +0.000000000f, +0.937275767f, +0.000000000f + { .i = {0x3F710BE1,0x00000000,0x3F710BE1,0x00000000} }, //+0.941587508f, +0.000000000f, +0.941587508f, +0.000000000f + { .i = {0x3F7226C8,0x00000000,0x3F7226C8,0x00000000} }, //+0.945904255f, +0.000000000f, +0.945904255f, +0.000000000f + { .i = {0x3F734202,0x00000000,0x3F734202,0x00000000} }, //+0.950225949f, +0.000000000f, +0.950225949f, +0.000000000f + { .i = {0x3F745D8E,0x00000000,0x3F745D8E,0x00000000} }, //+0.954552531f, +0.000000000f, +0.954552531f, +0.000000000f + { .i = {0x3F75796C,0x00000000,0x3F75796C,0x00000000} }, //+0.958884001f, +0.000000000f, +0.958884001f, +0.000000000f + { .i = {0x3F76959C,0x00000000,0x3F76959C,0x00000000} }, //+0.963220358f, +0.000000000f, +0.963220358f, +0.000000000f + { .i = {0x3F77B21F,0x00000000,0x3F77B21F,0x00000000} }, //+0.967561662f, +0.000000000f, +0.967561662f, +0.000000000f + { .i = {0x3F78CEF3,0x00000000,0x3F78CEF3,0x00000000} }, //+0.971907794f, +0.000000000f, +0.971907794f, +0.000000000f + { .i = {0x3F79EC19,0x00000000,0x3F79EC19,0x00000000} }, //+0.976258814f, +0.000000000f, +0.976258814f, +0.000000000f + { .i = {0x3F7B0990,0x00000000,0x3F7B0990,0x00000000} }, //+0.980614662f, +0.000000000f, +0.980614662f, +0.000000000f + { .i = {0x3F7C2759,0x00000000,0x3F7C2759,0x00000000} }, //+0.984975398f, +0.000000000f, +0.984975398f, +0.000000000f + { .i = {0x3F7D4572,0x00000000,0x3F7D4572,0x00000000} }, //+0.989340901f, +0.000000000f, +0.989340901f, +0.000000000f + { .i = {0x3F7E63DD,0x00000000,0x3F7E63DD,0x00000000} }, //+0.993711293f, +0.000000000f, +0.993711293f, +0.000000000f + { .i = {0x3F7F8298,0x00000000,0x3F7F8298,0x00000000} }, //+0.998086452f, +0.000000000f, +0.998086452f, +0.000000000f + { .i = {0x3F8050D2,0x00000000,0x3F8050D2,0x00000000} }, //+1.002466440f, +0.000000000f, +1.002466440f, +0.000000000f + { .i = {0x3F80E080,0x00000000,0x3F80E080,0x00000000} }, //+1.006851196f, +0.000000000f, +1.006851196f, +0.000000000f + { .i = {0x3F817056,0x00000000,0x3F817056,0x00000000} }, //+1.011240721f, +0.000000000f, +1.011240721f, +0.000000000f + { .i = {0x3F820054,0x00000000,0x3F820054,0x00000000} }, //+1.015635014f, +0.000000000f, +1.015635014f, +0.000000000f + { .i = {0x3F82907A,0x00000000,0x3F82907A,0x00000000} }, //+1.020034075f, +0.000000000f, +1.020034075f, +0.000000000f + { .i = {0x3F8320C8,0x00000000,0x3F8320C8,0x00000000} }, //+1.024437904f, +0.000000000f, +1.024437904f, +0.000000000f + { .i = {0x3F83B13E,0x00000000,0x3F83B13E,0x00000000} }, //+1.028846502f, +0.000000000f, +1.028846502f, +0.000000000f + { .i = {0x3F8441DB,0x00000000,0x3F8441DB,0x00000000} }, //+1.033259749f, +0.000000000f, +1.033259749f, +0.000000000f + { .i = {0x3F84D2A0,0x00000000,0x3F84D2A0,0x00000000} }, //+1.037677765f, +0.000000000f, +1.037677765f, +0.000000000f + { .i = {0x3F85638C,0x00000000,0x3F85638C,0x00000000} }, //+1.042100430f, +0.000000000f, +1.042100430f, +0.000000000f + { .i = {0x3F85F4A0,0x00000000,0x3F85F4A0,0x00000000} }, //+1.046527863f, +0.000000000f, +1.046527863f, +0.000000000f + { .i = {0x3F8685DB,0x00000000,0x3F8685DB,0x00000000} }, //+1.050959945f, +0.000000000f, +1.050959945f, +0.000000000f + { .i = {0x3F87173D,0x00000000,0x3F87173D,0x00000000} }, //+1.055396676f, +0.000000000f, +1.055396676f, +0.000000000f + { .i = {0x3F87A8C7,0x00000000,0x3F87A8C7,0x00000000} }, //+1.059838176f, +0.000000000f, +1.059838176f, +0.000000000f + { .i = {0x3F883A77,0x00000000,0x3F883A77,0x00000000} }, //+1.064284205f, +0.000000000f, +1.064284205f, +0.000000000f + { .i = {0x3F88CC4F,0x00000000,0x3F88CC4F,0x00000000} }, //+1.068735003f, +0.000000000f, +1.068735003f, +0.000000000f + { .i = {0x3F895E4D,0x00000000,0x3F895E4D,0x00000000} }, //+1.073190331f, +0.000000000f, +1.073190331f, +0.000000000f + { .i = {0x3F89F072,0x00000000,0x3F89F072,0x00000000} }, //+1.077650309f, +0.000000000f, +1.077650309f, +0.000000000f + { .i = {0x3F8A82BE,0x00000000,0x3F8A82BE,0x00000000} }, //+1.082114935f, +0.000000000f, +1.082114935f, +0.000000000f + { .i = {0x3F8B1531,0x00000000,0x3F8B1531,0x00000000} }, //+1.086584210f, +0.000000000f, +1.086584210f, +0.000000000f + { .i = {0x3F8BA7CA,0x00000000,0x3F8BA7CA,0x00000000} }, //+1.091058016f, +0.000000000f, +1.091058016f, +0.000000000f + { .i = {0x3F8C3A8A,0x00000000,0x3F8C3A8A,0x00000000} }, //+1.095536470f, +0.000000000f, +1.095536470f, +0.000000000f + { .i = {0x3F8CCD70,0x00000000,0x3F8CCD70,0x00000000} }, //+1.100019455f, +0.000000000f, +1.100019455f, +0.000000000f + { .i = {0x3F8D607D,0x00000000,0x3F8D607D,0x00000000} }, //+1.104507089f, +0.000000000f, +1.104507089f, +0.000000000f + { .i = {0x3F8DF3B0,0x00000000,0x3F8DF3B0,0x00000000} }, //+1.108999252f, +0.000000000f, +1.108999252f, +0.000000000f + { .i = {0x3F8E8709,0x00000000,0x3F8E8709,0x00000000} }, //+1.113495946f, +0.000000000f, +1.113495946f, +0.000000000f + { .i = {0x3F8F1A88,0x00000000,0x3F8F1A88,0x00000000} }, //+1.117997169f, +0.000000000f, +1.117997169f, +0.000000000f + { .i = {0x3F8FAE2D,0x00000000,0x3F8FAE2D,0x00000000} }, //+1.122502923f, +0.000000000f, +1.122502923f, +0.000000000f + { .i = {0x3F9041F8,0x00000000,0x3F9041F8,0x00000000} }, //+1.127013206f, +0.000000000f, +1.127013206f, +0.000000000f + { .i = {0x3F90D5EA,0x00000000,0x3F90D5EA,0x00000000} }, //+1.131528139f, +0.000000000f, +1.131528139f, +0.000000000f + { .i = {0x3F916A00,0x00000000,0x3F916A00,0x00000000} }, //+1.136047363f, +0.000000000f, +1.136047363f, +0.000000000f + { .i = {0x3F91FE3D,0x00000000,0x3F91FE3D,0x00000000} }, //+1.140571237f, +0.000000000f, +1.140571237f, +0.000000000f + { .i = {0x3F92929F,0x00000000,0x3F92929F,0x00000000} }, //+1.145099521f, +0.000000000f, +1.145099521f, +0.000000000f + { .i = {0x3F932727,0x00000000,0x3F932727,0x00000000} }, //+1.149632335f, +0.000000000f, +1.149632335f, +0.000000000f + { .i = {0x3F93BBD5,0x00000000,0x3F93BBD5,0x00000000} }, //+1.154169679f, +0.000000000f, +1.154169679f, +0.000000000f + { .i = {0x3F9450A8,0x00000000,0x3F9450A8,0x00000000} }, //+1.158711433f, +0.000000000f, +1.158711433f, +0.000000000f + { .i = {0x3F94E5A0,0x00000000,0x3F94E5A0,0x00000000} }, //+1.163257599f, +0.000000000f, +1.163257599f, +0.000000000f + { .i = {0x3F957ABD,0x00000000,0x3F957ABD,0x00000000} }, //+1.167808175f, +0.000000000f, +1.167808175f, +0.000000000f + { .i = {0x3F961000,0x00000000,0x3F961000,0x00000000} }, //+1.172363281f, +0.000000000f, +1.172363281f, +0.000000000f + { .i = {0x3F96A568,0x00000000,0x3F96A568,0x00000000} }, //+1.176922798f, +0.000000000f, +1.176922798f, +0.000000000f + { .i = {0x3F973AF5,0x00000000,0x3F973AF5,0x00000000} }, //+1.181486726f, +0.000000000f, +1.181486726f, +0.000000000f + { .i = {0x3F97D0A7,0x00000000,0x3F97D0A7,0x00000000} }, //+1.186055064f, +0.000000000f, +1.186055064f, +0.000000000f + { .i = {0x3F98667E,0x00000000,0x3F98667E,0x00000000} }, //+1.190627813f, +0.000000000f, +1.190627813f, +0.000000000f + { .i = {0x3F98FC7A,0x00000000,0x3F98FC7A,0x00000000} }, //+1.195204973f, +0.000000000f, +1.195204973f, +0.000000000f + { .i = {0x3F99929A,0x00000000,0x3F99929A,0x00000000} }, //+1.199786425f, +0.000000000f, +1.199786425f, +0.000000000f + { .i = {0x3F9A28DF,0x00000000,0x3F9A28DF,0x00000000} }, //+1.204372287f, +0.000000000f, +1.204372287f, +0.000000000f + { .i = {0x3F9ABF49,0x00000000,0x3F9ABF49,0x00000000} }, //+1.208962560f, +0.000000000f, +1.208962560f, +0.000000000f + { .i = {0x3F9B55D8,0x00000000,0x3F9B55D8,0x00000000} }, //+1.213557243f, +0.000000000f, +1.213557243f, +0.000000000f + { .i = {0x3F9BEC8B,0x00000000,0x3F9BEC8B,0x00000000} }, //+1.218156219f, +0.000000000f, +1.218156219f, +0.000000000f + { .i = {0x3F9C8363,0x00000000,0x3F9C8363,0x00000000} }, //+1.222759604f, +0.000000000f, +1.222759604f, +0.000000000f + { .i = {0x3F9D1A5E,0x00000000,0x3F9D1A5E,0x00000000} }, //+1.227367163f, +0.000000000f, +1.227367163f, +0.000000000f + { .i = {0x3F9DB17F,0x00000000,0x3F9DB17F,0x00000000} }, //+1.231979251f, +0.000000000f, +1.231979251f, +0.000000000f + { .i = {0x3F9E48C3,0x00000000,0x3F9E48C3,0x00000000} }, //+1.236595511f, +0.000000000f, +1.236595511f, +0.000000000f + { .i = {0x3F9EE02C,0x00000000,0x3F9EE02C,0x00000000} }, //+1.241216183f, +0.000000000f, +1.241216183f, +0.000000000f + { .i = {0x3F9F77B8,0x00000000,0x3F9F77B8,0x00000000} }, //+1.245841026f, +0.000000000f, +1.245841026f, +0.000000000f + { .i = {0x3FA00F69,0x00000000,0x3FA00F69,0x00000000} }, //+1.250470281f, +0.000000000f, +1.250470281f, +0.000000000f + { .i = {0x3FA0A73E,0x00000000,0x3FA0A73E,0x00000000} }, //+1.255103827f, +0.000000000f, +1.255103827f, +0.000000000f + { .i = {0x3FA13F37,0x00000000,0x3FA13F37,0x00000000} }, //+1.259741664f, +0.000000000f, +1.259741664f, +0.000000000f + { .i = {0x3FA1D753,0x00000000,0x3FA1D753,0x00000000} }, //+1.264383674f, +0.000000000f, +1.264383674f, +0.000000000f + { .i = {0x3FA26F93,0x00000000,0x3FA26F93,0x00000000} }, //+1.269029975f, +0.000000000f, +1.269029975f, +0.000000000f + { .i = {0x3FA307F7,0x00000000,0x3FA307F7,0x00000000} }, //+1.273680568f, +0.000000000f, +1.273680568f, +0.000000000f + { .i = {0x3FA3A07F,0x00000000,0x3FA3A07F,0x00000000} }, //+1.278335452f, +0.000000000f, +1.278335452f, +0.000000000f + { .i = {0x3FA4392A,0x00000000,0x3FA4392A,0x00000000} }, //+1.282994509f, +0.000000000f, +1.282994509f, +0.000000000f + { .i = {0x3FA4D1F9,0x00000000,0x3FA4D1F9,0x00000000} }, //+1.287657857f, +0.000000000f, +1.287657857f, +0.000000000f + { .i = {0x3FA56AEB,0x00000000,0x3FA56AEB,0x00000000} }, //+1.292325377f, +0.000000000f, +1.292325377f, +0.000000000f + { .i = {0x3FA60400,0x00000000,0x3FA60400,0x00000000} }, //+1.296997070f, +0.000000000f, +1.296997070f, +0.000000000f + { .i = {0x3FA69D39,0x00000000,0x3FA69D39,0x00000000} }, //+1.301673055f, +0.000000000f, +1.301673055f, +0.000000000f + { .i = {0x3FA73695,0x00000000,0x3FA73695,0x00000000} }, //+1.306353211f, +0.000000000f, +1.306353211f, +0.000000000f + { .i = {0x3FA7D015,0x00000000,0x3FA7D015,0x00000000} }, //+1.311037660f, +0.000000000f, +1.311037660f, +0.000000000f + { .i = {0x3FA869B7,0x00000000,0x3FA869B7,0x00000000} }, //+1.315726161f, +0.000000000f, +1.315726161f, +0.000000000f + { .i = {0x3FA9037D,0x00000000,0x3FA9037D,0x00000000} }, //+1.320418954f, +0.000000000f, +1.320418954f, +0.000000000f + { .i = {0x3FA99D65,0x00000000,0x3FA99D65,0x00000000} }, //+1.325115800f, +0.000000000f, +1.325115800f, +0.000000000f + { .i = {0x3FAA3771,0x00000000,0x3FAA3771,0x00000000} }, //+1.329816937f, +0.000000000f, +1.329816937f, +0.000000000f + { .i = {0x3FAAD19F,0x00000000,0x3FAAD19F,0x00000000} }, //+1.334522128f, +0.000000000f, +1.334522128f, +0.000000000f + { .i = {0x3FAB6BF0,0x00000000,0x3FAB6BF0,0x00000000} }, //+1.339231491f, +0.000000000f, +1.339231491f, +0.000000000f + { .i = {0x3FAC0664,0x00000000,0x3FAC0664,0x00000000} }, //+1.343945026f, +0.000000000f, +1.343945026f, +0.000000000f + { .i = {0x3FACA0FB,0x00000000,0x3FACA0FB,0x00000000} }, //+1.348662734f, +0.000000000f, +1.348662734f, +0.000000000f + { .i = {0x3FAD3BB4,0x00000000,0x3FAD3BB4,0x00000000} }, //+1.353384495f, +0.000000000f, +1.353384495f, +0.000000000f + { .i = {0x3FADD690,0x00000000,0x3FADD690,0x00000000} }, //+1.358110428f, +0.000000000f, +1.358110428f, +0.000000000f + { .i = {0x3FAE718E,0x00000000,0x3FAE718E,0x00000000} }, //+1.362840414f, +0.000000000f, +1.362840414f, +0.000000000f + { .i = {0x3FAF0CAF,0x00000000,0x3FAF0CAF,0x00000000} }, //+1.367574573f, +0.000000000f, +1.367574573f, +0.000000000f + { .i = {0x3FAFA7F2,0x00000000,0x3FAFA7F2,0x00000000} }, //+1.372312784f, +0.000000000f, +1.372312784f, +0.000000000f + { .i = {0x3FB04358,0x00000000,0x3FB04358,0x00000000} }, //+1.377055168f, +0.000000000f, +1.377055168f, +0.000000000f + { .i = {0x3FB0DEE0,0x00000000,0x3FB0DEE0,0x00000000} }, //+1.381801605f, +0.000000000f, +1.381801605f, +0.000000000f + { .i = {0x3FB17A8A,0x00000000,0x3FB17A8A,0x00000000} }, //+1.386552095f, +0.000000000f, +1.386552095f, +0.000000000f + { .i = {0x3FB21656,0x00000000,0x3FB21656,0x00000000} }, //+1.391306639f, +0.000000000f, +1.391306639f, +0.000000000f + { .i = {0x3FB2B244,0x00000000,0x3FB2B244,0x00000000} }, //+1.396065235f, +0.000000000f, +1.396065235f, +0.000000000f + { .i = {0x3FB34E55,0x00000000,0x3FB34E55,0x00000000} }, //+1.400828004f, +0.000000000f, +1.400828004f, +0.000000000f + { .i = {0x3FB3EA87,0x00000000,0x3FB3EA87,0x00000000} }, //+1.405594707f, +0.000000000f, +1.405594707f, +0.000000000f + { .i = {0x3FB486DB,0x00000000,0x3FB486DB,0x00000000} }, //+1.410365462f, +0.000000000f, +1.410365462f, +0.000000000f + { .i = {0x3FB52352,0x00000000,0x3FB52352,0x00000000} }, //+1.415140390f, +0.000000000f, +1.415140390f, +0.000000000f + { .i = {0x3FB5BFE9,0x00000000,0x3FB5BFE9,0x00000000} }, //+1.419919133f, +0.000000000f, +1.419919133f, +0.000000000f + { .i = {0x3FB65CA3,0x00000000,0x3FB65CA3,0x00000000} }, //+1.424702048f, +0.000000000f, +1.424702048f, +0.000000000f + { .i = {0x3FB6F97F,0x00000000,0x3FB6F97F,0x00000000} }, //+1.429489017f, +0.000000000f, +1.429489017f, +0.000000000f + { .i = {0x3FB7967C,0x00000000,0x3FB7967C,0x00000000} }, //+1.434279919f, +0.000000000f, +1.434279919f, +0.000000000f + { .i = {0x3FB8339A,0x00000000,0x3FB8339A,0x00000000} }, //+1.439074755f, +0.000000000f, +1.439074755f, +0.000000000f + { .i = {0x3FB8D0DB,0x00000000,0x3FB8D0DB,0x00000000} }, //+1.443873763f, +0.000000000f, +1.443873763f, +0.000000000f + { .i = {0x3FB96E3C,0x00000000,0x3FB96E3C,0x00000000} }, //+1.448676586f, +0.000000000f, +1.448676586f, +0.000000000f + { .i = {0x3FBA0BBF,0x00000000,0x3FBA0BBF,0x00000000} }, //+1.453483462f, +0.000000000f, +1.453483462f, +0.000000000f + { .i = {0x3FBAA964,0x00000000,0x3FBAA964,0x00000000} }, //+1.458294392f, +0.000000000f, +1.458294392f, +0.000000000f + { .i = {0x3FBB472A,0x00000000,0x3FBB472A,0x00000000} }, //+1.463109255f, +0.000000000f, +1.463109255f, +0.000000000f + { .i = {0x3FBBE511,0x00000000,0x3FBBE511,0x00000000} }, //+1.467928052f, +0.000000000f, +1.467928052f, +0.000000000f + { .i = {0x3FBC8319,0x00000000,0x3FBC8319,0x00000000} }, //+1.472750783f, +0.000000000f, +1.472750783f, +0.000000000f + { .i = {0x3FBD2142,0x00000000,0x3FBD2142,0x00000000} }, //+1.477577448f, +0.000000000f, +1.477577448f, +0.000000000f + { .i = {0x3FBDBF8D,0x00000000,0x3FBDBF8D,0x00000000} }, //+1.482408166f, +0.000000000f, +1.482408166f, +0.000000000f + { .i = {0x3FBE5DF8,0x00000000,0x3FBE5DF8,0x00000000} }, //+1.487242699f, +0.000000000f, +1.487242699f, +0.000000000f + { .i = {0x3FBEFC85,0x00000000,0x3FBEFC85,0x00000000} }, //+1.492081285f, +0.000000000f, +1.492081285f, +0.000000000f + { .i = {0x3FBF9B32,0x00000000,0x3FBF9B32,0x00000000} }, //+1.496923685f, +0.000000000f, +1.496923685f, +0.000000000f + { .i = {0x3FC03A01,0x00000000,0x3FC03A01,0x00000000} }, //+1.501770139f, +0.000000000f, +1.501770139f, +0.000000000f + { .i = {0x3FC0D8F0,0x00000000,0x3FC0D8F0,0x00000000} }, //+1.506620407f, +0.000000000f, +1.506620407f, +0.000000000f + { .i = {0x3FC17800,0x00000000,0x3FC17800,0x00000000} }, //+1.511474609f, +0.000000000f, +1.511474609f, +0.000000000f + { .i = {0x3FC21730,0x00000000,0x3FC21730,0x00000000} }, //+1.516332626f, +0.000000000f, +1.516332626f, +0.000000000f + { .i = {0x3FC2B682,0x00000000,0x3FC2B682,0x00000000} }, //+1.521194696f, +0.000000000f, +1.521194696f, +0.000000000f + { .i = {0x3FC355F3,0x00000000,0x3FC355F3,0x00000000} }, //+1.526060462f, +0.000000000f, +1.526060462f, +0.000000000f + { .i = {0x3FC3F586,0x00000000,0x3FC3F586,0x00000000} }, //+1.530930281f, +0.000000000f, +1.530930281f, +0.000000000f + { .i = {0x3FC49539,0x00000000,0x3FC49539,0x00000000} }, //+1.535803914f, +0.000000000f, +1.535803914f, +0.000000000f + { .i = {0x3FC5350C,0x00000000,0x3FC5350C,0x00000000} }, //+1.540681362f, +0.000000000f, +1.540681362f, +0.000000000f + { .i = {0x3FC5D500,0x00000000,0x3FC5D500,0x00000000} }, //+1.545562744f, +0.000000000f, +1.545562744f, +0.000000000f + { .i = {0x3FC67514,0x00000000,0x3FC67514,0x00000000} }, //+1.550447941f, +0.000000000f, +1.550447941f, +0.000000000f + { .i = {0x3FC71549,0x00000000,0x3FC71549,0x00000000} }, //+1.555337071f, +0.000000000f, +1.555337071f, +0.000000000f + { .i = {0x3FC7B59E,0x00000000,0x3FC7B59E,0x00000000} }, //+1.560230017f, +0.000000000f, +1.560230017f, +0.000000000f + { .i = {0x3FC85613,0x00000000,0x3FC85613,0x00000000} }, //+1.565126777f, +0.000000000f, +1.565126777f, +0.000000000f + { .i = {0x3FC8F6A8,0x00000000,0x3FC8F6A8,0x00000000} }, //+1.570027351f, +0.000000000f, +1.570027351f, +0.000000000f + { .i = {0x3FC9975D,0x00000000,0x3FC9975D,0x00000000} }, //+1.574931741f, +0.000000000f, +1.574931741f, +0.000000000f + { .i = {0x3FCA3832,0x00000000,0x3FCA3832,0x00000000} }, //+1.579839945f, +0.000000000f, +1.579839945f, +0.000000000f + { .i = {0x3FCAD928,0x00000000,0x3FCAD928,0x00000000} }, //+1.584752083f, +0.000000000f, +1.584752083f, +0.000000000f + { .i = {0x3FCB7A3D,0x00000000,0x3FCB7A3D,0x00000000} }, //+1.589667916f, +0.000000000f, +1.589667916f, +0.000000000f + { .i = {0x3FCC1B72,0x00000000,0x3FCC1B72,0x00000000} }, //+1.594587564f, +0.000000000f, +1.594587564f, +0.000000000f + { .i = {0x3FCCBCC7,0x00000000,0x3FCCBCC7,0x00000000} }, //+1.599511027f, +0.000000000f, +1.599511027f, +0.000000000f + { .i = {0x3FCD5E3C,0x00000000,0x3FCD5E3C,0x00000000} }, //+1.604438305f, +0.000000000f, +1.604438305f, +0.000000000f + { .i = {0x3FCDFFD1,0x00000000,0x3FCDFFD1,0x00000000} }, //+1.609369397f, +0.000000000f, +1.609369397f, +0.000000000f + { .i = {0x3FCEA185,0x00000000,0x3FCEA185,0x00000000} }, //+1.614304185f, +0.000000000f, +1.614304185f, +0.000000000f + { .i = {0x3FCF4359,0x00000000,0x3FCF4359,0x00000000} }, //+1.619242787f, +0.000000000f, +1.619242787f, +0.000000000f + { .i = {0x3FCFE54C,0x00000000,0x3FCFE54C,0x00000000} }, //+1.624185085f, +0.000000000f, +1.624185085f, +0.000000000f + { .i = {0x3FD0875F,0x00000000,0x3FD0875F,0x00000000} }, //+1.629131198f, +0.000000000f, +1.629131198f, +0.000000000f + { .i = {0x3FD12992,0x00000000,0x3FD12992,0x00000000} }, //+1.634081125f, +0.000000000f, +1.634081125f, +0.000000000f + { .i = {0x3FD1CBE4,0x00000000,0x3FD1CBE4,0x00000000} }, //+1.639034748f, +0.000000000f, +1.639034748f, +0.000000000f + { .i = {0x3FD26E56,0x00000000,0x3FD26E56,0x00000000} }, //+1.643992186f, +0.000000000f, +1.643992186f, +0.000000000f + { .i = {0x3FD310E7,0x00000000,0x3FD310E7,0x00000000} }, //+1.648953319f, +0.000000000f, +1.648953319f, +0.000000000f + { .i = {0x3FD3B397,0x00000000,0x3FD3B397,0x00000000} }, //+1.653918147f, +0.000000000f, +1.653918147f, +0.000000000f + { .i = {0x3FD45666,0x00000000,0x3FD45666,0x00000000} }, //+1.658886671f, +0.000000000f, +1.658886671f, +0.000000000f + { .i = {0x3FD4F955,0x00000000,0x3FD4F955,0x00000000} }, //+1.663859010f, +0.000000000f, +1.663859010f, +0.000000000f + { .i = {0x3FD59C63,0x00000000,0x3FD59C63,0x00000000} }, //+1.668835044f, +0.000000000f, +1.668835044f, +0.000000000f + { .i = {0x3FD63F90,0x00000000,0x3FD63F90,0x00000000} }, //+1.673814774f, +0.000000000f, +1.673814774f, +0.000000000f + { .i = {0x3FD6E2DC,0x00000000,0x3FD6E2DC,0x00000000} }, //+1.678798199f, +0.000000000f, +1.678798199f, +0.000000000f + { .i = {0x3FD78647,0x00000000,0x3FD78647,0x00000000} }, //+1.683785319f, +0.000000000f, +1.683785319f, +0.000000000f + { .i = {0x3FD829D2,0x00000000,0x3FD829D2,0x00000000} }, //+1.688776255f, +0.000000000f, +1.688776255f, +0.000000000f + { .i = {0x3FD8CD7B,0x00000000,0x3FD8CD7B,0x00000000} }, //+1.693770766f, +0.000000000f, +1.693770766f, +0.000000000f + { .i = {0x3FD97143,0x00000000,0x3FD97143,0x00000000} }, //+1.698768973f, +0.000000000f, +1.698768973f, +0.000000000f + { .i = {0x3FDA152A,0x00000000,0x3FDA152A,0x00000000} }, //+1.703770876f, +0.000000000f, +1.703770876f, +0.000000000f + { .i = {0x3FDAB930,0x00000000,0x3FDAB930,0x00000000} }, //+1.708776474f, +0.000000000f, +1.708776474f, +0.000000000f + { .i = {0x3FDB5D54,0x00000000,0x3FDB5D54,0x00000000} }, //+1.713785648f, +0.000000000f, +1.713785648f, +0.000000000f + { .i = {0x3FDC0197,0x00000000,0x3FDC0197,0x00000000} }, //+1.718798518f, +0.000000000f, +1.718798518f, +0.000000000f + { .i = {0x3FDCA5F9,0x00000000,0x3FDCA5F9,0x00000000} }, //+1.723815084f, +0.000000000f, +1.723815084f, +0.000000000f + { .i = {0x3FDD4A7A,0x00000000,0x3FDD4A7A,0x00000000} }, //+1.728835344f, +0.000000000f, +1.728835344f, +0.000000000f + { .i = {0x3FDDEF19,0x00000000,0x3FDDEF19,0x00000000} }, //+1.733859181f, +0.000000000f, +1.733859181f, +0.000000000f + { .i = {0x3FDE93D7,0x00000000,0x3FDE93D7,0x00000000} }, //+1.738886714f, +0.000000000f, +1.738886714f, +0.000000000f + { .i = {0x3FDF38B3,0x00000000,0x3FDF38B3,0x00000000} }, //+1.743917823f, +0.000000000f, +1.743917823f, +0.000000000f + { .i = {0x3FDFDDAE,0x00000000,0x3FDFDDAE,0x00000000} }, //+1.748952627f, +0.000000000f, +1.748952627f, +0.000000000f + { .i = {0x3FE082C7,0x00000000,0x3FE082C7,0x00000000} }, //+1.753991008f, +0.000000000f, +1.753991008f, +0.000000000f + { .i = {0x3FE127FE,0x00000000,0x3FE127FE,0x00000000} }, //+1.759032965f, +0.000000000f, +1.759032965f, +0.000000000f + { .i = {0x3FE1CD54,0x00000000,0x3FE1CD54,0x00000000} }, //+1.764078617f, +0.000000000f, +1.764078617f, +0.000000000f + { .i = {0x3FE272C8,0x00000000,0x3FE272C8,0x00000000} }, //+1.769127846f, +0.000000000f, +1.769127846f, +0.000000000f + { .i = {0x3FE3185A,0x00000000,0x3FE3185A,0x00000000} }, //+1.774180651f, +0.000000000f, +1.774180651f, +0.000000000f + { .i = {0x3FE3BE0B,0x00000000,0x3FE3BE0B,0x00000000} }, //+1.779237151f, +0.000000000f, +1.779237151f, +0.000000000f + { .i = {0x3FE463DA,0x00000000,0x3FE463DA,0x00000000} }, //+1.784297228f, +0.000000000f, +1.784297228f, +0.000000000f + { .i = {0x3FE509C6,0x00000000,0x3FE509C6,0x00000000} }, //+1.789360762f, +0.000000000f, +1.789360762f, +0.000000000f + { .i = {0x3FE5AFD1,0x00000000,0x3FE5AFD1,0x00000000} }, //+1.794427991f, +0.000000000f, +1.794427991f, +0.000000000f + { .i = {0x3FE655FA,0x00000000,0x3FE655FA,0x00000000} }, //+1.799498796f, +0.000000000f, +1.799498796f, +0.000000000f + { .i = {0x3FE6FC41,0x00000000,0x3FE6FC41,0x00000000} }, //+1.804573178f, +0.000000000f, +1.804573178f, +0.000000000f + { .i = {0x3FE7A2A6,0x00000000,0x3FE7A2A6,0x00000000} }, //+1.809651136f, +0.000000000f, +1.809651136f, +0.000000000f + { .i = {0x3FE84929,0x00000000,0x3FE84929,0x00000000} }, //+1.814732671f, +0.000000000f, +1.814732671f, +0.000000000f + { .i = {0x3FE8EFC9,0x00000000,0x3FE8EFC9,0x00000000} }, //+1.819817662f, +0.000000000f, +1.819817662f, +0.000000000f + { .i = {0x3FE99688,0x00000000,0x3FE99688,0x00000000} }, //+1.824906349f, +0.000000000f, +1.824906349f, +0.000000000f + { .i = {0x3FEA3D64,0x00000000,0x3FEA3D64,0x00000000} }, //+1.829998493f, +0.000000000f, +1.829998493f, +0.000000000f + { .i = {0x3FEAE45E,0x00000000,0x3FEAE45E,0x00000000} }, //+1.835094213f, +0.000000000f, +1.835094213f, +0.000000000f + { .i = {0x3FEB8B76,0x00000000,0x3FEB8B76,0x00000000} }, //+1.840193510f, +0.000000000f, +1.840193510f, +0.000000000f + { .i = {0x3FEC32AB,0x00000000,0x3FEC32AB,0x00000000} }, //+1.845296264f, +0.000000000f, +1.845296264f, +0.000000000f + { .i = {0x3FECD9FE,0x00000000,0x3FECD9FE,0x00000000} }, //+1.850402594f, +0.000000000f, +1.850402594f, +0.000000000f + { .i = {0x3FED816E,0x00000000,0x3FED816E,0x00000000} }, //+1.855512381f, +0.000000000f, +1.855512381f, +0.000000000f + { .i = {0x3FEE28FC,0x00000000,0x3FEE28FC,0x00000000} }, //+1.860625744f, +0.000000000f, +1.860625744f, +0.000000000f + { .i = {0x3FEED0A8,0x00000000,0x3FEED0A8,0x00000000} }, //+1.865742683f, +0.000000000f, +1.865742683f, +0.000000000f + { .i = {0x3FEF7871,0x00000000,0x3FEF7871,0x00000000} }, //+1.870863080f, +0.000000000f, +1.870863080f, +0.000000000f + { .i = {0x3FF02057,0x00000000,0x3FF02057,0x00000000} }, //+1.875986934f, +0.000000000f, +1.875986934f, +0.000000000f + { .i = {0x3FF0C85B,0x00000000,0x3FF0C85B,0x00000000} }, //+1.881114364f, +0.000000000f, +1.881114364f, +0.000000000f + { .i = {0x3FF1707C,0x00000000,0x3FF1707C,0x00000000} }, //+1.886245251f, +0.000000000f, +1.886245251f, +0.000000000f + { .i = {0x3FF218BA,0x00000000,0x3FF218BA,0x00000000} }, //+1.891379595f, +0.000000000f, +1.891379595f, +0.000000000f + { .i = {0x3FF2C116,0x00000000,0x3FF2C116,0x00000000} }, //+1.896517515f, +0.000000000f, +1.896517515f, +0.000000000f + { .i = {0x3FF3698F,0x00000000,0x3FF3698F,0x00000000} }, //+1.901658893f, +0.000000000f, +1.901658893f, +0.000000000f + { .i = {0x3FF41225,0x00000000,0x3FF41225,0x00000000} }, //+1.906803727f, +0.000000000f, +1.906803727f, +0.000000000f + { .i = {0x3FF4BAD8,0x00000000,0x3FF4BAD8,0x00000000} }, //+1.911952019f, +0.000000000f, +1.911952019f, +0.000000000f + { .i = {0x3FF563A8,0x00000000,0x3FF563A8,0x00000000} }, //+1.917103767f, +0.000000000f, +1.917103767f, +0.000000000f + { .i = {0x3FF60C95,0x00000000,0x3FF60C95,0x00000000} }, //+1.922258973f, +0.000000000f, +1.922258973f, +0.000000000f + { .i = {0x3FF6B59F,0x00000000,0x3FF6B59F,0x00000000} }, //+1.927417636f, +0.000000000f, +1.927417636f, +0.000000000f + { .i = {0x3FF75EC7,0x00000000,0x3FF75EC7,0x00000000} }, //+1.932579875f, +0.000000000f, +1.932579875f, +0.000000000f + { .i = {0x3FF8080B,0x00000000,0x3FF8080B,0x00000000} }, //+1.937745452f, +0.000000000f, +1.937745452f, +0.000000000f + { .i = {0x3FF8B16C,0x00000000,0x3FF8B16C,0x00000000} }, //+1.942914486f, +0.000000000f, +1.942914486f, +0.000000000f + { .i = {0x3FF95AEA,0x00000000,0x3FF95AEA,0x00000000} }, //+1.948086977f, +0.000000000f, +1.948086977f, +0.000000000f + { .i = {0x3FFA0485,0x00000000,0x3FFA0485,0x00000000} }, //+1.953262925f, +0.000000000f, +1.953262925f, +0.000000000f + { .i = {0x3FFAAE3C,0x00000000,0x3FFAAE3C,0x00000000} }, //+1.958442211f, +0.000000000f, +1.958442211f, +0.000000000f + { .i = {0x3FFB5810,0x00000000,0x3FFB5810,0x00000000} }, //+1.963624954f, +0.000000000f, +1.963624954f, +0.000000000f + { .i = {0x3FFC0201,0x00000000,0x3FFC0201,0x00000000} }, //+1.968811154f, +0.000000000f, +1.968811154f, +0.000000000f + { .i = {0x3FFCAC0F,0x00000000,0x3FFCAC0F,0x00000000} }, //+1.974000812f, +0.000000000f, +1.974000812f, +0.000000000f + { .i = {0x3FFD5639,0x00000000,0x3FFD5639,0x00000000} }, //+1.979193807f, +0.000000000f, +1.979193807f, +0.000000000f + { .i = {0x3FFE0080,0x00000000,0x3FFE0080,0x00000000} }, //+1.984390259f, +0.000000000f, +1.984390259f, +0.000000000f + { .i = {0x3FFEAAE4,0x00000000,0x3FFEAAE4,0x00000000} }, //+1.989590168f, +0.000000000f, +1.989590168f, +0.000000000f + { .i = {0x3FFF5564,0x00000000,0x3FFF5564,0x00000000} }, //+1.994793415f, +0.000000000f, +1.994793415f, +0.000000000f + + /* this value isn't part of the original table but index is sometimes accesed (this seems like a bug in original + * code, o perhaps a byproduct of inexact Ps2 float emulation), return 0 as a previsible result */ + { .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] = { + { .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 + { .i = {0x3F54DB31,0x3F4D9F02,0x3F45E403,0x3F3DAEF9} }, //+0.831469595f, +0.803207517f, +0.773010433f, +0.740951121f + { .i = {0x3F3504F3,0x3F2BEB49,0x3F226799,0x3F187FC0} }, //+0.707106769f, +0.671558917f, +0.634393275f, +0.595699310f + { .i = {0x3F0E39D9,0x3F039C3C,0x3EF15AE7,0x3EDAE881} }, //+0.555570185f, +0.514102697f, +0.471396655f, +0.427555114f + { .i = {0x3EC3EF15,0x3EAC7CD3,0x3E94A030,0x3E78CFC8} }, //+0.382683426f, +0.336889833f, +0.290284634f, +0.242980123f + { .i = {0x3E47C5BC,0x3E164085,0x3DC8BD35,0x3D48FB29} }, //+0.195090234f, +0.146730497f, +0.098017134f, +0.049067650f + { .i = {0x3F3504F3,0x3F7D3AAC,0x3F74FA0B,0x3F676BD8} }, //+0.707106769f, +0.989176512f, +0.956940353f, +0.903989315f + { .i = {0x3F54DB31,0x3F3DAEF9,0x3F226799,0x3F039C3C} }, //+0.831469595f, +0.740951121f, +0.634393275f, +0.514102697f + { .i = {0x3EC3EF15,0x3E78CFC8,0x3DC8BD35,0xBD48FB41} }, //+0.382683426f, +0.242980123f, +0.098017134f, -0.049067739f + { .i = {0xBE47C5C2,0xBEAC7CD6,0xBEF15AED,0xBF187FC1} }, //-0.195090324f, -0.336889923f, -0.471396834f, -0.595699370f + { .i = {0xBF3504F3,0xBF4D9F04,0xBF61C599,0xBF710909} }, //-0.707106769f, -0.803207636f, -0.881921351f, -0.941544116f + { .i = {0xBF7B14BF,0xBF7FB10F,0xBF7EC46D,0xBF7853F8} }, //-0.980785310f, -0.998795450f, -0.995184720f, -0.970031261f + { .i = {0xBF6C835E,0xBF5B9419,0xBF45E402,0xBF2BEB49} }, //-0.923879504f, -0.857728541f, -0.773010373f, -0.671558917f + { .i = {0xBF0E39D6,0xBEDAE87B,0xBE94A02D,0xBE16407F} }, //-0.555570006f, -0.427554935f, -0.290284544f, -0.146730408f + { .i = {0x3F3504F3,0x3F7853F8,0x3F61C597,0x3F3DAEF9} }, //+0.707106769f, +0.970031261f, +0.881921232f, +0.740951121f + { .i = {0x3F0E39D9,0x3EAC7CD3,0x3DC8BD35,0xBE16408A} }, //+0.555570185f, +0.336889833f, +0.098017134f, -0.146730572f + { .i = {0xBEC3EF18,0xBF187FC1,0xBF45E404,0xBF676BD8} }, //-0.382683516f, -0.595699370f, -0.773010492f, -0.903989315f + { .i = {0xBF7B14BF,0xBF7FB10F,0xBF74FA0A,0xBF5B9419} }, //-0.980785310f, -0.998795450f, -0.956940293f, -0.857728541f + { .i = {0xBF3504F1,0xBF039C3E,0xBE94A02D,0xBD48FAD2} }, //-0.707106650f, -0.514102817f, -0.290284544f, -0.049067326f + { .i = {0x3E47C5C8,0x3EDAE88A,0x3F22679A,0x3F4D9F05} }, //+0.195090413f, +0.427555382f, +0.634393334f, +0.803207695f + { .i = {0x3F6C835F,0x3F7D3AAD,0x3F7EC46D,0x3F710908} }, //+0.923879564f, +0.989176571f, +0.995184720f, +0.941544056f + { .i = {0x3F54DB31,0x3F2BEB49,0x3EF15AE7,0x3E78CFC8} }, //+0.831469595f, +0.671558917f, +0.471396655f, +0.242980123f + { .i = {0x3F3504F3,0x3F710908,0x3F45E403,0x3F039C3C} }, //+0.707106769f, +0.941544056f, +0.773010433f, +0.514102697f + { .i = {0x3E47C5BC,0xBE16408A,0xBEF15AED,0xBF3DAEFB} }, //+0.195090234f, -0.146730572f, -0.471396834f, -0.740951240f + { .i = {0xBF6C8360,0xBF7FB10F,0xBF74FA0A,0xBF4D9F02} }, //-0.923879623f, -0.998795450f, -0.956940293f, -0.803207517f + { .i = {0xBF0E39D6,0xBE78CFBA,0x3DC8BD5D,0x3EDAE88A} }, //-0.555570006f, -0.242979914f, +0.098017432f, +0.427555382f + { .i = {0x3F3504F7,0x3F676BDA,0x3F7EC46E,0x3F7853F8} }, //+0.707107008f, +0.903989434f, +0.995184779f, +0.970031261f + { .i = {0x3F54DB31,0x3F187FC0,0x3E94A030,0xBD48FB41} }, //+0.831469595f, +0.595699310f, +0.290284634f, -0.049067739f + { .i = {0xBEC3EF18,0xBF2BEB4B,0xBF61C599,0xBF7D3AAC} }, //-0.382683516f, -0.671559036f, -0.881921351f, -0.989176512f + { .i = {0xBF7B14BE,0xBF5B9419,0xBF22679A,0xBEAC7CD4} }, //-0.980785251f, -0.857728541f, -0.634393334f, -0.336889863f + { .i = {0x3F3504F3,0x3F676BD8,0x3F226799,0x3E78CFC8} }, //+0.707106769f, +0.903989315f, +0.634393275f, +0.242980123f + { .i = {0xBE47C5C2,0xBF187FC1,0xBF61C599,0xBF7FB10F} }, //-0.195090324f, -0.595699370f, -0.881921351f, -0.998795450f + { .i = {0xBF6C835E,0xBF2BEB49,0xBE94A02D,0x3E164080} }, //-0.923879504f, -0.671558917f, -0.290284544f, +0.146730423f + { .i = {0x3F0E39DD,0x3F5B941B,0x3F7EC46E,0x3F710908} }, //+0.555570424f, +0.857728660f, +0.995184779f, +0.941544056f + { .i = {0x3F3504F3,0x3EAC7CD3,0xBDC8BD41,0xBF039C3D} }, //+0.707106769f, +0.336889833f, -0.098017223f, -0.514102757f + { .i = {0xBF54DB32,0xBF7D3AAC,0xBF74FA0A,0xBF3DAEF9} }, //-0.831469655f, -0.989176512f, -0.956940293f, -0.740951121f + { .i = {0xBEC3EF0B,0x3D48FB58,0x3EF15AE9,0x3F4D9F05} }, //-0.382683128f, +0.049067825f, +0.471396714f, +0.803207695f + { .i = {0x3F7B14BF,0x3F7853F8,0x3F45E403,0x3EDAE881} }, //+0.980785310f, +0.970031261f, +0.773010433f, +0.427555114f + { .i = {0x3F3504F3,0x3F5B941A,0x3EF15AE7,0xBD48FB41} }, //+0.707106769f, +0.857728601f, +0.471396655f, -0.049067739f + { .i = {0xBF0E39DC,0xBF676BD8,0xBF7EC46D,0xBF4D9F02} }, //-0.555570364f, -0.903989315f, -0.995184720f, -0.803207517f + { .i = {0xBEC3EF0B,0x3E164080,0x3F22679A,0x3F710909} }, //-0.382683128f, +0.146730423f, +0.634393334f, +0.941544116f + { .i = {0x3F7B14BE,0x3F3DAEF9,0x3E94A030,0xBE78CFCD} }, //+0.980785251f, +0.740951121f, +0.290284634f, -0.242980197f + { .i = {0xBF3504F3,0xBF7853F8,0xBF74FA0A,0xBF2BEB49} }, //-0.707106769f, -0.970031261f, -0.956940293f, -0.671558917f + { .i = {0xBE47C5C6,0x3EAC7CD5,0x3F45E405,0x3F7D3AAD} }, //-0.195090383f, +0.336889893f, +0.773010552f, +0.989176571f + { .i = {0x3F6C835E,0x3F187FC0,0x3DC8BD35,0xBEDAE880} }, //+0.923879504f, +0.595699310f, +0.098017134f, -0.427555084f + { .i = {0xBF54DB32,0xBF7FB10F,0xBF61C597,0xBF039C3E} }, //-0.831469655f, -0.998795450f, -0.881921232f, -0.514102817f + { .i = {0x3F3504F3,0x3F4D9F02,0x3E94A030,0xBEAC7CD6} }, //+0.707106769f, +0.803207517f, +0.290284634f, -0.336889923f + { .i = {0xBF54DB32,0xBF7FB10F,0xBF45E402,0xBE78CFBA} }, //-0.831469655f, -0.998795450f, -0.773010373f, -0.242979914f + { .i = {0x3EC3EF1B,0x3F5B941B,0x3F7EC46D,0x3F3DAEF9} }, //+0.382683605f, +0.857728660f, +0.995184720f, +0.740951121f + { .i = {0x3E47C5BC,0xBEDAE880,0xBF61C599,0xBF7D3AAC} }, //+0.195090234f, -0.427555084f, -0.881921351f, -0.989176512f + { .i = {0xBF3504F1,0xBE16407F,0x3EF15AE9,0x3F676BDA} }, //-0.707106650f, -0.146730408f, +0.471396714f, +0.903989434f + { .i = {0x3F7B14BE,0x3F2BEB49,0x3DC8BD35,0xBF039C3D} }, //+0.980785251f, +0.671558917f, +0.098017134f, -0.514102757f + { .i = {0xBF6C8360,0xBF7853F8,0xBF22679A,0xBD48FAD2} }, //-0.923879623f, -0.970031261f, -0.634393334f, -0.049067326f + { .i = {0x3F0E39DD,0x3F710909,0x3F74FA0B,0x3F187FC0} }, //+0.555570424f, +0.941544116f, +0.956940353f, +0.595699310f + { .i = {0x3F3504F3,0x3F3DAEF9,0x3DC8BD35,0xBF187FC1} }, //+0.707106769f, +0.740951121f, +0.098017134f, -0.595699370f + { .i = {0xBF7B14BF,0xBF5B9419,0xBE94A02D,0x3EDAE88A} }, //-0.980785310f, -0.857728541f, -0.290284544f, +0.427555382f + { .i = {0x3F6C835F,0x3F710908,0x3EF15AE7,0xBE78CFCD} }, //+0.923879564f, +0.941544056f, +0.471396655f, -0.242980197f + { .i = {0xBF54DB32,0xBF7D3AAC,0xBF22679A,0x3D48FB58} }, //-0.831469655f, -0.989176512f, -0.634393334f, +0.049067825f + { .i = {0x3F3504F7,0x3F7FB10F,0x3F45E403,0x3E164085} }, //+0.707107008f, +0.998795450f, +0.773010433f, +0.146730497f + { .i = {0xBF0E39DC,0xBF7853F8,0xBF61C597,0xBEAC7CD4} }, //-0.555570364f, -0.970031261f, -0.881921232f, -0.336889863f + { .i = {0x3EC3EF1B,0x3F676BDA,0x3F74FA0B,0x3F039C3C} }, //+0.382683605f, +0.903989434f, +0.956940353f, +0.514102697f + { .i = {0xBE47C5C2,0xBF4D9F04,0xBF7EC46D,0xBF2BEB49} }, //-0.195090324f, -0.803207636f, -0.995184720f, -0.671558917f + { .i = {0x3F3504F3,0x3F2BEB49,0xBDC8BD41,0xBF4D9F04} }, //+0.707106769f, +0.671558917f, -0.098017223f, -0.803207636f + { .i = {0xBF7B14BE,0xBF039C3E,0x3E94A03D,0x3F676BDA} }, //-0.980785251f, -0.514102817f, +0.290285021f, +0.903989434f + { .i = {0x3F6C835E,0x3EAC7CD3,0xBEF15AED,0xBF7853F8} }, //+0.923879504f, +0.336889833f, -0.471396834f, -0.970031261f + { .i = {0xBF54DB30,0xBE16407F,0x3F22679A,0x3F7FB10F} }, //-0.831469536f, -0.146730408f, +0.634393334f, +0.998795450f + { .i = {0x3F3504F3,0xBD48FB41,0xBF45E404,0xBF7D3AAC} }, //+0.707106769f, -0.049067739f, -0.773010492f, -0.989176512f + { .i = {0xBF0E39D6,0x3E78CFDB,0x3F61C599,0x3F710908} }, //-0.555570006f, +0.242980406f, +0.881921351f, +0.941544056f + { .i = {0x3EC3EF15,0xBEDAE880,0xBF74FA0B,0xBF5B9419} }, //+0.382683426f, -0.427555084f, -0.956940353f, -0.857728541f + { .i = {0xBE47C5C6,0x3F187FBF,0x3F7EC46E,0x3F3DAEF9} }, //-0.195090383f, +0.595699251f, +0.995184779f, +0.740951121f + { .i = {0x3F3504F3,0x3F187FC0,0xBE94A033,0xBF710909} }, //+0.707106769f, +0.595699310f, -0.290284723f, -0.941544116f + { .i = {0xBF54DB30,0xBD48FAD2,0x3F45E405,0x3F7853F8} }, //-0.831469536f, -0.049067326f, +0.773010552f, +0.970031261f + { .i = {0x3EC3EF15,0xBF039C3D,0xBF7EC46D,0xBF2BEB49} }, //+0.382683426f, -0.514102757f, -0.995184720f, -0.671558917f + { .i = {0x3E47C5C8,0x3F676BDA,0x3F61C597,0x3E164085} }, //+0.195090413f, +0.903989434f, +0.881921232f, +0.146730497f + { .i = {0xBF3504F3,0xBF7D3AAC,0xBEF15AE8,0x3EDAE88A} }, //-0.707106769f, -0.989176512f, -0.471396685f, +0.427555382f + { .i = {0x3F7B14BF,0x3F3DAEF9,0xBDC8BD41,0xBF5B941A} }, //+0.980785310f, +0.740951121f, -0.098017223f, -0.857728601f + { .i = {0xBF6C835E,0xBE78CFBA,0x3F22679A,0x3F7FB10F} }, //-0.923879504f, -0.242979914f, +0.634393334f, +0.998795450f + { .i = {0x3F0E39D9,0xBEAC7CD6,0xBF74FA0B,0xBF4D9F02} }, //+0.555570185f, -0.336889923f, -0.956940353f, -0.803207517f + { .i = {0x3F3504F3,0x3F039C3C,0xBEF15AED,0xBF7FB10F} }, //+0.707106769f, +0.514102697f, -0.471396834f, -0.998795450f + { .i = {0xBF0E39D6,0x3EDAE88A,0x3F7EC46E,0x3F187FC0} }, //-0.555570006f, +0.427555382f, +0.995184779f, +0.595699310f + { .i = {0xBEC3EF18,0xBF7D3AAC,0xBF22679A,0x3EAC7CD5} }, //-0.382683516f, -0.989176512f, -0.634393334f, +0.336889893f + { .i = {0x3F7B14BF,0x3F2BEB49,0xBE94A033,0xBF7853F8} }, //+0.980785310f, +0.671558917f, -0.290284723f, -0.970031261f + { .i = {0xBF3504F1,0x3E78CFDB,0x3F74FA0C,0x3F3DAEF9} }, //-0.707106650f, +0.242980406f, +0.956940413f, +0.740951121f + { .i = {0xBE47C5C2,0xBF710909,0xBF45E402,0x3E164080} }, //-0.195090324f, -0.941544116f, -0.773010373f, +0.146730423f + { .i = {0x3F6C835F,0x3F4D9F02,0xBDC8BD41,0xBF676BD8} }, //+0.923879564f, +0.803207517f, -0.098017223f, -0.903989315f + { .i = {0xBF54DB30,0x3D48FB58,0x3F61C599,0x3F5B941A} }, //-0.831469536f, +0.049067825f, +0.881921351f, +0.857728601f + { .i = {0x3F3504F3,0x3EDAE881,0xBF226799,0xBF7853F8} }, //+0.707106769f, +0.427555114f, -0.634393275f, -0.970031261f + { .i = {0xBE47C5C6,0x3F4D9F05,0x3F61C597,0xBD48FB41} }, //-0.195090383f, +0.803207695f, +0.881921232f, -0.049067739f + { .i = {0xBF6C8360,0xBF3DAEF9,0x3E94A03D,0x3F7D3AAD} }, //-0.923879623f, -0.740951121f, +0.290285021f, +0.989176571f + { .i = {0x3F0E39D9,0xBF039C3D,0xBF7EC46D,0xBEAC7CD4} }, //+0.555570185f, -0.514102757f, -0.995184720f, -0.336889863f + { .i = {0x3F3504F7,0x3F710908,0x3DC8BD35,0xBF5B941A} }, //+0.707107008f, +0.941544056f, +0.098017134f, -0.857728601f + { .i = {0xBF54DB30,0x3E164080,0x3F74FA0C,0x3F2BEB49} }, //-0.831469536f, +0.146730423f, +0.956940413f, +0.671558917f + { .i = {0xBEC3EF18,0xBF7FB10F,0xBEF15AE8,0x3F187FBF} }, //-0.382683516f, -0.998795450f, -0.471396685f, +0.595699251f + { .i = {0x3F7B14BE,0x3E78CFC8,0xBF45E404,0xBF676BD7} }, //+0.980785251f, +0.242980123f, -0.773010492f, -0.903989255f + { .i = {0x3F3504F3,0x3EAC7CD3,0xBF45E404,0xBF5B9419} }, //+0.707106769f, +0.336889833f, -0.773010492f, -0.857728541f + { .i = {0x3E47C5C8,0x3F7D3AAD,0x3EF15AE7,0xBF2BEB4B} }, //+0.195090413f, +0.989176571f, +0.471396655f, -0.671559036f + { .i = {0xBF6C835E,0x3D48FB58,0x3F74FA0C,0x3F187FC0} }, //-0.923879504f, +0.049067825f, +0.956940413f, +0.595699310f + { .i = {0xBF0E39DC,0xBF7853F8,0xBDC8BD1A,0x3F676BDA} }, //-0.555570364f, -0.970031261f, -0.098016933f, +0.903989434f + { .i = {0x3F3504F3,0xBEDAE880,0xBF7EC46D,0xBE78CFBA} }, //+0.707106769f, -0.427555084f, -0.995184720f, -0.242979914f + { .i = {0x3F54DB31,0x3F4D9F02,0xBE94A033,0xBF7FB10F} }, //+0.831469595f, +0.803207517f, -0.290284723f, -0.998795450f + { .i = {0xBEC3EF0B,0x3F3DAEF9,0x3F61C597,0xBE16408A} }, //-0.382683128f, +0.740951121f, +0.881921232f, -0.146730572f + { .i = {0xBF7B14BF,0xBF039C3E,0x3F22679A,0x3F710908} }, //-0.980785310f, -0.514102817f, +0.634393334f, +0.941544056f + { .i = {0x3F3504F3,0x3E78CFC8,0xBF61C599,0xBF2BEB49} }, //+0.707106769f, +0.242980123f, -0.881921351f, -0.671558917f + { .i = {0x3F0E39DD,0x3F710908,0xBDC8BD41,0xBF7D3AAC} }, //+0.555570424f, +0.941544056f, -0.098017223f, -0.989176512f + { .i = {0xBEC3EF0B,0x3F4D9F05,0x3F45E403,0xBEDAE880} }, //-0.382683128f, +0.803207695f, +0.773010433f, -0.427555084f + { .i = {0xBF7B14BE,0xBD48FAD2,0x3F74FA0C,0x3F039C3C} }, //-0.980785251f, -0.049067326f, +0.956940413f, +0.514102697f + { .i = {0xBF3504F3,0xBF5B9419,0x3E94A03D,0x3F7FB10F} }, //-0.707106769f, -0.857728541f, +0.290285021f, +0.998795450f + { .i = {0x3E47C5BC,0xBF676BD8,0xBF22679A,0x3F187FBF} }, //+0.195090234f, -0.903989315f, -0.634393334f, +0.595699251f + { .i = {0x3F6C835E,0xBE16408A,0xBF7EC46D,0xBEAC7CD4} }, //+0.923879504f, -0.146730572f, -0.995184720f, -0.336889863f + { .i = {0x3F54DB31,0x3F3DAEF9,0xBEF15AED,0xBF7853F8} }, //+0.831469595f, +0.740951121f, -0.471396834f, -0.970031261f + { .i = {0x3F3504F3,0x3E164085,0xBF74FA0B,0xBEDAE87B} }, //+0.707106769f, +0.146730497f, -0.956940353f, -0.427554935f + { .i = {0x3F54DB31,0x3F2BEB49,0xBF226799,0xBF5B9419} }, //+0.831469595f, +0.671558917f, -0.634393275f, -0.857728541f + { .i = {0x3EC3EF1B,0x3F7853F8,0xBDC8BD41,0xBF7FB10F} }, //+0.382683605f, +0.970031261f, -0.098017223f, -0.998795450f + { .i = {0xBE47C5C6,0x3F710909,0x3EF15AE7,0xBF4D9F04} }, //-0.195090383f, +0.941544116f, +0.471396655f, -0.803207636f + { .i = {0xBF3504F1,0x3F187FBF,0x3F61C597,0xBEAC7CD6} }, //-0.707106650f, +0.595699251f, +0.881921232f, -0.336889923f + { .i = {0xBF7B14BE,0x3D48FB58,0x3F7EC46E,0x3E78CFC8} }, //-0.980785251f, +0.049067825f, +0.995184779f, +0.242980123f + { .i = {0xBF6C8360,0xBF039C3E,0x3F45E405,0x3F3DAEF9} }, //-0.923879623f, -0.514102817f, +0.773010552f, +0.740951121f + { .i = {0xBF0E39DC,0xBF676BD7,0x3E94A03D,0x3F7D3AAC} }, //-0.555570364f, -0.903989255f, +0.290285021f, +0.989176512f + { .i = {0x3F3504F3,0x3D48FB29,0xBF7EC46D,0xBE16407F} }, //+0.707106769f, +0.049067650f, -0.995184720f, -0.146730408f + { .i = {0x3F7B14BF,0x3E78CFC8,0xBF74FA0B,0xBEAC7CD4} }, //+0.980785310f, +0.242980123f, -0.956940353f, -0.336889863f + { .i = {0x3F6C835F,0x3EDAE881,0xBF61C599,0xBF039C3E} }, //+0.923879564f, +0.427555114f, -0.881921351f, -0.514102817f + { .i = {0x3F54DB31,0x3F187FC0,0xBF45E404,0xBF2BEB49} }, //+0.831469595f, +0.595699310f, -0.773010492f, -0.671558917f + { .i = {0x3F3504F7,0x3F3DAEF9,0xBF226799,0xBF4D9F02} }, //+0.707107008f, +0.740951121f, -0.634393275f, -0.803207517f + { .i = {0x3F0E39DD,0x3F5B941A,0xBEF15AED,0xBF676BD7} }, //+0.555570424f, +0.857728601f, -0.471396834f, -0.903989255f + { .i = {0x3EC3EF1B,0x3F710908,0xBE94A033,0xBF7853F8} }, //+0.382683605f, +0.941544056f, -0.290284723f, -0.970031261f + { .i = {0x3E47C5C8,0x3F7D3AAC,0xBDC8BD41,0xBF7FB10F} }, //+0.195090413f, +0.989176512f, -0.098017223f, -0.998795450f + { .i = {0x3F3504F3,0xBD48FB41,0xBF7EC46D,0x3E164080} }, //+0.707106769f, -0.049067739f, -0.995184720f, +0.146730423f + { .i = {0x3F7B14BE,0xBE78CFCD,0xBF74FA0A,0x3EAC7CD5} }, //+0.980785251f, -0.242980197f, -0.956940293f, +0.336889893f + { .i = {0x3F6C835E,0xBEDAE880,0xBF61C597,0x3F039C3E} }, //+0.923879504f, -0.427555084f, -0.881921232f, +0.514102817f + { .i = {0x3F54DB31,0xBF187FC1,0xBF45E402,0x3F2BEB4C} }, //+0.831469595f, -0.595699370f, -0.773010373f, +0.671559095f + { .i = {0x3F3504F3,0xBF3DAEFB,0xBF22679A,0x3F4D9F05} }, //+0.707106769f, -0.740951240f, -0.634393334f, +0.803207695f + { .i = {0x3F0E39D9,0xBF5B941A,0xBEF15AE8,0x3F676BDA} }, //+0.555570185f, -0.857728601f, -0.471396685f, +0.903989434f + { .i = {0x3EC3EF15,0xBF710909,0xBE94A02D,0x3F7853F8} }, //+0.382683426f, -0.941544116f, -0.290284544f, +0.970031261f + { .i = {0x3E47C5BC,0xBF7D3AAC,0xBDC8BD1A,0x3F7FB10F} }, //+0.195090234f, -0.989176512f, -0.098016933f, +0.998795450f + { .i = {0x3F3504F3,0xBE16408A,0xBF74FA0A,0x3EDAE88A} }, //+0.707106769f, -0.146730572f, -0.956940293f, +0.427555382f + { .i = {0x3F54DB31,0xBF2BEB4B,0xBF22679A,0x3F5B941B} }, //+0.831469595f, -0.671559036f, -0.634393334f, +0.857728660f + { .i = {0x3EC3EF15,0xBF7853F8,0xBDC8BD1A,0x3F7FB10F} }, //+0.382683426f, -0.970031261f, -0.098016933f, +0.998795450f + { .i = {0xBE47C5C2,0xBF710908,0x3EF15AE9,0x3F4D9F02} }, //-0.195090324f, -0.941544056f, +0.471396714f, +0.803207517f + { .i = {0xBF3504F3,0xBF187FBE,0x3F61C599,0x3EAC7CD3} }, //-0.707106769f, -0.595699191f, +0.881921351f, +0.336889833f + { .i = {0xBF7B14BF,0xBD48FAD2,0x3F7EC46D,0xBE78CFCD} }, //-0.980785310f, -0.049067326f, +0.995184720f, -0.242980197f + { .i = {0xBF6C835E,0x3F039C3E,0x3F45E403,0xBF3DAEFB} }, //-0.923879504f, +0.514102817f, +0.773010433f, -0.740951240f + { .i = {0xBF0E39D6,0x3F676BDA,0x3E94A030,0xBF7D3AAC} }, //-0.555570006f, +0.903989434f, +0.290284634f, -0.989176512f + { .i = {0x3F3504F3,0xBE78CFCD,0xBF61C597,0x3F2BEB4C} }, //+0.707106769f, -0.242980197f, -0.881921232f, +0.671559095f + { .i = {0x3F0E39D9,0xBF710909,0xBDC8BD1A,0x3F7D3AAC} }, //+0.555570185f, -0.941544116f, -0.098016933f, +0.989176512f + { .i = {0xBEC3EF18,0xBF4D9F02,0x3F45E405,0x3EDAE881} }, //-0.382683516f, -0.803207517f, +0.773010552f, +0.427555114f + { .i = {0xBF7B14BF,0x3D48FB58,0x3F74FA0B,0xBF039C3D} }, //-0.980785310f, +0.049067825f, +0.956940353f, -0.514102757f + { .i = {0xBF3504F1,0x3F5B941B,0x3E94A030,0xBF7FB10F} }, //-0.707106650f, +0.857728660f, +0.290284634f, -0.998795450f + { .i = {0x3E47C5C8,0x3F676BD8,0xBF226799,0xBF187FBE} }, //+0.195090413f, +0.903989315f, -0.634393275f, -0.595699191f + { .i = {0x3F6C835F,0x3E164085,0xBF7EC46D,0x3EAC7CD5} }, //+0.923879564f, +0.146730497f, -0.995184720f, +0.336889893f + { .i = {0x3F54DB31,0xBF3DAEFB,0xBEF15AE8,0x3F7853F8} }, //+0.831469595f, -0.740951240f, -0.471396685f, +0.970031261f + { .i = {0x3F3504F3,0xBEAC7CD6,0xBF45E402,0x3F5B941B} }, //+0.707106769f, -0.336889923f, -0.773010373f, +0.857728660f + { .i = {0x3E47C5BC,0xBF7D3AAC,0x3EF15AE9,0x3F2BEB49} }, //+0.195090234f, -0.989176512f, +0.471396714f, +0.671558917f + { .i = {0xBF6C8360,0xBD48FAD2,0x3F74FA0B,0xBF187FC1} }, //-0.923879623f, -0.049067326f, +0.956940353f, -0.595699370f + { .i = {0xBF0E39D6,0x3F7853F8,0xBDC8BD41,0xBF676BD7} }, //-0.555570006f, +0.970031261f, -0.098017223f, -0.903989255f + { .i = {0x3F3504F7,0x3EDAE881,0xBF7EC46D,0x3E78CFDB} }, //+0.707107008f, +0.427555114f, -0.995184720f, +0.242980406f + { .i = {0x3F54DB31,0xBF4D9F04,0xBE94A02D,0x3F7FB10F} }, //+0.831469595f, -0.803207636f, -0.290284544f, +0.998795450f + { .i = {0xBEC3EF18,0xBF3DAEF9,0x3F61C599,0x3E164085} }, //-0.382683516f, -0.740951121f, +0.881921351f, +0.146730497f + { .i = {0xBF7B14BE,0x3F039C3E,0x3F226799,0xBF710909} }, //-0.980785251f, +0.514102817f, +0.634393275f, -0.941544116f + { .i = {0x3F3504F3,0xBEDAE880,0xBF22679A,0x3F7853F8} }, //+0.707106769f, -0.427555084f, -0.634393334f, +0.970031261f + { .i = {0xBE47C5C2,0xBF4D9F02,0x3F61C599,0x3D48FB29} }, //-0.195090324f, -0.803207517f, +0.881921351f, +0.049067650f + { .i = {0xBF6C835E,0x3F3DAEF9,0x3E94A030,0xBF7D3AAC} }, //-0.923879504f, +0.740951121f, +0.290284634f, -0.989176512f + { .i = {0x3F0E39DD,0x3F039C3C,0xBF7EC46D,0x3EAC7CD5} }, //+0.555570424f, +0.514102697f, -0.995184720f, +0.336889893f + { .i = {0x3F3504F3,0xBF710909,0x3DC8BD5D,0x3F5B941A} }, //+0.707106769f, -0.941544116f, +0.098017432f, +0.857728601f + { .i = {0xBF54DB32,0xBE16407F,0x3F74FA0B,0xBF2BEB4B} }, //-0.831469655f, -0.146730408f, +0.956940353f, -0.671559036f + { .i = {0xBEC3EF0B,0x3F7FB10F,0xBEF15AED,0xBF187FBE} }, //-0.382683128f, +0.998795450f, -0.471396834f, -0.595699191f + { .i = {0x3F7B14BF,0xBE78CFCD,0xBF45E402,0x3F676BDA} }, //+0.980785310f, -0.242980197f, -0.773010373f, +0.903989434f + { .i = {0x3F3504F3,0xBF039C3D,0xBEF15AE8,0x3F7FB10F} }, //+0.707106769f, -0.514102757f, -0.471396685f, +0.998795450f + { .i = {0xBF0E39DC,0xBEDAE87B,0x3F7EC46D,0xBF187FC1} }, //-0.555570364f, -0.427554935f, +0.995184720f, -0.595699370f + { .i = {0xBEC3EF0B,0x3F7D3AAC,0xBF226799,0xBEAC7CD4} }, //-0.382683128f, +0.989176512f, -0.634393275f, -0.336889863f + { .i = {0x3F7B14BE,0xBF2BEB4B,0xBE94A02D,0x3F7853F8} }, //+0.980785251f, -0.671559036f, -0.290284544f, +0.970031261f + { .i = {0xBF3504F3,0xBE78CFBA,0x3F74FA0B,0xBF3DAEFB} }, //-0.707106769f, -0.242979914f, +0.956940353f, -0.740951240f + { .i = {0xBE47C5C6,0x3F710908,0xBF45E404,0xBE16407F} }, //-0.195090383f, +0.941544056f, -0.773010492f, -0.146730408f + { .i = {0x3F6C835E,0xBF4D9F04,0xBDC8BD1A,0x3F676BD8} }, //+0.923879504f, -0.803207636f, -0.098016933f, +0.903989315f + { .i = {0xBF54DB32,0xBD48FAD2,0x3F61C597,0xBF5B941A} }, //-0.831469655f, -0.049067326f, +0.881921232f, -0.857728601f + { .i = {0x3F3504F3,0xBF187FC1,0xBE94A02D,0x3F710908} }, //+0.707106769f, -0.595699370f, -0.290284544f, +0.941544056f + { .i = {0xBF54DB32,0x3D48FB58,0x3F45E403,0xBF7853F8} }, //-0.831469655f, +0.049067825f, +0.773010433f, -0.970031261f + { .i = {0x3EC3EF1B,0x3F039C3C,0xBF7EC46D,0x3F2BEB4C} }, //+0.382683605f, +0.514102697f, -0.995184720f, +0.671559095f + { .i = {0x3E47C5BC,0xBF676BD7,0x3F61C599,0xBE16408A} }, //+0.195090234f, -0.903989255f, +0.881921351f, -0.146730572f + { .i = {0xBF3504F1,0x3F7D3AAD,0xBEF15AED,0xBEDAE87B} }, //-0.707106650f, +0.989176571f, -0.471396834f, -0.427554935f + { .i = {0x3F7B14BE,0xBF3DAEFB,0xBDC8BD1A,0x3F5B941A} }, //+0.980785251f, -0.740951240f, -0.098016933f, +0.857728601f + { .i = {0xBF6C8360,0x3E78CFDB,0x3F226799,0xBF7FB10F} }, //-0.923879623f, +0.242980406f, +0.634393275f, -0.998795450f + { .i = {0x3F0E39DD,0x3EAC7CD3,0xBF74FA0A,0x3F4D9F05} }, //+0.555570424f, +0.336889833f, -0.956940293f, +0.803207695f + { .i = {0x3F3504F3,0xBF2BEB4B,0xBDC8BD1A,0x3F4D9F02} }, //+0.707106769f, -0.671559036f, -0.098016933f, +0.803207517f + { .i = {0xBF7B14BF,0x3F039C3E,0x3E94A030,0xBF676BD7} }, //-0.980785310f, +0.514102817f, +0.290284634f, -0.903989255f + { .i = {0x3F6C835F,0xBEAC7CD6,0xBEF15AE8,0x3F7853F8} }, //+0.923879564f, -0.336889923f, -0.471396685f, +0.970031261f + { .i = {0xBF54DB32,0x3E164080,0x3F226799,0xBF7FB10F} }, //-0.831469655f, +0.146730423f, +0.634393275f, -0.998795450f + { .i = {0x3F3504F7,0x3D48FB29,0xBF45E402,0x3F7D3AAD} }, //+0.707107008f, +0.049067650f, -0.773010373f, +0.989176571f + { .i = {0xBF0E39DC,0xBE78CFBA,0x3F61C597,0xBF710909} }, //-0.555570364f, -0.242979914f, +0.881921232f, -0.941544116f + { .i = {0x3EC3EF1B,0x3EDAE881,0xBF74FA0A,0x3F5B941B} }, //+0.382683605f, +0.427555114f, -0.956940293f, +0.857728660f + { .i = {0xBE47C5C2,0xBF187FBE,0x3F7EC46D,0xBF3DAEFB} }, //-0.195090324f, -0.595699191f, +0.995184720f, -0.740951240f + { .i = {0x3F3504F3,0xBF3DAEFB,0x3DC8BD5D,0x3F187FC0} }, //+0.707106769f, -0.740951240f, +0.098017432f, +0.595699310f + { .i = {0xBF7B14BE,0x3F5B941B,0xBE94A033,0xBEDAE87B} }, //-0.980785251f, +0.857728660f, -0.290284723f, -0.427554935f + { .i = {0x3F6C835E,0xBF710909,0x3EF15AE9,0x3E78CFC8} }, //+0.923879504f, -0.941544116f, +0.471396714f, +0.242980123f + { .i = {0xBF54DB30,0x3F7D3AAD,0xBF226799,0xBD48FAD2} }, //-0.831469536f, +0.989176571f, -0.634393275f, -0.049067326f + { .i = {0x3F3504F3,0xBF7FB10F,0x3F45E405,0xBE16408A} }, //+0.707106769f, -0.998795450f, +0.773010552f, -0.146730572f + { .i = {0xBF0E39D6,0x3F7853F8,0xBF61C599,0x3EAC7CD5} }, //-0.555570006f, +0.970031261f, -0.881921351f, +0.336889893f + { .i = {0x3EC3EF15,0xBF676BD7,0x3F74FA0C,0xBF039C3D} }, //+0.382683426f, -0.903989255f, +0.956940413f, -0.514102757f + { .i = {0xBE47C5C6,0x3F4D9F02,0xBF7EC46D,0x3F2BEB4C} }, //-0.195090383f, +0.803207517f, -0.995184720f, +0.671559095f + { .i = {0x3F3504F3,0xBF4D9F04,0x3E94A03D,0x3EAC7CD3} }, //+0.707106769f, -0.803207636f, +0.290285021f, +0.336889833f + { .i = {0xBF54DB30,0x3F7FB10F,0xBF45E404,0x3E78CFDB} }, //-0.831469536f, +0.998795450f, -0.773010492f, +0.242980406f + { .i = {0x3EC3EF15,0xBF5B9419,0x3F7EC46E,0xBF3DAEFB} }, //+0.382683426f, -0.857728541f, +0.995184779f, -0.740951240f + { .i = {0x3E47C5C8,0x3EDAE881,0xBF61C597,0x3F7D3AAD} }, //+0.195090413f, +0.427555114f, -0.881921232f, +0.989176571f + { .i = {0xBF3504F3,0x3E164080,0x3EF15AE7,0xBF676BD7} }, //-0.707106769f, +0.146730423f, +0.471396655f, -0.903989255f + { .i = {0x3F7B14BF,0xBF2BEB4B,0x3DC8BD5D,0x3F039C3C} }, //+0.980785310f, -0.671559036f, +0.098017432f, +0.514102697f + { .i = {0xBF6C835E,0x3F7853F8,0xBF226799,0x3D48FB58} }, //-0.923879504f, +0.970031261f, -0.634393275f, +0.049067825f + { .i = {0x3F0E39D9,0xBF710908,0x3F74FA0C,0xBF187FC1} }, //+0.555570185f, -0.941544056f, +0.956940413f, -0.595699370f + { .i = {0x3F3504F3,0xBF5B941A,0x3EF15AE9,0x3D48FB29} }, //+0.707106769f, -0.857728601f, +0.471396714f, +0.049067650f + { .i = {0xBF0E39D6,0x3F676BD8,0xBF7EC46D,0x3F4D9F05} }, //-0.555570006f, +0.903989315f, -0.995184720f, +0.803207695f + { .i = {0xBEC3EF18,0xBE16407F,0x3F226799,0xBF710908} }, //-0.382683516f, -0.146730408f, +0.634393275f, -0.941544056f + { .i = {0x3F7B14BF,0xBF3DAEFB,0x3E94A03D,0x3E78CFC8} }, //+0.980785310f, -0.740951240f, +0.290285021f, +0.242980123f + { .i = {0xBF3504F1,0x3F7853F8,0xBF74FA0B,0x3F2BEB4C} }, //-0.707106650f, +0.970031261f, -0.956940353f, +0.671559095f + { .i = {0xBE47C5C2,0xBEAC7CD4,0x3F45E403,0xBF7D3AAC} }, //-0.195090324f, -0.336889863f, +0.773010433f, -0.989176512f + { .i = {0x3F6C835F,0xBF187FC1,0x3DC8BD5D,0x3EDAE881} }, //+0.923879564f, -0.595699370f, +0.098017432f, +0.427555114f + { .i = {0xBF54DB30,0x3F7FB10F,0xBF61C599,0x3F039C3E} }, //-0.831469536f, +0.998795450f, -0.881921351f, +0.514102817f + { .i = {0x3F3504F3,0xBF676BD8,0x3F22679A,0xBE78CFCD} }, //+0.707106769f, -0.903989315f, +0.634393334f, -0.242980197f + { .i = {0xBE47C5C6,0x3F187FC0,0xBF61C597,0x3F7FB10F} }, //-0.195090383f, +0.595699310f, -0.881921232f, +0.998795450f + { .i = {0xBF6C8360,0x3F2BEB4C,0xBE94A033,0xBE16407F} }, //-0.923879623f, +0.671559095f, -0.290284723f, -0.146730408f + { .i = {0x3F0E39D9,0xBF5B9419,0x3F7EC46D,0xBF710909} }, //+0.555570185f, -0.857728541f, +0.995184720f, -0.941544116f + { .i = {0x3F3504F7,0xBEAC7CD6,0xBDC8BD1A,0x3F039C3C} }, //+0.707107008f, -0.336889923f, -0.098016933f, +0.514102697f + { .i = {0xBF54DB30,0x3F7D3AAC,0xBF74FA0B,0x3F3DAEF9} }, //-0.831469536f, +0.989176512f, -0.956940353f, +0.740951121f + { .i = {0xBEC3EF18,0xBD48FAD2,0x3EF15AE7,0xBF4D9F02} }, //-0.382683516f, -0.049067326f, +0.471396655f, -0.803207517f + { .i = {0x3F7B14BE,0xBF7853F8,0x3F45E405,0xBEDAE880} }, //+0.980785251f, -0.970031261f, +0.773010552f, -0.427555084f + { .i = {0x3F3504F3,0xBF710909,0x3F45E405,0xBF039C3D} }, //+0.707106769f, -0.941544116f, +0.773010552f, -0.514102757f + { .i = {0x3E47C5C8,0x3E164085,0xBEF15AE8,0x3F3DAEF9} }, //+0.195090413f, +0.146730497f, -0.471396685f, +0.740951121f + { .i = {0xBF6C835E,0x3F7FB10F,0xBF74FA0B,0x3F4D9F05} }, //-0.923879504f, +0.998795450f, -0.956940353f, +0.803207695f + { .i = {0xBF0E39DC,0x3E78CFDB,0x3DC8BD35,0xBEDAE87B} }, //-0.555570364f, +0.242980406f, +0.098017134f, -0.427554935f + { .i = {0x3F3504F3,0xBF676BD7,0x3F7EC46D,0xBF7853F8} }, //+0.707106769f, -0.903989255f, +0.995184720f, -0.970031261f + { .i = {0x3F54DB31,0xBF187FC1,0x3E94A03D,0x3D48FB29} }, //+0.831469595f, -0.595699370f, +0.290285021f, +0.049067650f + { .i = {0xBEC3EF0B,0x3F2BEB49,0xBF61C597,0x3F7D3AAC} }, //-0.382683128f, +0.671558917f, -0.881921232f, +0.989176512f + { .i = {0xBF7B14BF,0x3F5B941B,0xBF226799,0x3EAC7CD5} }, //-0.980785310f, +0.857728660f, -0.634393275f, +0.336889893f + { .i = {0x3F3504F3,0xBF7853F8,0x3F61C599,0xBF3DAEFB} }, //+0.707106769f, -0.970031261f, +0.881921351f, -0.740951240f + { .i = {0x3F0E39DD,0xBEAC7CD6,0x3DC8BD5D,0x3E164085} }, //+0.555570424f, -0.336889923f, +0.098017432f, +0.146730497f + { .i = {0xBEC3EF0B,0x3F187FC0,0xBF45E402,0x3F676BD8} }, //-0.382683128f, +0.595699310f, -0.773010373f, +0.903989315f + { .i = {0xBF7B14BE,0x3F7FB10F,0xBF74FA0B,0x3F5B941B} }, //-0.980785251f, +0.998795450f, -0.956940353f, +0.857728660f + { .i = {0xBF3504F3,0x3F039C3E,0xBE94A033,0x3D48FB58} }, //-0.707106769f, +0.514102817f, -0.290284723f, +0.049067825f + { .i = {0x3E47C5BC,0xBEDAE87B,0x3F226799,0xBF4D9F02} }, //+0.195090234f, -0.427554935f, +0.634393275f, -0.803207517f + { .i = {0x3F6C835E,0xBF7D3AAC,0x3F7EC46E,0xBF710909} }, //+0.923879504f, -0.989176512f, +0.995184779f, -0.941544116f + { .i = {0x3F54DB31,0xBF2BEB4B,0x3EF15AE9,0xBE78CFCD} }, //+0.831469595f, -0.671559036f, +0.471396714f, -0.242980197f + { .i = {0x3F3504F3,0xBF7D3AAC,0x3F74FA0C,0xBF676BD8} }, //+0.707106769f, -0.989176512f, +0.956940413f, -0.903989315f + { .i = {0x3F54DB31,0xBF3DAEFB,0x3F22679A,0xBF039C3D} }, //+0.831469595f, -0.740951240f, +0.634393334f, -0.514102757f + { .i = {0x3EC3EF1B,0xBE78CFCD,0x3DC8BD5D,0x3D48FB29} }, //+0.382683605f, -0.242980197f, +0.098017432f, +0.049067650f + { .i = {0xBE47C5C6,0x3EAC7CD3,0xBEF15AE8,0x3F187FC0} }, //-0.195090383f, +0.336889833f, -0.471396685f, +0.595699310f + { .i = {0xBF3504F1,0x3F4D9F02,0xBF61C597,0x3F710908} }, //-0.707106650f, +0.803207517f, -0.881921232f, +0.941544056f + { .i = {0xBF7B14BE,0x3F7FB10F,0xBF7EC46D,0x3F7853F8} }, //-0.980785251f, +0.998795450f, -0.995184720f, +0.970031261f + { .i = {0xBF6C8360,0x3F5B941B,0xBF45E404,0x3F2BEB4C} }, //-0.923879623f, +0.857728660f, -0.773010492f, +0.671559095f + { .i = {0xBF0E39DC,0x3EDAE88A,0xBE94A033,0x3E164080} }, //-0.555570364f, +0.427555382f, -0.290284723f, +0.146730423f + { .i = {0x3F3504F3,0xBF7FB10F,0x3F7EC46E,0xBF7D3AAC} }, //+0.707106769f, -0.998795450f, +0.995184779f, -0.989176512f + { .i = {0x3F7B14BF,0xBF7853F8,0x3F74FA0C,0xBF710909} }, //+0.980785310f, -0.970031261f, +0.956940413f, -0.941544116f + { .i = {0x3F6C835F,0xBF676BD8,0x3F61C599,0xBF5B941A} }, //+0.923879564f, -0.903989315f, +0.881921351f, -0.857728601f + { .i = {0x3F54DB31,0xBF4D9F04,0x3F45E405,0xBF3DAEFB} }, //+0.831469595f, -0.803207636f, +0.773010552f, -0.740951240f + { .i = {0x3F3504F7,0xBF2BEB4B,0x3F22679A,0xBF187FC1} }, //+0.707107008f, -0.671559036f, +0.634393334f, -0.595699370f + { .i = {0x3F0E39DD,0xBF039C3D,0x3EF15AE9,0xBEDAE880} }, //+0.555570424f, -0.514102757f, +0.471396714f, -0.427555084f + { .i = {0x3EC3EF1B,0xBEAC7CD6,0x3E94A03D,0xBE78CFCD} }, //+0.382683605f, -0.336889923f, +0.290285021f, -0.242980197f + { .i = {0x3E47C5C8,0xBE16408A,0x3DC8BD5D,0xBD48FB41} }, //+0.195090413f, -0.146730572f, +0.098017432f, -0.049067739f +}; + +/* 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] = { + /* 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 + { .i = {0xB8000074,0xB8000074,0xB8000074,0xB83FFF9B} }, //-0.000030518f, -0.000030518f, -0.000030518f, -0.000045776f + { .i = {0xB83FFF9B,0xB87FFFD5,0xB87FFFD5,0xB8A00008} }, //-0.000045776f, -0.000061035f, -0.000061035f, -0.000076294f + { .i = {0xB8A00008,0xB8C00025,0xB8E00041,0xB8E00041} }, //-0.000076294f, -0.000091553f, -0.000106812f, -0.000106812f + { .i = {0xB8FFFFD5,0xB90FFFF9,0xB9200008,0xB9300016} }, //-0.000122070f, -0.000137329f, -0.000152588f, -0.000167847f + { .i = {0xB94FFFEE,0xB95FFFFD,0xB980000D,0xB987FFF2} }, //-0.000198364f, -0.000213623f, -0.000244141f, -0.000259399f + { .i = {0xB9980000,0xB9A8000F,0xB9C00002,0xB9D00011} }, //-0.000289917f, -0.000320435f, -0.000366211f, -0.000396729f + { .i = {0xB9E80004,0xB9F7FFF0,0xBA0C0007,0xBA180000} }, //-0.000442505f, -0.000473022f, -0.000534058f, -0.000579834f + { .i = {0xBA23FFFA,0xBA340008,0xBA440006,0xBA540003} }, //-0.000625610f, -0.000686646f, -0.000747681f, -0.000808716f + { .i = {0xBA680004,0xBA7C0005,0xBA880003,0xBA920003} }, //-0.000885010f, -0.000961304f, -0.001037598f, -0.001113892f + { .i = {0xBA9DFFFD,0xBAA9FFFF,0xBAB60002,0xBAC20004} }, //-0.001205444f, -0.001296997f, -0.001388550f, -0.001480103f + { .i = {0xBACFFFFF,0xBADE0004,0xBAE9FFFD,0xBAFA0003} }, //-0.001586914f, -0.001693726f, -0.001785278f, -0.001907349f + { .i = {0xBB03FFFF,0xBB0B0001,0xBB130000,0xBB1A0002} }, //-0.002014160f, -0.002120972f, -0.002243042f, -0.002349854f + { .i = {0xBB210000,0xBB28FFFE,0xBB300001,0xBB36FFFE} }, //-0.002456665f, -0.002578735f, -0.002685547f, -0.002792358f + { .i = {0xBB3E0000,0xBB440001,0xBB49FFFE,0xBB4FFFFF} }, //-0.002899170f, -0.002990723f, -0.003082275f, -0.003173828f + { .i = {0x3B550000,0x3B5A0000,0x3B5DFFFF,0x3B610002} }, //+0.003250122f, +0.003326416f, +0.003387451f, +0.003433228f + { .i = {0x3B62FFFF,0x3B640000,0x3B640000,0x3B62FFFF} }, //+0.003463745f, +0.003479004f, +0.003479004f, +0.003463745f + { .i = {0x3B600001,0x3B5CFFFE,0x3B570002,0x3B4FFFFF} }, //+0.003417969f, +0.003372192f, +0.003280640f, +0.003173828f + { .i = {0x3B480001,0x3B3CFFFF,0x3B310001,0x3B230002} }, //+0.003051758f, +0.002883911f, +0.002700806f, +0.002487183f + { .i = {0x3B11FFFF,0x3AFDFFFE,0x3AD40003,0x3AA5FFFC} }, //+0.002227783f, +0.001937866f, +0.001617432f, +0.001266479f + { .i = {0x3A640000,0x39E80004,0xB8000074,0xBA0FFFF9} }, //+0.000869751f, +0.000442505f, -0.000030518f, -0.000549316f + { .i = {0xBA900002,0xBADE0004,0xBB190001,0xBB44FFFE} }, //-0.001098633f, -0.001693726f, -0.002334595f, -0.003005981f + { .i = {0xBB740002,0xBB930000,0xBBAD8000,0xBBC87FFF} }, //-0.003723145f, -0.004486084f, -0.005294800f, -0.006118774f + { .i = {0xBBE58000,0xBC01C001,0xBC114000,0xBC214000} }, //-0.007003784f, -0.007919312f, -0.008865356f, -0.009841919f + { .i = {0xBC31C000,0xBC42C000,0xBC540000,0xBC65C000} }, //-0.010848999f, -0.011886597f, -0.012939453f, -0.014022827f + { .i = {0xBC77C000,0xBC850000,0xBC8E2000,0xBC974000} }, //-0.015121460f, -0.016235352f, -0.017349243f, -0.018463135f + { .i = {0xBCA06000,0xBCA98000,0xBCB28000,0xBCBB4000} }, //-0.019577026f, -0.020690918f, -0.021789551f, -0.022857666f + { .i = {0xBCC3E000,0xBCCC4000,0xBCD44000,0xBCDBE000} }, //-0.023910522f, -0.024932861f, -0.025909424f, -0.026840210f + { .i = {0xBCE32000,0xBCE9C000,0xBCEFE000,0xBCF54000} }, //-0.027725220f, -0.028533936f, -0.029281616f, -0.029937744f + { .i = {0xBCFA2000,0xBCFE0000,0xBD009000,0xBD01B000} }, //-0.030532837f, -0.031005859f, -0.031387329f, -0.031661987f + { .i = {0xBD025000,0xBD027000,0xBD020000,0xBD00F000} }, //-0.031814575f, -0.031845093f, -0.031738281f, -0.031478882f + { .i = {0x3CFEA000,0x3CFA0000,0x3CF40000,0x3CECA000} }, //+0.031082153f, +0.030517578f, +0.029785156f, +0.028884888f + { .i = {0x3CE3C000,0x3CD96000,0x3CCD8000,0x3CBFE000} }, //+0.027801514f, +0.026535034f, +0.025085449f, +0.023422241f + { .i = {0x3CB0C000,0x3CA00000,0x3C8D6000,0x3C728000} }, //+0.021575928f, +0.019531250f, +0.017257690f, +0.014801025f + { .i = {0x3C468001,0x3C174000,0x3BC90000,0x3B390000} }, //+0.012115479f, +0.009231567f, +0.006134033f, +0.002822876f + { .i = {0xBA340008,0xBB8FFFFF,0xBC084000,0xBC4B8000} }, //-0.000686646f, -0.004394531f, -0.008316040f, -0.012420654f + { .i = {0xBC88E000,0xBCAD8000,0xBCD38000,0xBCFAC000} }, //-0.016708374f, -0.021179199f, -0.025817871f, -0.030609131f + { .i = {0xBD11A000,0xBD267000,0xBD3BC000,0xBD517000} }, //-0.035552979f, -0.040634155f, -0.045837402f, -0.051132202f + { .i = {0xBD679000,0xBD7DF000,0xBD8A4800,0xBD95A000} }, //-0.056533813f, -0.061996460f, -0.067520142f, -0.073059082f + { .i = {0xBDA10800,0xBDAC6800,0xBDB7B800,0xBDC2E800} }, //-0.078628540f, -0.084182739f, -0.089706421f, -0.095169067f + { .i = {0xBDCDE800,0xBDD8B800,0xBDE33800,0xBDED6800} }, //-0.100540161f, -0.105819702f, -0.110946655f, -0.115921021f + { .i = {0xBDF73000,0xBE004400,0xBE04AC00,0xBE08CC00} }, //-0.120697021f, -0.125259399f, -0.129562378f, -0.133590698f + { .i = {0xBE0C9800,0xBE100C00,0xBE132000,0xBE15C400} }, //-0.137298584f, -0.140670776f, -0.143676758f, -0.146255493f + { .i = {0xBE17FC00,0xBE19B800,0xBE1AF000,0xBE1B9C00} }, //-0.148422241f, -0.150115967f, -0.151306152f, -0.151962280f + { .i = {0xBE1BB800,0xBE1B3C00,0xBE1A1C00,0xBE185800} }, //-0.152069092f, -0.151596069f, -0.150497437f, -0.148773193f + { .i = {0xBE15E000,0xBE12B400,0xBE0ECC00,0xBE0A2000} }, //-0.146362305f, -0.143264771f, -0.139450073f, -0.134887695f + { .i = {0xBE04B000,0xBDFCE000,0xBDEEC000,0xBDDEF000} }, //-0.129577637f, -0.123474121f, -0.116577148f, -0.108856201f + { .i = {0x3DCD7000,0x3DBA3800,0x3DA54000,0x3D8E8800} }, //+0.100311279f, +0.090927124f, +0.080688477f, +0.069595337f + { .i = {0x3D6C0000,0x3D377000,0x3CFEA000,0x3C874000} }, //+0.057617188f, +0.044784546f, +0.031082153f, +0.016510010f + { .i = {0x3A8BFFFE,0xBC797FFF,0xBD04A000,0xBD4E4000} }, //+0.001068115f, -0.015228271f, -0.032379150f, -0.050354004f + { .i = {0xBD8DA800,0xBDB5D000,0xBDDF9000,0xBE057000} }, //-0.069168091f, -0.088775635f, -0.109161377f, -0.130310059f + { .i = {0xBE1BDC00,0xBE32FC00,0xBE4AD000,0xBE635000} }, //-0.152206421f, -0.174789429f, -0.198059082f, -0.221984863f + { .i = {0xBE7C6C00,0xBE8B0E00,0xBE982C00,0xBEA58A00} }, //-0.246505737f, -0.271591187f, -0.297210693f, -0.323318481f + { .i = {0xBEB32200,0xBEC0EC00,0xBECEE400,0xBEDD0200} }, //-0.349868774f, -0.376800537f, -0.404083252f, -0.431655884f + { .i = {0xBEEB4000,0xBEF99600,0xBF03FF00,0xBF0B3800} }, //-0.459472656f, -0.487472534f, -0.515609741f, -0.543823242f + { .i = {0xBF127100,0xBF19A800,0xBF20D800,0xBF27FE00} }, //-0.572036743f, -0.600219727f, -0.628295898f, -0.656219482f + { .i = {0xBF2F1500,0xBF361900,0xBF3D0600,0xBF43D900} }, //-0.683914185f, -0.711318970f, -0.738372803f, -0.765029907f + { .i = {0xBF4A8D00,0xBF511E00,0xBF578A00,0xBF5DCA00} }, //-0.791213989f, -0.816864014f, -0.841949463f, -0.866363525f + { .i = {0xBF63DD00,0xBF69BE00,0xBF6F6900,0xBF74DC00} }, //-0.890090942f, -0.913055420f, -0.935195923f, -0.956481934f + { .i = {0xBF7A1300,0xBF7F0A00,0xBF81DF00,0xBF841680} }, //-0.976852417f, -0.996246338f, -1.014617920f, -1.031936646f + { .i = {0xBF862A00,0xBF881780,0xBF89DF00,0xBF8B7E00} }, //-1.048156738f, -1.063217163f, -1.077117920f, -1.089782715f + { .i = {0xBF8CF480,0xBF8E4180,0xBF8F6380,0xBF905A00} }, //-1.101211548f, -1.111373901f, -1.120223999f, -1.127746582f + { .i = {0xBF912480,0xBF91C300,0xBF923400,0xBF927800} }, //-1.133926392f, -1.138763428f, -1.142211914f, -1.144287109f + /* part 2 */ + { .i = {0x3F928F00,0x3F927800,0x3F923400,0x3F91C300} }, //+1.144989014f, +1.144287109f, +1.142211914f, +1.138763428f + { .i = {0x3F912480,0x3F905A00,0x3F8F6380,0x3F8E4180} }, //+1.133926392f, +1.127746582f, +1.120223999f, +1.111373901f + { .i = {0x3F8CF480,0x3F8B7E00,0x3F89DF00,0x3F881780} }, //+1.101211548f, +1.089782715f, +1.077117920f, +1.063217163f + { .i = {0x3F862A00,0x3F841680,0x3F81DF00,0x3F7F0A00} }, //+1.048156738f, +1.031936646f, +1.014617920f, +0.996246338f + { .i = {0x3F7A1300,0x3F74DC00,0x3F6F6900,0x3F69BE00} }, //+0.976852417f, +0.956481934f, +0.935195923f, +0.913055420f + { .i = {0x3F63DD00,0x3F5DCA00,0x3F578A00,0x3F511E00} }, //+0.890090942f, +0.866363525f, +0.841949463f, +0.816864014f + { .i = {0x3F4A8D00,0x3F43D900,0x3F3D0600,0x3F361900} }, //+0.791213989f, +0.765029907f, +0.738372803f, +0.711318970f + { .i = {0x3F2F1500,0x3F27FE00,0x3F20D800,0x3F19A800} }, //+0.683914185f, +0.656219482f, +0.628295898f, +0.600219727f + { .i = {0x3F127100,0x3F0B3800,0x3F03FF00,0x3EF99600} }, //+0.572036743f, +0.543823242f, +0.515609741f, +0.487472534f + { .i = {0x3EEB4000,0x3EDD0200,0x3ECEE400,0x3EC0EC00} }, //+0.459472656f, +0.431655884f, +0.404083252f, +0.376800537f + { .i = {0x3EB32200,0x3EA58A00,0x3E982C00,0x3E8B0E00} }, //+0.349868774f, +0.323318481f, +0.297210693f, +0.271591187f + { .i = {0x3E7C6C00,0x3E635000,0x3E4AD000,0x3E32FC00} }, //+0.246505737f, +0.221984863f, +0.198059082f, +0.174789429f + { .i = {0x3E1BDC00,0x3E057000,0x3DDF9000,0x3DB5D000} }, //+0.152206421f, +0.130310059f, +0.109161377f, +0.088775635f + { .i = {0x3D8DA800,0x3D4E4000,0x3D04A000,0x3C797FFF} }, //+0.069168091f, +0.050354004f, +0.032379150f, +0.015228271f + { .i = {0xBA8BFFFE,0xBC874000,0xBCFEA000,0xBD377000} }, //-0.001068115f, -0.016510010f, -0.031082153f, -0.044784546f + { .i = {0xBD6C0000,0xBD8E8800,0xBDA54000,0xBDBA3800} }, //-0.057617188f, -0.069595337f, -0.080688477f, -0.090927124f + { .i = {0x3DCD7000,0x3DDEF000,0x3DEEC000,0x3DFCE000} }, //+0.100311279f, +0.108856201f, +0.116577148f, +0.123474121f + { .i = {0x3E04B000,0x3E0A2000,0x3E0ECC00,0x3E12B400} }, //+0.129577637f, +0.134887695f, +0.139450073f, +0.143264771f + { .i = {0x3E15E000,0x3E185800,0x3E1A1C00,0x3E1B3C00} }, //+0.146362305f, +0.148773193f, +0.150497437f, +0.151596069f + { .i = {0x3E1BB800,0x3E1B9C00,0x3E1AF000,0x3E19B800} }, //+0.152069092f, +0.151962280f, +0.151306152f, +0.150115967f + { .i = {0x3E17FC00,0x3E15C400,0x3E132000,0x3E100C00} }, //+0.148422241f, +0.146255493f, +0.143676758f, +0.140670776f + { .i = {0x3E0C9800,0x3E08CC00,0x3E04AC00,0x3E004400} }, //+0.137298584f, +0.133590698f, +0.129562378f, +0.125259399f + { .i = {0x3DF73000,0x3DED6800,0x3DE33800,0x3DD8B800} }, //+0.120697021f, +0.115921021f, +0.110946655f, +0.105819702f + { .i = {0x3DCDE800,0x3DC2E800,0x3DB7B800,0x3DAC6800} }, //+0.100540161f, +0.095169067f, +0.089706421f, +0.084182739f + { .i = {0x3DA10800,0x3D95A000,0x3D8A4800,0x3D7DF000} }, //+0.078628540f, +0.073059082f, +0.067520142f, +0.061996460f + { .i = {0x3D679000,0x3D517000,0x3D3BC000,0x3D267000} }, //+0.056533813f, +0.051132202f, +0.045837402f, +0.040634155f + { .i = {0x3D11A000,0x3CFAC000,0x3CD38000,0x3CAD8000} }, //+0.035552979f, +0.030609131f, +0.025817871f, +0.021179199f + { .i = {0x3C88E000,0x3C4B8000,0x3C084000,0x3B8FFFFF} }, //+0.016708374f, +0.012420654f, +0.008316040f, +0.004394531f + { .i = {0x3A340008,0xBB390000,0xBBC90000,0xBC174000} }, //+0.000686646f, -0.002822876f, -0.006134033f, -0.009231567f + { .i = {0xBC468001,0xBC728000,0xBC8D6000,0xBCA00000} }, //-0.012115479f, -0.014801025f, -0.017257690f, -0.019531250f + { .i = {0xBCB0C000,0xBCBFE000,0xBCCD8000,0xBCD96000} }, //-0.021575928f, -0.023422241f, -0.025085449f, -0.026535034f + { .i = {0xBCE3C000,0xBCECA000,0xBCF40000,0xBCFA0000} }, //-0.027801514f, -0.028884888f, -0.029785156f, -0.030517578f + { .i = {0x3CFEA000,0x3D00F000,0x3D020000,0x3D027000} }, //+0.031082153f, +0.031478882f, +0.031738281f, +0.031845093f + { .i = {0x3D025000,0x3D01B000,0x3D009000,0x3CFE0000} }, //+0.031814575f, +0.031661987f, +0.031387329f, +0.031005859f + { .i = {0x3CFA2000,0x3CF54000,0x3CEFE000,0x3CE9C000} }, //+0.030532837f, +0.029937744f, +0.029281616f, +0.028533936f + { .i = {0x3CE32000,0x3CDBE000,0x3CD44000,0x3CCC4000} }, //+0.027725220f, +0.026840210f, +0.025909424f, +0.024932861f + { .i = {0x3CC3E000,0x3CBB4000,0x3CB28000,0x3CA98000} }, //+0.023910522f, +0.022857666f, +0.021789551f, +0.020690918f + { .i = {0x3CA06000,0x3C974000,0x3C8E2000,0x3C850000} }, //+0.019577026f, +0.018463135f, +0.017349243f, +0.016235352f + { .i = {0x3C77C000,0x3C65C000,0x3C540000,0x3C42C000} }, //+0.015121460f, +0.014022827f, +0.012939453f, +0.011886597f + { .i = {0x3C31C000,0x3C214000,0x3C114000,0x3C01C001} }, //+0.010848999f, +0.009841919f, +0.008865356f, +0.007919312f + { .i = {0x3BE58000,0x3BC87FFF,0x3BAD8000,0x3B930000} }, //+0.007003784f, +0.006118774f, +0.005294800f, +0.004486084f + { .i = {0x3B740002,0x3B44FFFE,0x3B190001,0x3ADE0004} }, //+0.003723145f, +0.003005981f, +0.002334595f, +0.001693726f + { .i = {0x3A900002,0x3A0FFFF9,0x38000074,0xB9E80004} }, //+0.001098633f, +0.000549316f, +0.000030518f, -0.000442505f + { .i = {0xBA640000,0xBAA5FFFC,0xBAD40003,0xBAFDFFFE} }, //-0.000869751f, -0.001266479f, -0.001617432f, -0.001937866f + { .i = {0xBB11FFFF,0xBB230002,0xBB310001,0xBB3CFFFF} }, //-0.002227783f, -0.002487183f, -0.002700806f, -0.002883911f + { .i = {0xBB480001,0xBB4FFFFF,0xBB570002,0xBB5CFFFE} }, //-0.003051758f, -0.003173828f, -0.003280640f, -0.003372192f + { .i = {0xBB600001,0xBB62FFFF,0xBB640000,0xBB640000} }, //-0.003417969f, -0.003463745f, -0.003479004f, -0.003479004f + { .i = {0xBB62FFFF,0xBB610002,0xBB5DFFFF,0xBB5A0000} }, //-0.003463745f, -0.003433228f, -0.003387451f, -0.003326416f + { .i = {0x3B550000,0x3B4FFFFF,0x3B49FFFE,0x3B440001} }, //+0.003250122f, +0.003173828f, +0.003082275f, +0.002990723f + { .i = {0x3B3E0000,0x3B36FFFE,0x3B300001,0x3B28FFFE} }, //+0.002899170f, +0.002792358f, +0.002685547f, +0.002578735f + { .i = {0x3B210000,0x3B1A0002,0x3B130000,0x3B0B0001} }, //+0.002456665f, +0.002349854f, +0.002243042f, +0.002120972f + { .i = {0x3B03FFFF,0x3AFA0003,0x3AE9FFFD,0x3ADE0004} }, //+0.002014160f, +0.001907349f, +0.001785278f, +0.001693726f + { .i = {0x3ACFFFFF,0x3AC20004,0x3AB60002,0x3AA9FFFF} }, //+0.001586914f, +0.001480103f, +0.001388550f, +0.001296997f + { .i = {0x3A9DFFFD,0x3A920003,0x3A880003,0x3A7C0005} }, //+0.001205444f, +0.001113892f, +0.001037598f, +0.000961304f + { .i = {0x3A680004,0x3A540003,0x3A440006,0x3A340008} }, //+0.000885010f, +0.000808716f, +0.000747681f, +0.000686646f + { .i = {0x3A23FFFA,0x3A180000,0x3A0C0007,0x39F7FFF0} }, //+0.000625610f, +0.000579834f, +0.000534058f, +0.000473022f + { .i = {0x39E80004,0x39D00011,0x39C00002,0x39A8000F} }, //+0.000442505f, +0.000396729f, +0.000366211f, +0.000320435f + { .i = {0x39980000,0x3987FFF2,0x3980000D,0x395FFFFD} }, //+0.000289917f, +0.000259399f, +0.000244141f, +0.000213623f + { .i = {0x394FFFEE,0x39300016,0x39200008,0x390FFFF9} }, //+0.000198364f, +0.000167847f, +0.000152588f, +0.000137329f + { .i = {0x38FFFFD5,0x38E00041,0x38E00041,0x38C00025} }, //+0.000122070f, +0.000106812f, +0.000106812f, +0.000091553f + { .i = {0x38A00008,0x38A00008,0x387FFFD5,0x387FFFD5} }, //+0.000076294f, +0.000076294f, +0.000061035f, +0.000061035f + { .i = {0x383FFF9B,0x383FFF9B,0x38000074,0x38000074} }, //+0.000045776f, +0.000045776f, +0.000030518f, +0.000030518f + { .i = {0x38000074,0x38000074,0x37800074,0x37800074} }, //+0.000030518f, +0.000030518f, +0.000015259f, +0.000015259f + { .i = {0x37800074,0x37800074,0x37800074,0x37800074} }, //+0.000015259f, +0.000015259f, +0.000015259f, +0.000015259f +}; + +/* DCT-II table? */ +static const REG_VF SYNTH_TABLE[32/4] = { + { .i = {0x3F000000,0x3F002785,0x3F009E8D,0x3F01668B} }, //+0.500000000f, +0.500603020f, +0.502419293f, +0.505470932f + { .i = {0x3F0281F6,0x3F03F45A,0x3F05C278,0x3F07F268} }, //+0.509795547f, +0.515447259f, +0.522498608f, +0.531042576f + { .i = {0x3F0A8BD4,0x3F0D9837,0x3F11233F,0x3F153B3A} }, //+0.541196108f, +0.553103864f, +0.566944063f, +0.582934976f + { .i = {0x3F19F1BD,0x3F1F5C6E,0x3F25961C,0x3F2CC03D} }, //+0.601344883f, +0.622504115f, +0.646821737f, +0.674808323f + { .i = {0x3F3504F2,0x3F3E99ED,0x3F49C480,0x3F56DF9E} }, //+0.707106709f, +0.744536221f, +0.788154602f, +0.839349627f + { .i = {0x3F6664D7,0x3F78FA3B,0x3F87C449,0x3F95B034} }, //+0.899976194f, +0.972568214f, +1.060677648f, +1.169439793f + { .i = {0x3FA73D74,0x3FBDF91B,0x3FDC7925,0x4003B2AF} }, //+1.306562901f, +1.484164596f, +1.722447038f, +2.057780981f + { .i = {0x402406CE,0x405A1641,0x40A33C9C,0x41230A46} }, //+2.562915325f, +3.407608271f, +5.101148605f, +10.190008163f +}; + +#endif /* _TAC_DECODER_LIB_DATA_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib_ops.h b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib_ops.h new file mode 100644 index 000000000..6a265a5cd --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/coding/tac_decoder_lib_ops.h @@ -0,0 +1,376 @@ +#ifndef _TAC_DECODER_LIB_OPS_H_ +#define _TAC_DECODER_LIB_OPS_H_ + +#include +#include "tac_decoder_lib_ops.h" + +/* The following ops are similar to VU1's ops, but not quite the same. For example VU1 has special op + * registers like the ACC, and updates zero/neg/etc flags per op (plus added here a few helper ops). + * Main reason to use them vs doing standard +*-/ in code is allowing to simulate PS2 floats. + * See Nisto's decoder for actual emulation. */ + + +/* PS2 floats are slightly different vs IEEE 754 floats: + * - NaN and Inf (exp 255) don't exist on the PS2, meaning it has a bigger range of floats + * - denormals (exp 0) don't exist either, and ops truncate to 0 + * - rounding on PS2 always rounds towards zero + * The code below (partially) simulates this, but for audio it only means +-1 differences, + * plus we can't fully emulate exact behaviour, so it's disabled for performance + * (function call is optimized out by compiler). */ +#define TAC_ENABLE_PS2_FLOATS 0 + +static inline void UPDATE_FLOATS(uint8_t dest, REG_VF *vf) { +#if TAC_ENABLE_PS2_FLOATS + int i; + + for (i = 0; i < 4; i++) { + int shift = 3 - i; + if (dest & (1 << shift)) { + + if (vf->F[i] == 0.0) { + uint32_t v = vf->UL[i]; + int exp = (v >> 23) & 0xff; + uint32_t s = v & 0x80000000; + + switch (exp) { + case 0: + vf->UL[i] = s; + break; + case 255: + vf->UL[i] = s|0x7f7fffff; /* max allowed */ + break; + default: /* standard */ + vf->UL[i] = v; + break; + } + } + } + } +#endif +} + +static inline void _DIV_INTERNAL(REG_VF *fd, const REG_VF *fs, const REG_VF *ft, int from) { + float dividend = fs->F[from]; + float divisor = ft->F[from]; + +#if TAC_ENABLE_PS2_FLOATS + if (divisor == 0.0) { + if ((ft->UL[from] & 0x80000000) != (0x80000000 & fs->UL[from])) { + fd->UL[from] = 0xFF7FFFFF; + } + else { + fd->UL[from] = 0x7F7FFFFF; + } + } + else { + fd->F[from] = dividend / divisor; + } +#else + fd->F[from] = dividend / divisor; +#endif +} + +/////////////////////////////////////////////////////////////////////////////// + +static inline void DIV(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) _DIV_INTERNAL(fd, fs, ft, 0); + if (dest & __y__) _DIV_INTERNAL(fd, fs, ft, 1); + if (dest & ___z_) _DIV_INTERNAL(fd, fs, ft, 2); + if (dest & ____w) _DIV_INTERNAL(fd, fs, ft, 3); + UPDATE_FLOATS(dest, fd); +} + +/////////////////////////////////////////////////////////////////////////////// + +static inline void ADD(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fs->f.x + ft->f.x; + if (dest & __y__) fd->f.y = fs->f.y + ft->f.y; + if (dest & ___z_) fd->f.z = fs->f.z + ft->f.z; + if (dest & ____w) fd->f.w = fs->f.w + ft->f.w; + UPDATE_FLOATS(dest, fd); +} + +static inline void ADDx(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fs->f.x + ft->f.x; + if (dest & __y__) fd->f.y = fs->f.y + ft->f.x; + if (dest & ___z_) fd->f.z = fs->f.z + ft->f.x; + if (dest & ____w) fd->f.w = fs->f.w + ft->f.x; + UPDATE_FLOATS(dest, fd); +} + +static inline void ADDy(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fs->f.x + ft->f.y; + if (dest & __y__) fd->f.y = fs->f.y + ft->f.y; + if (dest & ___z_) fd->f.z = fs->f.z + ft->f.y; + if (dest & ____w) fd->f.w = fs->f.w + ft->f.y; + UPDATE_FLOATS(dest, fd); +} + +static inline void ADDz(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fs->f.x + ft->f.z; + if (dest & __y__) fd->f.y = fs->f.y + ft->f.z; + if (dest & ___z_) fd->f.z = fs->f.z + ft->f.z; + if (dest & ____w) fd->f.w = fs->f.w + ft->f.z; + UPDATE_FLOATS(dest, fd); +} + +static inline void ADDw(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fs->f.x + ft->f.w; + if (dest & __y__) fd->f.y = fs->f.y + ft->f.w; + if (dest & ___z_) fd->f.z = fs->f.z + ft->f.w; + if (dest & ____w) fd->f.w = fs->f.w + ft->f.w; + UPDATE_FLOATS(dest, fd); +} + +/////////////////////////////////////////////////////////////////////////////// + +static inline void SUB(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fs->f.x - ft->f.x; + if (dest & __y__) fd->f.y = fs->f.y - ft->f.y; + if (dest & ___z_) fd->f.z = fs->f.z - ft->f.z; + if (dest & ____w) fd->f.w = fs->f.w - ft->f.w; + UPDATE_FLOATS(dest, fd); +} + +static inline void SUBx(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fs->f.x - ft->f.x; + if (dest & __y__) fd->f.y = fs->f.y - ft->f.x; + if (dest & ___z_) fd->f.z = fs->f.z - ft->f.x; + if (dest & ____w) fd->f.w = fs->f.w - ft->f.x; + UPDATE_FLOATS(dest, fd); +} + +static inline void SUBy(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fs->f.x - ft->f.y; + if (dest & __y__) fd->f.y = fs->f.y - ft->f.y; + if (dest & ___z_) fd->f.z = fs->f.z - ft->f.y; + if (dest & ____w) fd->f.w = fs->f.w - ft->f.y; + UPDATE_FLOATS(dest, fd); +} + +static inline void SUBz(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fs->f.x - ft->f.z; + if (dest & __y__) fd->f.y = fs->f.y - ft->f.z; + if (dest & ___z_) fd->f.z = fs->f.z - ft->f.z; + if (dest & ____w) fd->f.w = fs->f.w - ft->f.z; + UPDATE_FLOATS(dest, fd); +} + +static inline void SUBw(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fs->f.x - ft->f.w; + if (dest & __y__) fd->f.y = fs->f.y - ft->f.w; + if (dest & ___z_) fd->f.z = fs->f.z - ft->f.w; + if (dest & ____w) fd->f.w = fs->f.w - ft->f.w; + UPDATE_FLOATS(dest, fd); +} + +/////////////////////////////////////////////////////////////////////////////// + +static inline void MUL(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fs->f.x * ft->f.x; + if (dest & __y__) fd->f.y = fs->f.y * ft->f.y; + if (dest & ___z_) fd->f.z = fs->f.z * ft->f.z; + if (dest & ____w) fd->f.w = fs->f.w * ft->f.w; + UPDATE_FLOATS(dest, fd); +} + +static inline void MULx(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fs->f.x * ft->f.x; + if (dest & __y__) fd->f.y = fs->f.y * ft->f.x; + if (dest & ___z_) fd->f.z = fs->f.z * ft->f.x; + if (dest & ____w) fd->f.w = fs->f.w * ft->f.x; + UPDATE_FLOATS(dest, fd); +} + +static inline void MULy(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fs->f.x * ft->f.y; + if (dest & __y__) fd->f.y = fs->f.y * ft->f.y; + if (dest & ___z_) fd->f.z = fs->f.z * ft->f.y; + if (dest & ____w) fd->f.w = fs->f.w * ft->f.y; + UPDATE_FLOATS(dest, fd); +} + +static inline void MULz(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fs->f.x * ft->f.z; + if (dest & __y__) fd->f.y = fs->f.y * ft->f.z; + if (dest & ___z_) fd->f.z = fs->f.z * ft->f.z; + if (dest & ____w) fd->f.w = fs->f.w * ft->f.z; + UPDATE_FLOATS(dest, fd); +} + +static inline void MULw(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fs->f.x * ft->f.w; + if (dest & __y__) fd->f.y = fs->f.y * ft->f.w; + if (dest & ___z_) fd->f.z = fs->f.z * ft->f.w; + if (dest & ____w) fd->f.w = fs->f.w * ft->f.w; + UPDATE_FLOATS(dest, fd); +} + +/////////////////////////////////////////////////////////////////////////////// + +static inline void MADD(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fd->f.x + (fs->f.x * ft->f.x); + if (dest & __y__) fd->f.y = fd->f.y + (fs->f.y * ft->f.y); + if (dest & ___z_) fd->f.z = fd->f.z + (fs->f.z * ft->f.z); + if (dest & ____w) fd->f.w = fd->f.w + (fs->f.w * ft->f.w); + UPDATE_FLOATS(dest, fd); +} + +static inline void MADDx(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fd->f.x + (fs->f.x * ft->f.x); + if (dest & __y__) fd->f.y = fd->f.y + (fs->f.y * ft->f.x); + if (dest & ___z_) fd->f.z = fd->f.z + (fs->f.z * ft->f.x); + if (dest & ____w) fd->f.w = fd->f.w + (fs->f.w * ft->f.x); + UPDATE_FLOATS(dest, fd); +} + +static inline void MADDy(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fd->f.x + (fs->f.x * ft->f.y); + if (dest & __y__) fd->f.y = fd->f.y + (fs->f.y * ft->f.y); + if (dest & ___z_) fd->f.z = fd->f.z + (fs->f.z * ft->f.y); + if (dest & ____w) fd->f.w = fd->f.w + (fs->f.w * ft->f.y); + UPDATE_FLOATS(dest, fd); +} + +static inline void MADDz(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fd->f.x + (fs->f.x * ft->f.z); + if (dest & __y__) fd->f.y = fd->f.y + (fs->f.y * ft->f.z); + if (dest & ___z_) fd->f.z = fd->f.z + (fs->f.z * ft->f.z); + if (dest & ____w) fd->f.w = fd->f.w + (fs->f.w * ft->f.z); + UPDATE_FLOATS(dest, fd); +} + +static inline void MADDw(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fd->f.x + (fs->f.x * ft->f.w); + if (dest & __y__) fd->f.y = fd->f.y + (fs->f.y * ft->f.w); + if (dest & ___z_) fd->f.z = fd->f.z + (fs->f.z * ft->f.w); + if (dest & ____w) fd->f.w = fd->f.w + (fs->f.w * ft->f.w); + UPDATE_FLOATS(dest, fd); +} + +static inline void MSUBx(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fd->f.x - (fs->f.x * ft->f.x); + if (dest & __y__) fd->f.y = fd->f.y - (fs->f.y * ft->f.x); + if (dest & ___z_) fd->f.z = fd->f.z - (fs->f.z * ft->f.x); + if (dest & ____w) fd->f.w = fd->f.w - (fs->f.w * ft->f.x); + UPDATE_FLOATS(dest, fd); +} + +static inline void MSUBy(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fd->f.x - (fs->f.x * ft->f.y); + if (dest & __y__) fd->f.y = fd->f.y - (fs->f.y * ft->f.y); + if (dest & ___z_) fd->f.z = fd->f.z - (fs->f.z * ft->f.y); + if (dest & ____w) fd->f.w = fd->f.w - (fs->f.w * ft->f.y); + UPDATE_FLOATS(dest, fd); +} + +static inline void MSUBz(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fd->f.x - (fs->f.x * ft->f.z); + if (dest & __y__) fd->f.y = fd->f.y - (fs->f.y * ft->f.z); + if (dest & ___z_) fd->f.z = fd->f.z - (fs->f.z * ft->f.z); + if (dest & ____w) fd->f.w = fd->f.w - (fs->f.w * ft->f.z); + UPDATE_FLOATS(dest, fd); +} + +static inline void MSUBw(uint8_t dest, REG_VF *fd, const REG_VF *fs, const REG_VF *ft) { + if (dest & _x___) fd->f.x = fd->f.x - (fs->f.x * ft->f.w); + if (dest & __y__) fd->f.y = fd->f.y - (fs->f.y * ft->f.w); + if (dest & ___z_) fd->f.z = fd->f.z - (fs->f.z * ft->f.w); + if (dest & ____w) fd->f.w = fd->f.w - (fs->f.w * ft->f.w); + UPDATE_FLOATS(dest, fd); +} + +/////////////////////////////////////////////////////////////////////////////// + +static inline void FMUL(uint8_t dest, REG_VF *fd, const REG_VF *fs, const float I_F) { + if (dest & _x___) fd->f.x = fs->f.x * I_F; + if (dest & __y__) fd->f.y = fs->f.y * I_F; + if (dest & ___z_) fd->f.z = fs->f.z * I_F; + if (dest & ____w) fd->f.w = fs->f.w * I_F; + UPDATE_FLOATS(dest, fd); +} + +static inline void FMULf(uint8_t dest, REG_VF *fd, const float fs) { + if (dest & _x___) fd->f.x = fd->f.x * fs; + if (dest & __y__) fd->f.y = fd->f.y * fs; + if (dest & ___z_) fd->f.z = fd->f.z * fs; + if (dest & ____w) fd->f.w = fd->f.w * fs; + UPDATE_FLOATS(dest, fd); +} + +/////////////////////////////////////////////////////////////////////////////// + +static inline void ABS(uint8_t dest, REG_VF *ft, const REG_VF *fs) { + if (dest & _x___) ft->f.x = fabsf(fs->f.x); + if (dest & __y__) ft->f.y = fabsf(fs->f.y); + if (dest & ___z_) ft->f.z = fabsf(fs->f.z); + if (dest & ____w) ft->f.w = fabsf(fs->f.w); +} + +static inline void FTOI0(uint8_t dest, REG_VF *ft, const REG_VF *fs) { + if (dest & _x___) ft->SL[0] = (int32_t)fs->f.x; + if (dest & __y__) ft->SL[1] = (int32_t)fs->f.y; + if (dest & ___z_) ft->SL[2] = (int32_t)fs->f.z; + if (dest & ____w) ft->SL[3] = (int32_t)fs->f.w; +} + +static inline void ITOF0(uint8_t dest, REG_VF *ft, const REG_VF *fs) { + if (dest & _x___) ft->f.x = (float)fs->SL[0]; + if (dest & __y__) ft->f.y = (float)fs->SL[1]; + if (dest & ___z_) ft->f.z = (float)fs->SL[2]; + if (dest & ____w) ft->f.w = (float)fs->SL[3]; +} + +static inline void MR32(uint8_t dest, REG_VF *ft, const REG_VF *fs) { + float x = fs->f.x; + if (dest & _x___) ft->f.x = fs->f.y; + if (dest & __y__) ft->f.y = fs->f.z; + if (dest & ___z_) ft->f.z = fs->f.w; + if (dest & ____w) ft->f.w = x; +} + +/////////////////////////////////////////////////////////////////////////////// + +static inline void LOAD(uint8_t dest, REG_VF *ft, REG_VF* src, int pos) { + if (dest & _x___) ft->f.x = src[pos].f.x; + if (dest & __y__) ft->f.y = src[pos].f.y; + if (dest & ___z_) ft->f.z = src[pos].f.z; + if (dest & ____w) ft->f.w = src[pos].f.w; +} + +static inline void STORE(uint8_t dest, REG_VF* dst, const REG_VF *fs, int pos) { + if (dest & _x___) dst[pos].f.x = fs->f.x; + if (dest & __y__) dst[pos].f.y = fs->f.y; + if (dest & ___z_) dst[pos].f.z = fs->f.z; + if (dest & ____w) dst[pos].f.w = fs->f.w; +} + +static inline void MOVE(uint8_t dest, REG_VF *fd, const REG_VF *fs) { + if (dest & _x___) fd->f.x = fs->f.x; + if (dest & __y__) fd->f.y = fs->f.y; + if (dest & ___z_) fd->f.z = fs->f.z; + if (dest & ____w) fd->f.w = fs->f.w; +} + +static inline void MOVEx(uint8_t dest, REG_VF *fd, const REG_VF *fs) { + if (dest & _x___) fd->f.x = fs->f.x; + if (dest & __y__) fd->f.y = fs->f.x; + if (dest & ___z_) fd->f.z = fs->f.x; + if (dest & ____w) fd->f.w = fs->f.x; +} + +static inline void SIGN(uint8_t dest, REG_VF *fd, const REG_VF *fs) { + if (dest & _x___) if (fs->f.x < 0) fd->f.x = -fd->f.x; + if (dest & __y__) if (fs->f.y < 0) fd->f.y = -fd->f.y; + if (dest & ___z_) if (fs->f.z < 0) fd->f.z = -fd->f.z; + if (dest & ____w) if (fs->f.w < 0) fd->f.w = -fd->f.w; +} + +static inline void COPY(uint8_t dest, REG_VF *fd, const int16_t* buf) { + if (dest & _x___) fd->f.x = buf[0]; + if (dest & __y__) fd->f.y = buf[1]; + if (dest & ___z_) fd->f.z = buf[2]; + if (dest & ____w) fd->f.w = buf[3]; +} + +#endif /* _TAC_DECODER_LIB_OPS_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c index 6aa4fffd0..8f2665011 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/xa_decoder.c @@ -38,45 +38,57 @@ static const int IK1[4] = { 0, 0, 832, 880 }; * PS1 XA is apparently upsampled and interpolated to 44100, vgmstream doesn't simulate this. * * XA has an 8-bit decoding and "emphasis" modes, that no PS1 game actually uses, but apparently - * are supported by the CD hardware and will play if found. + * are supported by the CD hardware and will play if found. Some odd CD-i game does use 8-bit mode. + * Official "sound quality level" modes: + * - Level A: 37.8hz, 8-bit + * - Level B: 37.8hz, 4-bit + * - Level C: 18.9hz, 4-bit * * Info (Green Book): https://www.lscdweb.com/data/downloadables/2/8/cdi_may94_r2.pdf * BRR info (no$sns): http://problemkaputt.de/fullsnes.htm#snesapudspbrrsamples * (bsnes): https://github.com/byuu/bsnes/blob/master/bsnes/sfc/dsp/SPC_DSP.cpp#L316 */ -void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { +/* data layout (mono): + * - CD-XA audio is divided into sectors ("audio blocks"), each with 18*0x80 frames + * (sectors handled externally, this decoder only sees N frames) + * - each frame ("sound group") is divided into 8 interleaved subframes ("sound unit"), with + * 8*0x01 subframe headers x2 ("sound parameters") first then 28*0x04 subframe nibbles ("sound data") + * - subframe headers: 0..3 + repeat 0..3 + 4..7 + repeat 4..7 (where N = subframe N header) + * (repeats may be for error correction, though probably unused) + * header has a "range" N (gain of 2^N, or simplified as a shift) and a "filter" (control gains K0 and K1) + * - subframe nibbles: 32b with nibble0 for subframes 0..8, 32b with nibble1 for subframes 0..8, etc + * (low first: 32b = sf1-n0 sf0-n0 sf3-n0 sf2-n0 sf5-n0 sf4-n0 sf7-n0 sf6-n0, etc) + * + * stereo layout is the same but alternates channels: subframe 0/2/4/6=L, subframe 1/3/5/7=R + * + * example: + * subframe 0: header @ 0x00 or 0x04, 28 nibbles (low) @ 0x10,14,18,1c,20 ... 7c + * subframe 1: header @ 0x01 or 0x05, 28 nibbles (high) @ 0x10,14,18,1c,20 ... 7c + * subframe 2: header @ 0x02 or 0x06, 28 nibbles (low) @ 0x11,15,19,1d,21 ... 7d + * ... + * subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f + * + * 8-bit layout is similar but only has 4 subframes + subframe bytes, so half the samples: + * subframe 0: header @ 0x00 or 0x04/08/0c, 28 bytes @ 0x10,14,18,1c,20 ... 7c + * subframe 1: header @ 0x01 or 0x05/09/0d, 28 bytes @ 0x11,16,19,1d,21 ... 7d + * ... + * subframe 3: header @ 0x03 or 0x07/0b/0f, 28 bytes @ 0x13,17,1b,1f,23 ... 7f + */ + +void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_xa8) { uint8_t frame[0x80] = {0}; off_t frame_offset; int i,j, sp_pos, frames_in, samples_done = 0, sample_count = 0; size_t bytes_per_frame, samples_per_frame; int32_t hist1 = stream->adpcm_history1_32; int32_t hist2 = stream->adpcm_history2_32; + int subframes = (is_xa8) ? 4 : 8; - /* data layout (mono): - * - CD-XA audio is divided into sectors ("audio blocks"), each with 18 size 0x80 frames - * (handled externally, this decoder only gets frames) - * - a frame ("sound group") is divided into 8 subframes ("sound unit"), with - * subframe headers ("sound parameters") first then subframe nibbles ("sound data") - * - headers: 0..3 + repeat 0..3 + 4..7 + repeat 4..7 (where N = subframe N header) - * (repeats may be for error correction, though probably unused) - * - nibbles: 32b with nibble0 for subframes 0..8, 32b with nibble1 for subframes 0..8, etc - * (low first: 32b = sf1-n0 sf0-n0 sf3-n0 sf2-n0 sf5-n0 sf4-n0 sf7-n0 sf6-n0, etc) - * - * stereo layout is the same but alternates channels: subframe 0/2/4/6=L, subframe 1/3/5/7=R - * - * example: - * subframe 0: header @ 0x00 or 0x04, 28 nibbles (low) @ 0x10,14,18,1c,20 ... 7c - * subframe 1: header @ 0x01 or 0x05, 28 nibbles (high) @ 0x10,14,18,1c,20 ... 7c - * subframe 2: header @ 0x02 or 0x06, 28 nibbles (low) @ 0x11,15,19,1d,21 ... 7d - * ... - * subframe 7: header @ 0x0b or 0x0f, 28 nibbles (high) @ 0x13,17,1b,1f,23 ... 7f - */ - /* external interleave (fixed size), mono/stereo */ bytes_per_frame = 0x80; - samples_per_frame = 28*8 / channelspacing; + samples_per_frame = 28*subframes / channelspacing; frames_in = first_sample / samples_per_frame; first_sample = first_sample % samples_per_frame; @@ -84,25 +96,27 @@ void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, i frame_offset = stream->offset + bytes_per_frame * frames_in; read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ - VGM_ASSERT(get_32bitBE(frame+0x0) != get_32bitBE(frame+0x4) || get_32bitBE(frame+0x8) != get_32bitBE(frame+0xC), + VGM_ASSERT(get_u32be(frame+0x0) != get_u32be(frame+0x4) || get_u32be(frame+0x8) != get_u32be(frame+0xC), "bad frames at %x\n", (uint32_t)frame_offset); - /* decode subframes */ - for (i = 0; i < 8 / channelspacing; i++) { + for (i = 0; i < subframes / channelspacing; i++) { int32_t coef1, coef2; uint8_t coef_index, shift_factor; /* parse current subframe (sound unit)'s header (sound parameters) */ - sp_pos = 0x04 + i*channelspacing + channel; + sp_pos = is_xa8 ? + i*channelspacing + channel: + 0x04 + i*channelspacing + channel; coef_index = (frame[sp_pos] >> 4) & 0xf; shift_factor = (frame[sp_pos] >> 0) & 0xf; - VGM_ASSERT(coef_index > 4 || shift_factor > 12, "XA: incorrect coefs/shift at %x\n", (uint32_t)frame_offset + sp_pos); + /* mastered values like 0xFF exist [Micro Machines (CDi), demo and release] */ + VGM_ASSERT(coef_index > 4 || shift_factor > (is_xa8 ? 8 : 12), "XA: incorrect coefs/shift at %x\n", (uint32_t)frame_offset + sp_pos); if (coef_index > 4) coef_index = 0; /* only 4 filters are used, rest is apparently 0 */ - if (shift_factor > 12) - shift_factor = 9; /* supposedly, from Nocash PSX docs */ + if (shift_factor > (is_xa8 ? 8 : 12)) + shift_factor = (is_xa8 ? 8 : 9); /* supposedly, from Nocash PSX docs (in 8-bit mode max range should be 8 though) */ coef1 = IK0[coef_index]; coef2 = IK1[coef_index]; @@ -110,15 +124,7 @@ void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, i /* decode subframe nibbles */ for(j = 0; j < 28; j++) { - uint8_t nibbles; - int32_t new_sample; - - int su_pos = (channelspacing==1) ? - 0x10 + j*0x04 + (i/2) : /* mono */ - 0x10 + j*0x04 + i; /* stereo */ - int get_high_nibble = (channelspacing==1) ? - (i&1) : /* mono (even subframes = low, off subframes = high) */ - (channel == 1); /* stereo (L channel / even subframes = low, R channel / odd subframes = high) */ + int32_t sample; /* skip half decodes to make sure hist isn't touched (kinda hack-ish) */ if (!(sample_count >= first_sample && samples_done < samples_to_do)) { @@ -126,22 +132,39 @@ void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, i continue; } - nibbles = frame[su_pos]; + if (is_xa8) { + int su_pos = (channelspacing==1) ? + 0x10 + j*0x04 + i : /* mono */ + 0x10 + j*0x04 + i*2 + channel; /* stereo */ - new_sample = get_high_nibble ? - (nibbles >> 4) & 0x0f : - (nibbles >> 0) & 0x0f; + sample = frame[su_pos]; + sample = (int16_t)((sample << 8) & 0xff00) >> shift_factor; /* 16b sign extend + scale */ + } + else { + uint8_t nibbles; + int su_pos = (channelspacing==1) ? + 0x10 + j*0x04 + (i/2) : /* mono */ + 0x10 + j*0x04 + i; /* stereo */ + int get_high_nibble = (channelspacing==1) ? + (i&1) : /* mono (even subframes = low, off subframes = high) */ + (channel == 1); /* stereo (L channel / even subframes = low, R channel / odd subframes = high) */ - new_sample = (int16_t)((new_sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */ - new_sample = new_sample << 4; - new_sample = new_sample - ((coef1*hist1 + coef2*hist2) >> 10); + nibbles = frame[su_pos]; + sample = get_high_nibble ? + (nibbles >> 4) & 0x0f : + (nibbles >> 0) & 0x0f; + sample = (int16_t)((sample << 12) & 0xf000) >> shift_factor; /* 16b sign extend + scale */ + } + + sample = sample << 4; /* scale for current IK */ + sample = sample - ((coef1*hist1 + coef2*hist2) >> 10); hist2 = hist1; - hist1 = new_sample; /* must go before clamp, somehow */ - new_sample = new_sample >> 4; - new_sample = clamp16(new_sample); + hist1 = sample; /* must go before clamp, somehow */ + sample = sample >> 4; + sample = clamp16(sample); - outbuf[samples_done * channelspacing] = new_sample; + outbuf[samples_done * channelspacing] = sample; samples_done++; sample_count++; @@ -152,11 +175,13 @@ void decode_xa(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, i stream->adpcm_history2_32 = hist2; } -size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2) { + +size_t xa_bytes_to_samples(size_t bytes, int channels, int is_blocked, int is_form2, int bps) { + int subframes = (bps == 8) ? 4 : 8; if (is_blocked) { - return (bytes / 0x930) * (28*8/ channels) * (is_form2 ? 18 : 16); + return (bytes / 0x930) * (28*subframes/ channels) * (is_form2 ? 18 : 16); } else { - return (bytes / 0x80) * (28*8 / channels); + return (bytes / 0x80) * (28*subframes / channels); } } diff --git a/Frameworks/vgmstream/vgmstream/src/decode.c b/Frameworks/vgmstream/vgmstream/src/decode.c index 500012731..17ecbb92b 100644 --- a/Frameworks/vgmstream/vgmstream/src/decode.c +++ b/Frameworks/vgmstream/vgmstream/src/decode.c @@ -32,6 +32,10 @@ void free_codec(VGMSTREAM* vgmstream) { free_hca(vgmstream->codec_data); } + if (vgmstream->coding_type == coding_TAC) { + free_tac(vgmstream->codec_data); + } + if (vgmstream->coding_type == coding_UBI_ADPCM) { free_ubi_adpcm(vgmstream->codec_data); } @@ -129,6 +133,10 @@ void seek_codec(VGMSTREAM* vgmstream) { loop_hca(vgmstream->codec_data, vgmstream->loop_current_sample); } + if (vgmstream->coding_type == coding_TAC) { + seek_tac(vgmstream->codec_data, vgmstream->loop_current_sample); + } + if (vgmstream->coding_type == coding_UBI_ADPCM) { seek_ubi_adpcm(vgmstream->codec_data, vgmstream->loop_current_sample); } @@ -231,6 +239,10 @@ void reset_codec(VGMSTREAM* vgmstream) { reset_hca(vgmstream->codec_data); } + if (vgmstream->coding_type == coding_TAC) { + reset_tac(vgmstream->codec_data); + } + if (vgmstream->coding_type == coding_UBI_ADPCM) { reset_ubi_adpcm(vgmstream->codec_data); } @@ -427,6 +439,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) { case coding_XA: return 28*8 / vgmstream->channels; /* 8 subframes per frame, mono/stereo */ + case coding_XA8: + return 28*4 / vgmstream->channels; /* 4 subframes per frame, mono/stereo */ case coding_PSX: case coding_PSX_badflags: case coding_HEVAG: @@ -509,6 +523,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) { return 0; /* 512 */ case coding_CRI_HCA: return 0; /* 1024 - delay/padding (which can be bigger than 1024) */ + case coding_TAC: + return 0; /* 1024 - delay/padding */ #if defined(VGM_USE_MP4V2) && defined(VGM_USE_FDKAAC) case coding_MP4_AAC: return ((mp4_aac_codec_data*)vgmstream->codec_data)->samples_per_frame; @@ -635,6 +651,7 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) { return 0x00; /* variable (block-controlled) */ case coding_XA: + case coding_XA8: return 0x80; case coding_PSX: case coding_PSX_badflags: @@ -705,27 +722,16 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) { return vgmstream->interleave_block_size; case coding_PTADPCM: return vgmstream->interleave_block_size; - case coding_UBI_ADPCM: - return 0; /* varies per mode? */ - case coding_IMUSE: - return 0; /* varies per frame */ - case coding_COMPRESSWAVE: - return 0; /* huffman bits */ - case coding_EA_MT: - return 0; /* variable (frames of bit counts or PCM frames) */ -#ifdef VGM_USE_ATRAC9 - case coding_ATRAC9: - return 0; /* varies with config data, usually 0x100-200 */ -#endif -#ifdef VGM_USE_CELT - case coding_CELT_FSB: - return 0; /* varies, usually 0x80-100 */ -#endif -#ifdef VGM_USE_SPEEX - case coding_SPEEX: - return 0; /* varies, usually 0x40-60 */ -#endif - default: /* Vorbis, MPEG, ACM, etc */ + /* UBI_ADPCM: varies per mode? */ + /* IMUSE: VBR */ + /* EA_MT: VBR, frames of bit counts or PCM frames */ + /* COMPRESSWAVE: VBR/huffman bits */ + /* ATRAC9: CBR around 0x100-200 */ + /* CELT FSB: varies, usually 0x80-100 */ + /* SPEEX: varies, usually 0x40-60 */ + /* TAC: VBR around ~0x200-300 */ + /* Vorbis, MPEG, ACM, etc: varies */ + default: /* (VBR or managed by decoder) */ return 0; } } @@ -982,11 +988,14 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_ } break; case coding_XA: + case coding_XA8: { + int is_xa8 = (vgmstream->coding_type == coding_XA8); for (ch = 0; ch < vgmstream->channels; ch++) { decode_xa(&vgmstream->ch[ch], buffer+ch, - vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch); + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, is_xa8); } break; + } case coding_EA_XA: case coding_EA_XA_int: { int is_stereo = (vgmstream->channels > 1 && vgmstream->coding_type == coding_EA_XA); @@ -1038,6 +1047,9 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_ case coding_CRI_HCA: decode_hca(vgmstream->codec_data, buffer, samples_to_do); break; + case coding_TAC: + decode_tac(vgmstream, buffer, samples_to_do); + break; #ifdef VGM_USE_FFMPEG case coding_FFmpeg: decode_ffmpeg(vgmstream, buffer, samples_to_do, vgmstream->channels); diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index a9e4a88e0..95beabb0b 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -194,6 +194,7 @@ static const char* extension_list[] = { "genh", "gin", "gms", + "grn", "gsb", "gsf", "gtd", @@ -250,6 +251,7 @@ static const char* extension_list[] = { "kcey", //fake extension/header id for .pcm (renamed, to be removed) "km9", "kovs", //fake extension/header id for .kvs + "kno", "kns", "kraw", "ktac", @@ -717,6 +719,7 @@ static const coding_info coding_info_list[] = { {coding_G721, "CCITT G.721 4-bit ADPCM"}, {coding_XA, "CD-ROM XA 4-bit ADPCM"}, + {coding_XA8, "CD-ROM XA 8-bit ADPCM"}, {coding_PSX, "Playstation 4-bit ADPCM"}, {coding_PSX_badflags, "Playstation 4-bit ADPCM (bad flags)"}, {coding_PSX_cfg, "Playstation 4-bit ADPCM (configurable)"}, @@ -803,7 +806,8 @@ static const coding_info coding_info_list[] = { {coding_CIRCUS_VQ, "Circus VQ"}, {coding_RELIC, "Relic Codec"}, {coding_CRI_HCA, "CRI HCA"}, - + {coding_TAC, "tri-Ace Codec"}, + #ifdef VGM_USE_VORBIS {coding_OGG_VORBIS, "Ogg Vorbis"}, {coding_VORBIS_custom, "Custom Vorbis"}, @@ -1338,6 +1342,7 @@ static const meta_info meta_info_list[] = { {meta_KTAC, "Koei Tecmo KTAC header"}, {meta_MJB_MJH, "Sony MultiStream MJH+MJB header"}, {meta_BSNF, "id Software BSNF header"}, + {meta_TAC, "tri-Ace Codec header"}, }; void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_xa.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xa.c index 8e36d390e..60b128968 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_xa.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_xa.c @@ -3,12 +3,12 @@ #include "../vgmstream.h" /* parse a CD-XA raw mode2/form2 sector */ -void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) { - STREAMFILE* streamFile = vgmstream->ch[0].streamfile; +void block_update_xa(off_t block_offset, VGMSTREAM* vgmstream) { + STREAMFILE* sf = vgmstream->ch[0].streamfile; int i, is_audio; size_t block_samples; uint16_t xa_config, target_config; - uint8_t xa_submode; + uint8_t xa_submode, xa_header; /* XA mode2/form2 sector, size 0x930 @@ -22,8 +22,29 @@ void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) { * 0x930: end * Sectors with no data may exist near other with data */ - xa_config = read_u16be(block_offset + 0x10, streamFile); - target_config = vgmstream->codec_config; + xa_config = read_u16be(block_offset + 0x10, sf); /* file + channel */ + + /* submode flag bits (typical audio value = 0x64 01100100) + * - 7 (0x80 10000000): end of file (usually at last sector of a channel or at data end) + * - 6 (0x40 01000000): real time sector (special control flag) + * - 5 (0x20 00100000): sector form (0=form1, 1=form2) + * - 4 (0x10 00010000): trigger (generates interrupt for the application) + * - 3 (0x08 00001000): data sector + * - 2 (0x04 00000100): audio sector + * - 1 (0x02 00000010): video sector + * - 0 (0x01 00000001): end of audio (optional for non-real time XAs) + * Empty sectors with no flags may exist interleaved with other with audio/data. + */ + xa_submode = read_u8(block_offset + 0x12, sf); + + /* header bits: + * - 7 (0x80 10000000): reserved + * - 6 (0x40 01000000): emphasis (applies filter, same as CD-DA emphasis) + * - 4 (0x30 00110000): bits per sample (0=4-bit, 1=8-bit) + * - 2 (0x0C 00001100): sample rate (0=37.8hz, 1=18.9hz) + * - 0 (0x03 00000011): channels (0=mono, 1=stereo) + */ + xa_header = read_u8(block_offset + 0x13, sf); /* Sector subheader's file+channel markers are used to interleave streams (music/sfx/voices) * by reading one target file+channel while ignoring the rest. This is needed to adjust @@ -38,36 +59,23 @@ void block_update_xa(off_t block_offset, VGMSTREAM * vgmstream) { * Extractors deinterleave and split .xa using file + channel + EOF flags. * 'Channel' here doesn't mean "audio channel", just a fancy name for substreams (mono or stereo). * Files can go up to 255, normally file 0=sequential, 1+=interleaved */ - - - /* submode flag bits (typical audio value = 0x64 01100100) - * - 7 (0x80 10000000): end of file (usually at last sector of a channel or at data end) - * - 6 (0x40 01000000): real time sector (special control flag) - * - 5 (0x20 00100000): sector form (0=form1, 1=form2) - * - 4 (0x10 00010000): trigger (generates interrupt for the application) - * - 3 (0x08 00001000): data sector - * - 2 (0x04 00000100): audio sector - * - 1 (0x02 00000010): video sector - * - 0 (0x01 00000001): end of audio (optional for non-real time XAs) - * Empty sectors with no flags may exist interleaved with other with audio/data. - */ - xa_submode = read_u8(block_offset + 0x12,streamFile); + target_config = vgmstream->codec_config; /* audio sector must set/not set certain flags, as per spec (in theory form2 only too) */ is_audio = !(xa_submode & 0x08) && (xa_submode & 0x04) && !(xa_submode & 0x02); - if (xa_config != target_config) { block_samples = 0; /* not a target sector */ } else if (is_audio) { + int subframes = ((xa_header >> 4) & 3) == 1 ? 4 : 8; /* 8-bit mode = 4 subframes */ if (xa_submode & 0x20) { - /* form2 audio: size 0x900, 18 frames of size 0x80 with 8 subframes of 28 samples */ - block_samples = (28*8 / vgmstream->channels) * 18; + /* form2 audio: size 0x900, 18 frames of size 0x80 with N subframes of 28 samples */ + block_samples = (28*subframes / vgmstream->channels) * 18; } else { /* rare, found with empty audio [Glint Glitters (PS1), Dance! Dance! Dance! (PS1)] */ - /* form1 audio: size 0x800, 16 frames of size 0x80 with 8 subframes of 28 samples (rest is garbage/other data) */ - block_samples = (28*8 / vgmstream->channels) * 16; + /* form1 audio: size 0x800, 16 frames of size 0x80 with N subframes of 28 samples (rest is garbage/other data) */ + block_samples = (28*subframes / vgmstream->channels) * 16; } } else { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx.c b/Frameworks/vgmstream/vgmstream/src/meta/adx.c index 35d721b52..111e10d03 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx.c @@ -11,11 +11,15 @@ #define ADX_KEY_MAX_TEST_FRAMES 32768 #define ADX_KEY_TEST_BUFFER_SIZE 0x8000 -static int find_adx_key(STREAMFILE *sf, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add); +static int find_adx_key(STREAMFILE *sf, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add, uint16_t subkey); + +VGMSTREAM* init_vgmstream_adx(STREAMFILE* sf) { + return init_vgmstream_adx_subkey(sf, 0); +} /* ADX - CRI Middleware format */ -VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey) { + VGMSTREAM* vgmstream = NULL; off_t start_offset, hist_offset = 0; int loop_flag = 0, channel_count; int32_t loop_start_sample = 0, loop_end_sample = 0; @@ -32,18 +36,18 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { /* checks*/ /* .adx: standard * .adp: Headhunter (DC) */ - if (!check_extensions(streamFile,"adx,adp")) + if (!check_extensions(sf,"adx,adp")) goto fail; - if ((uint16_t)read_16bitBE(0x00,streamFile) != 0x8000) + if (read_u16be(0x00,sf) != 0x8000) goto fail; - start_offset = (uint16_t)read_16bitBE(0x02,streamFile) + 0x04; - if ((uint16_t)read_16bitBE(start_offset - 0x06,streamFile) != 0x2863 || /* "(c" */ - (uint32_t)read_32bitBE(start_offset - 0x04,streamFile) != 0x29435249) /* ")CRI" */ + start_offset = read_u16be(0x02,sf) + 0x04; + if (read_u16be(start_offset - 0x06,sf) != 0x2863 || /* "(c" */ + read_u32be(start_offset - 0x04,sf) != 0x29435249) /* ")CRI" */ goto fail; - encoding_type = read_8bit(0x04, streamFile); + encoding_type = read_u8(0x04, sf); switch (encoding_type) { case 0x02: coding_type = coding_CRI_ADX_fixed; @@ -60,29 +64,29 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { /* ADX encoders can't set this value, but is honored by ADXPlay if changed and multiple of 0x12, * though output is unusual and may not be fully supported (works in mono so not an interleave) */ - frame_size = read_8bit(0x05, streamFile); + frame_size = read_8bit(0x05, sf); - if (read_8bit(0x06,streamFile) != 4) /* bits per sample */ + if (read_u8(0x06,sf) != 4) /* bits per sample */ goto fail; /* older ADX (adxencd) up to 2ch, newer ADX (criatomencd) up to 8 */ - channel_count = read_8bit(0x07,streamFile); + channel_count = read_u8(0x07,sf); /* 0x08: sample rate */ /* 0x0c: samples */ /* 0x10: high-pass frequency */ - version = read_16bitBE(0x12,streamFile); + version = read_u16be(0x12,sf); /* encryption */ if (version == 0x0408) { - if (find_adx_key(streamFile, 8, &xor_start, &xor_mult, &xor_add)) { + if (find_adx_key(sf, 8, &xor_start, &xor_mult, &xor_add, 0)) { coding_type = coding_CRI_ADX_enc_8; version = 0x0400; } VGM_ASSERT(version != 0x0400, "ADX: keystring not found\n"); } else if (version == 0x0409) { - if (find_adx_key(streamFile, 9, &xor_start, &xor_mult, &xor_add)) { + if (find_adx_key(sf, 9, &xor_start, &xor_mult, &xor_add, subkey)) { coding_type = coding_CRI_ADX_enc_9; version = 0x0400; } @@ -103,11 +107,11 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { /* 0x00 (2): initial loop padding (the encoder adds a few blank samples so loop start is block-aligned; max 31) * ex. loop_start=12: enc_start=32, padding=20 (32-20=12); loop_start=35: enc_start=64, padding=29 (64-29=35) * 0x02 (2): loop sample(?) flag (always 1) */ - loop_flag = read_32bitBE(loops_offset+0x04,streamFile) != 0; /* loop offset(?) flag (always 1) */ - loop_start_sample = read_32bitBE(loops_offset+0x08,streamFile); - //loop_start_offset = read_32bitBE(loops_offset+0x0c,streamFile); - loop_end_sample = read_32bitBE(loops_offset+0x10,streamFile); - //loop_end_offset = read_32bitBE(loops_offset+0x14,streamFile); + loop_flag = read_32bitBE(loops_offset+0x04,sf) != 0; /* loop offset(?) flag (always 1) */ + loop_start_sample = read_32bitBE(loops_offset+0x08,sf); + //loop_start_offset = read_32bitBE(loops_offset+0x0c,sf); + loop_end_sample = read_32bitBE(loops_offset+0x10,sf); + //loop_end_offset = read_32bitBE(loops_offset+0x14,sf); } } else if (version == 0x0400) { /* common */ @@ -120,8 +124,8 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { hist_size = (channel_count > 1 ? 0x04 * channel_count : 0x04 + 0x04); /* min is 8, even in 1ch files */ ainf_offset = base_size + hist_size + 0x04; /* not seen with >2ch though */ - if ((uint32_t)read_32bitBE(ainf_offset+0x00,streamFile) == 0x41494E46) /* "AINF" */ - ainf_size = read_32bitBE(ainf_offset+0x04,streamFile); + if (read_u32be(ainf_offset+0x00,sf) == 0x41494E46) /* "AINF" */ + ainf_size = read_32bitBE(ainf_offset+0x04,sf); if (start_offset - ainf_size - 0x06 >= hist_offset + hist_size + loops_size) { /* enough space for loop info? */ off_t loops_offset = base_size + hist_size; @@ -129,11 +133,11 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { /* 0x00 (2): initial loop padding (the encoder adds a few blank samples so loop start is block-aligned; max 31) * ex. loop_start=12: enc_start=32, padding=20 (32-20=12); loop_start=35: enc_start=64, padding=29 (64-29=35) * 0x02 (2): loop sample(?) flag (always 1) */ - loop_flag = read_32bitBE(loops_offset+0x04,streamFile) != 0; /* loop offset(?) flag (always 1) */ - loop_start_sample = read_32bitBE(loops_offset+0x08,streamFile); - //loop_start_offset = read_32bitBE(loops_offset+0x0c,streamFile); - loop_end_sample = read_32bitBE(loops_offset+0x10,streamFile); - //loop_end_offset = read_32bitBE(loops_offset+0x14,streamFile); + loop_flag = read_32bitBE(loops_offset+0x04,sf) != 0; /* loop offset(?) flag (always 1) */ + loop_start_sample = read_32bitBE(loops_offset+0x08,sf); + //loop_start_offset = read_32bitBE(loops_offset+0x0c,sf); + loop_end_sample = read_32bitBE(loops_offset+0x10,sf); + //loop_end_offset = read_32bitBE(loops_offset+0x14,sf); } /* AINF header info (may be inserted by CRI's tools but is rarely used) @@ -165,8 +169,8 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { vgmstream = allocate_vgmstream(channel_count,loop_flag); if (!vgmstream) goto fail; - vgmstream->sample_rate = read_32bitBE(0x08,streamFile); - vgmstream->num_samples = read_32bitBE(0x0c,streamFile); + vgmstream->sample_rate = read_32bitBE(0x08,sf); + vgmstream->num_samples = read_32bitBE(0x0c,sf); vgmstream->loop_start_sample = loop_start_sample; vgmstream->loop_end_sample = loop_end_sample; @@ -195,7 +199,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { double x,y,z,a,b,c; int i; /* high-pass cutoff frequency, always 500 that I've seen */ - uint16_t cutoff = (uint16_t)read_16bitBE(0x10,streamFile); + uint16_t cutoff = read_u16be(0x10,sf); x = cutoff; y = vgmstream->sample_rate; @@ -222,8 +226,8 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { /* 2 hist shorts per ch, corresponding to the very first original sample repeated (verified with CRI's encoders). * Not vital as their effect is small, after a few samples they don't matter, and most songs start in silence. */ if (hist_offset) { - vgmstream->ch[i].adpcm_history1_32 = read_16bitBE(hist_offset + i*4 + 0x00,streamFile); - vgmstream->ch[i].adpcm_history2_32 = read_16bitBE(hist_offset + i*4 + 0x02,streamFile); + vgmstream->ch[i].adpcm_history1_32 = read_16bitBE(hist_offset + i*4 + 0x00,sf); + vgmstream->ch[i].adpcm_history2_32 = read_16bitBE(hist_offset + i*4 + 0x02,sf); } if (coding_type == coding_CRI_ADX_enc_8 || coding_type == coding_CRI_ADX_enc_9) { @@ -240,7 +244,7 @@ VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile) { } - if ( !vgmstream_open_stream(vgmstream,streamFile,start_offset) ) + if ( !vgmstream_open_stream(vgmstream, sf, start_offset) ) goto fail; return vgmstream; @@ -252,7 +256,7 @@ fail: /* ADX key detection works by reading XORed ADPCM scales in frames, and un-XORing with keys in * a list. If resulting values are within the expected range for N scales we accept that key. */ -static int find_adx_key(STREAMFILE *sf, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add) { +static int find_adx_key(STREAMFILE* sf, uint8_t type, uint16_t *xor_start, uint16_t *xor_mult, uint16_t *xor_add, uint16_t subkey) { const int frame_size = 0x12; uint16_t *scales = NULL; uint16_t *prescales = NULL; @@ -295,7 +299,17 @@ static int find_adx_key(STREAMFILE *sf, uint8_t type, uint16_t *xor_start, uint1 return 1; } else if (type == 9 && key_size == 0x08) { - uint64_t keycode = (uint64_t)get_64bitBE(keybuf); + uint64_t keycode = get_u64be(keybuf); + if (subkey) { + keycode = keycode * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) ); + } + derive_adx_key9(keycode, xor_start, xor_mult, xor_add); + return 1; + } + else if (type == 9 && key_size == 0x08+0x02) { + uint64_t file_key = get_u64be(keybuf+0x00); + uint16_t file_sub = get_u16be(keybuf+0x08); + uint64_t keycode = file_key * ( ((uint64_t)file_sub << 16u) | ((uint16_t)~file_sub + 2u) ); derive_adx_key9(keycode, xor_start, xor_mult, xor_add); return 1; } @@ -414,7 +428,11 @@ static int find_adx_key(STREAMFILE *sf, uint8_t type, uint16_t *xor_start, uint1 derive_adx_key8(keys[key_id].key8, &key_xor, &key_mul, &key_add); } else if (type == 9 && keys[key_id].key9) { - derive_adx_key9(keys[key_id].key9, &key_xor, &key_mul, &key_add); + uint64_t keycode = keys[key_id].key9; + if (subkey) { + keycode = keycode * ( ((uint64_t)subkey << 16u) | ((uint16_t)~subkey + 2u) ); + } + derive_adx_key9(keycode, &key_xor, &key_mul, &key_add); } else { VGM_LOG("ADX: incorrectly defined key id=%i\n", key_id); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h index 911e322c4..af33f5c5e 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h @@ -244,7 +244,7 @@ static const adxkey_info adxkey9_list[] = { {0x0000,0x1c85,0x7043, NULL,29915170}, // 0000000001C87822 /* Assault Lily Last Bullet (Android) */ - {0x0aca,0x0ef5,0x05c9, NULL,0}, // guessed with VGAudio (possible key: 5650EF42E5 / 370725044965) + {0x0000,0x0000,0x0000, NULL,6349046567469313}, // 00168E6C99510101 (+ AWB subkeys) }; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awb.c b/Frameworks/vgmstream/vgmstream/src/meta/awb.c index 29a091aa3..5d3293d94 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/awb.c @@ -135,7 +135,7 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) { if (!vgmstream) goto fail; break; case ADX: /* Okami HD (PS4) */ - vgmstream = init_vgmstream_adx(temp_sf); + vgmstream = init_vgmstream_adx_subkey(temp_sf, subkey); if (!vgmstream) goto fail; break; case VAG: /* Ukiyo no Roushi (Vita) */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index 57e46983f..0f8aa0161 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -388,6 +388,9 @@ static const hcakey_info hcakey_list[] = { /* Assault Lily Last Bullet (Android) */ {6349046567469313}, // 00168E6C99510101 + /* Sakura Kakumei (iOS/Android) */ + {382759}, // 000000000005D727 + /* Dragalia Lost (iOS/Android) */ {2967411924141, subkeys_dgl, sizeof(subkeys_dgl) / sizeof(subkeys_dgl[0]) }, // 000002B2E7889CAD diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktss.c b/Frameworks/vgmstream/vgmstream/src/meta/ktss.c index 56b93fba1..745cb46fb 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ktss.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktss.c @@ -1,44 +1,45 @@ #include "meta.h" #include "../coding/coding.h" -VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - int loop_flag, channel_count, sample_rate; - int8_t version, num_layers, codec_id; +VGMSTREAM* init_vgmstream_ktss(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + int loop_flag, channels, sample_rate; + int8_t version, tracks, codec_id; int32_t num_samples, loop_start, loop_length, coef_offset, coef_spacing; off_t start_offset; size_t data_size, skip = 0; - if (!check_extensions(streamFile, "kns,ktss")) + /* checks */ + /* .kns: Atelier Lydie & Suelle: The Alchemists and the Mysterious Paintings (Switch) + * .kno: Ciel Nosurge DX (Switch) + * .ktss: header id */ + if (!check_extensions(sf, "kns,kno,ktss")) goto fail; - if (read_32bitBE(0x00, streamFile) != 0x4B545353) /* "KTSS" */ + if (!is_id32be(0x00,sf, "KTSS")) goto fail; /* 0x04: data size */ - codec_id = read_8bit(0x20, streamFile); + codec_id = read_u8(0x20, sf); /* 0x01: null, part of codec? */ - version = read_8bit(0x22, streamFile); + version = read_u8(0x22, sf); /* 0x03: same as version? */ - start_offset = read_32bitLE(0x24, streamFile) + 0x20; + start_offset = read_u32le(0x24, sf) + 0x20; - // A layered stream/track model seems to be used in Hyrule Warriors (Switch). - // It's also present in other Koei Tecmo KNS but the channel count was always - // explicitly defined in the 0x29 byte and the number of layers was set to 1. - // Here, 10 channel files are set up with 2 channels in 5 layers. - // Super hacky on KT's part and ours to implement but it works. - num_layers = read_8bit(0x28, streamFile); - channel_count = read_8bit(0x29, streamFile) * num_layers; - sample_rate = read_32bitLE(0x2c, streamFile); + /* Layered tracks used in Hyrule Warriors (Switch), like 2*5 = 10 channels + * Other games typically use 1 track. */ + tracks = read_u8(0x28, sf); + channels = read_u8(0x29, sf) * tracks; + sample_rate = read_u32le(0x2c, sf); - num_samples = read_32bitLE(0x30, streamFile); - loop_start = read_32bitLE(0x34, streamFile); - loop_length = read_32bitLE(0x38, streamFile); + num_samples = read_s32le(0x30, sf); + loop_start = read_s32le(0x34, sf); + loop_length = read_s32le(0x38, sf); loop_flag = loop_length > 0; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; vgmstream->sample_rate = sample_rate; @@ -64,32 +65,32 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) { vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x8; - dsp_read_coefs_le(vgmstream, streamFile, coef_offset, coef_spacing); + dsp_read_coefs_le(vgmstream, sf, coef_offset, coef_spacing); break; #ifdef VGM_USE_FFMPEG case 0x9: { /* Opus - Dead or Alive Xtreme 3: Scarlet, Fire Emblem: Three Houses */ opus_config cfg = {0}; - start_offset = read_32bitLE(0x40, streamFile); /* after seek table, if any */ - data_size = read_32bitLE(0x44, streamFile); + start_offset = read_u32le(0x40, sf); /* after seek table, if any */ + data_size = read_u32le(0x44, sf); /* 0x48: seek table start (0 if no seek table) */ /* 0x4c: number of frames */ /* 0x50: frame size, or 0 if VBR */ /* 0x52: samples per frame */ /* 0x54: sample rate */ - cfg.skip = read_32bitLE(0x58, streamFile); + cfg.skip = read_s32le(0x58, sf); cfg.sample_rate = vgmstream->sample_rate; cfg.channels = vgmstream->channels; /* this info seems always included even for stereo streams */ if (vgmstream->channels <= 8) { int i; - cfg.stream_count = read_8bit(0x5a,streamFile); - cfg.coupled_count = read_8bit(0x5b,streamFile); + cfg.stream_count = read_u8(0x5a,sf); + cfg.coupled_count = read_u8(0x5b,sf); for (i = 0; i < vgmstream->channels; i++) { - cfg.channel_mapping[i] = read_8bit(0x5c + i,streamFile); + cfg.channel_mapping[i] = read_u8(0x5c + i,sf); } } @@ -97,7 +98,7 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) { /* 0x70: seek table (0x02 * frames) if VBR */ /* later games use VBR frames, hence the seek table [Warriors Orochi 4 Ultimate DLC (Switch)] */ - vgmstream->codec_data = init_ffmpeg_switch_opus_config(streamFile, start_offset, data_size, &cfg); + vgmstream->codec_data = init_ffmpeg_switch_opus_config(sf, start_offset, data_size, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -115,7 +116,7 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) { break; } if (vgmstream->num_samples == 0) { - vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, streamFile) - skip; + vgmstream->num_samples = switch_opus_get_samples(start_offset, data_size, sf) - skip; } break; } @@ -124,7 +125,7 @@ VGMSTREAM * init_vgmstream_ktss(STREAMFILE *streamFile) { goto fail; } - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 77f259f79..7506315f0 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -6,7 +6,8 @@ VGMSTREAM* init_vgmstream_silence(int channels, int sample_rate, int32_t num_samples); -VGMSTREAM * init_vgmstream_adx(STREAMFILE *streamFile); +VGMSTREAM* init_vgmstream_adx(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_adx_subkey(STREAMFILE* sf, uint16_t subkey); VGMSTREAM * init_vgmstream_afc(STREAMFILE *streamFile); @@ -944,4 +945,6 @@ VGMSTREAM* init_vgmstream_ktac(STREAMFILE* sf); VGMSTREAM* init_vgmstream_mjb_mjh(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_tac(STREAMFILE* sf); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nub.c b/Frameworks/vgmstream/vgmstream/src/meta/nub.c index 77bf09163..4794edfd5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nub.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nub.c @@ -2,18 +2,18 @@ #include "../coding/coding.h" -static void load_name(char *name, size_t name_size, STREAMFILE *sf, int big_endian, int total_subsongs, int target_subsong); -static STREAMFILE* setup_nub_streamfile(STREAMFILE *sf, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char *fake_ext); +static void load_name(char* name, size_t name_size, STREAMFILE* sf, int big_endian, int total_subsongs, int target_subsong); +static STREAMFILE* setup_nub_streamfile(STREAMFILE *sf, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char* fake_ext); /* .nub - Namco's nuSound2 audio container */ -VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *temp_sf = NULL; +VGMSTREAM* init_vgmstream_nub(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; int big_endian; - int total_subsongs, target_subsong = streamFile->stream_index; + int total_subsongs, target_subsong = sf->stream_index; uint32_t version, codec; const char* fake_ext; - VGMSTREAM*(*init_vgmstream_function)(STREAMFILE *) = NULL; + VGMSTREAM*(*init_vgmstream_function)(STREAMFILE*) = NULL; char name[STREAM_NAME_SIZE] = {0}; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; @@ -21,19 +21,19 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) { /* checks */ /* .nub: standard * .nub2: rare [iDOLM@STER - Gravure For You (PS3)] */ - if (!check_extensions(streamFile, "nub,nub2")) + if (!check_extensions(sf, "nub,nub2")) goto fail; - version = read_32bitBE(0x00,streamFile); + version = read_32bitBE(0x00,sf); if (version != 0x00020000 && /* v2.0 (rare, ex. Ridge Race 6 (X360)) */ version != 0x00020100 && /* v2.1 (common) */ version != 0x01020100) /* same but LE (seen in PSP/PC games, except PS4) */ goto fail; - if (read_32bitBE(0x04,streamFile) != 0x00000000) /* null */ + if (read_32bitBE(0x04,sf) != 0x00000000) /* null */ goto fail; /* sometimes LE [Soul Calibur: Broken Destiny (PSP), Tales of Vesperia (PS4) */ - big_endian = guess_endianness32bit(0x10, streamFile); + big_endian = guess_endianness32bit(0x18, sf); if (big_endian) { read_32bit = read_32bitBE; } else{ @@ -48,12 +48,13 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) { /* - base header */ /* 0x08: file id (0 = first) */ - total_subsongs = read_32bit(0x0c, streamFile); /* .nub with 0 files do exist, and with 1 song only too */ - data_start = read_32bit(0x10, streamFile); /* exists even with 0 files */ + total_subsongs = read_32bit(0x0c, sf); /* .nub with 0 files do exist, and with 1 song only too */ + data_start = read_32bit(0x10, sf); /* exists even with 0 files */ /* 0x14: data end (may have padding) */ - header_start = read_32bit(0x18, streamFile); + header_start = read_32bit(0x18, sf); /* exists even with 0 files */ /* 0x1c: header end */ + /* probably means "header end" in v2.0 */ if (version == 0x00020000) { data_start = align_size_to_block(data_start, 0x800); @@ -62,7 +63,7 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) { if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; - offset = read_32bit(header_start + (target_subsong-1)*0x04, streamFile); + offset = read_32bit(header_start + (target_subsong-1)*0x04, sf); /* .nus have all headers first then all data, but extractors often just paste them together, * so we'll combine header+data on the fly to make them playable with existing parsers. @@ -79,11 +80,11 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) { /* - tone header */ /* 0x00: config? */ /* 0x04: header id/number */ - codec = (uint32_t)read_32bit(offset + 0x08, streamFile); + codec = (uint32_t)read_32bit(offset + 0x08, sf); /* 0x0c: null */ - stream_size = read_32bit(offset + 0x10, streamFile); /* 0x10 aligned */ - stream_offset = read_32bit(offset + 0x14, streamFile) + data_start; - subheader_size = read_32bit(offset + 0x18, streamFile); + stream_size = read_32bit(offset + 0x10, sf); /* 0x10 aligned */ + stream_offset = read_32bit(offset + 0x14, sf) + data_start; + subheader_size = read_32bit(offset + 0x18, sf); /* 0x1c: extra info, size 0x10 (meaning varies but usually loop points) */ /* rest until sub-header start looks like config/volumes/pan/etc in various floats */ @@ -142,13 +143,13 @@ VGMSTREAM * init_vgmstream_nub(STREAMFILE *streamFile) { //;VGM_LOG("NUB: subfile header=%lx + %x, offset=%lx + %x\n", header_offset, header_size, stream_offset, stream_size); - temp_sf = setup_nub_streamfile(streamFile, header_offset, header_size, stream_offset, stream_size, fake_ext); + temp_sf = setup_nub_streamfile(sf, header_offset, header_size, stream_offset, stream_size, fake_ext); if (!temp_sf) goto fail; } /* get names from companion file, rarely [Noby Noby Boy (PS3), Wangan Midnight Maximum Tune (AC)] */ - load_name(name, sizeof(name), streamFile, big_endian, total_subsongs, target_subsong); + load_name(name, sizeof(name), sf, big_endian, total_subsongs, target_subsong); /* init the VGMSTREAM */ @@ -171,7 +172,7 @@ fail: /* *********************************************************** */ -static void load_name(char *name, size_t name_size, STREAMFILE *sf, int big_endian, int total_subsongs, int target_subsong) { +static void load_name(char* name, size_t name_size, STREAMFILE* sf, int big_endian, int total_subsongs, int target_subsong) { STREAMFILE *sf_names = NULL; char filename[PATH_LIMIT]; char basename[255]; @@ -215,9 +216,9 @@ done: } -static STREAMFILE* setup_nub_streamfile(STREAMFILE *sf, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char *fake_ext) { - STREAMFILE *new_sf = NULL; - STREAMFILE *multi_sf[2] = {0}; +static STREAMFILE* setup_nub_streamfile(STREAMFILE* sf, off_t header_offset, size_t header_size, off_t stream_offset, size_t stream_size, const char* fake_ext) { + STREAMFILE* new_sf = NULL; + STREAMFILE* multi_sf[2] = {0}; multi_sf[0] = open_wrap_streamfile(sf); multi_sf[0] = open_clamp_streamfile_f(multi_sf[0], header_offset, header_size); @@ -233,8 +234,8 @@ static STREAMFILE* setup_nub_streamfile(STREAMFILE *sf, off_t header_offset, siz //todo could be simplified /* .nub wav - from Namco NUB archives [Ridge Racer 7 (PS3)] */ -VGMSTREAM * init_vgmstream_nub_wav(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_nub_wav(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; int loop_flag, channel_count, sample_rate; size_t data_size, loop_start, loop_length; @@ -243,12 +244,12 @@ VGMSTREAM * init_vgmstream_nub_wav(STREAMFILE *streamFile) { /* checks */ - if (!check_extensions(streamFile, "wav,lwav")) + if (!check_extensions(sf, "wav,lwav")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x77617600) /* "wav\0" "*/ + if (read_32bitBE(0x00,sf) != 0x77617600) /* "wav\0" "*/ goto fail; - if (guess_endianness32bit(0x1c, streamFile)) { + if (guess_endianness32bit(0x1c, sf)) { read_32bit = read_32bitBE; read_16bit = read_16bitBE; } else { @@ -256,18 +257,18 @@ VGMSTREAM * init_vgmstream_nub_wav(STREAMFILE *streamFile) { read_16bit = read_16bitLE; } - data_size = read_32bit(0x14,streamFile); + data_size = read_32bit(0x14,sf); /* info header */ - loop_start = read_32bit(0x20,streamFile); - loop_length = read_32bit(0x24,streamFile); - loop_flag = read_32bit(0x28,streamFile); + loop_start = read_32bit(0x20,sf); + loop_length = read_32bit(0x24,sf); + loop_flag = read_32bit(0x28,sf); /* 0x2c: null */ /* format header: mini "fmt" chunk */ - if (read_16bit(0xBC + 0x00, streamFile) != 0x0001) + if (read_16bit(0xBC + 0x00, sf) != 0x0001) goto fail; - channel_count = read_16bit(0xBC + 0x02,streamFile); - sample_rate = read_32bit(0xBC + 0x04,streamFile); + channel_count = read_16bit(0xBC + 0x02,sf); + sample_rate = read_32bit(0xBC + 0x04,sf); /* 0x08: bitrate */ /* 0x0c: block align/bps */ @@ -288,7 +289,7 @@ VGMSTREAM * init_vgmstream_nub_wav(STREAMFILE *streamFile) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x02; - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; @@ -298,7 +299,7 @@ fail: } /* .nub vag - from Namco NUB archives [Ridge Racer 7 (PS3), Noby Noby Boy (PS3)] */ -VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_nub_vag(STREAMFILE* sf) { VGMSTREAM * vgmstream = NULL; off_t start_offset; int loop_flag, channel_count, sample_rate; @@ -307,26 +308,26 @@ VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE *streamFile) { /* checks */ - if ( !check_extensions(streamFile, "vag")) + if ( !check_extensions(sf, "vag")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x76616700) /* "vag\0" */ + if (read_32bitBE(0x00,sf) != 0x76616700) /* "vag\0" */ goto fail; - if (guess_endianness32bit(0x1c, streamFile)) { + if (guess_endianness32bit(0x1c, sf)) { read_32bit = read_32bitBE; } else { read_32bit = read_32bitLE; } - data_size = read_32bit(0x14,streamFile); + data_size = read_32bit(0x14,sf); /* info header */ - loop_start = read_32bit(0x20,streamFile); - loop_length = read_32bit(0x24,streamFile); - loop_flag = read_32bit(0x28,streamFile); + loop_start = read_32bit(0x20,sf); + loop_length = read_32bit(0x24,sf); + loop_flag = read_32bit(0x28,sf); /* 0x2c: null */ /* format header */ - sample_rate = read_32bit(0xBC + 0x00,streamFile); + sample_rate = read_32bit(0xBC + 0x00,sf); channel_count = 1; start_offset = 0xC0; @@ -346,7 +347,7 @@ VGMSTREAM * init_vgmstream_nub_vag(STREAMFILE *streamFile) { vgmstream->layout_type = layout_none; vgmstream->allow_dual_stereo = 1; - if (!vgmstream_open_stream(vgmstream, streamFile, start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; @@ -356,17 +357,17 @@ fail: } /* .nub at3 - from Namco NUB archives [Ridge Racer 7 (PS3), Katamari Forever (PS3)] */ -VGMSTREAM * init_vgmstream_nub_at3(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - STREAMFILE *temp_sf = NULL; +VGMSTREAM* init_vgmstream_nub_at3(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; off_t subfile_offset = 0; size_t subfile_size = 0; /* checks */ - if (!check_extensions(streamFile,"at3")) + if (!check_extensions(sf,"at3")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x61743300) /* "at3\0" */ + if (read_32bitBE(0x00,sf) != 0x61743300) /* "at3\0" */ goto fail; /* info header */ @@ -379,9 +380,9 @@ VGMSTREAM * init_vgmstream_nub_at3(STREAMFILE *streamFile) { /* we can just ignore and use RIFF at data start since it has the same info */ subfile_offset = 0x100; - subfile_size = read_32bitLE(subfile_offset + 0x04, streamFile) + 0x08; /* RIFF size */ + subfile_size = read_32bitLE(subfile_offset + 0x04, sf) + 0x08; /* RIFF size */ - temp_sf = setup_subfile_streamfile(streamFile, subfile_offset,subfile_size, NULL); + temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, NULL); if (!temp_sf) goto fail; vgmstream = init_vgmstream_riff(temp_sf); @@ -397,8 +398,8 @@ fail: /* .nub xma - from Namco NUB archives [Ridge Racer 6 (X360), Tekken 6 (X360), Galaga Legions DX (X360)] */ -VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_nub_xma(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset, chunk_offset; size_t data_size, chunk_size, header_size; int loop_flag, channel_count, sample_rate, nus_codec; @@ -406,27 +407,27 @@ VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) { /* checks */ - if (!check_extensions(streamFile,"xma")) + if (!check_extensions(sf,"xma")) goto fail; - if (read_32bitBE(0x00,streamFile) == 0x786D6100) { /* "xma\0" */ + if (read_32bitBE(0x00,sf) == 0x786D6100) { /* "xma\0" */ /* nub v2.1 */ - nus_codec = read_32bitBE(0x0C,streamFile); - data_size = read_32bitBE(0x14,streamFile); - header_size = read_32bitBE(0x1c,streamFile); + nus_codec = read_32bitBE(0x0C,sf); + data_size = read_32bitBE(0x14,sf); + header_size = read_32bitBE(0x1c,sf); chunk_offset = 0xBC; /* info header */ /* 0x20: null */ - chunk_size = read_32bitBE(0x24,streamFile); + chunk_size = read_32bitBE(0x24,sf); /* 0x24: loop flag */ /* 0x20: null */ } - else if (read_32bitBE(0x08,streamFile) == 0 && read_32bitBE(0x0c,streamFile) == 0) { + else if (read_32bitBE(0x08,sf) == 0 && read_32bitBE(0x0c,sf) == 0) { /* nub v2.0 from Ridge Racer 6 */ - nus_codec = read_32bitBE(0x08,streamFile); - data_size = read_32bitBE(0x10,streamFile); - header_size = read_32bitBE(0x18,streamFile); + nus_codec = read_32bitBE(0x08,sf); + data_size = read_32bitBE(0x10,sf); + header_size = read_32bitBE(0x18,sf); chunk_offset = 0xAC; chunk_size = header_size; @@ -440,7 +441,7 @@ VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) { if (nus_codec == 0x00) { /* XMA1 "fmt " */ int loop_start_b, loop_end_b, loop_subframe; - xma1_parse_fmt_chunk(streamFile, chunk_offset, &channel_count,&sample_rate, &loop_flag, &loop_start_b, &loop_end_b, &loop_subframe, 1); + xma1_parse_fmt_chunk(sf, chunk_offset, &channel_count,&sample_rate, &loop_flag, &loop_start_b, &loop_end_b, &loop_subframe, 1); { ms_sample_data msd = {0}; @@ -456,7 +457,7 @@ VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) { msd.loop_end_subframe = loop_subframe >> 4; /* upper 4b: subframe where the loop ends, 0..3 */ msd.chunk_offset= chunk_offset; - xma_get_samples(&msd, streamFile); + xma_get_samples(&msd, sf); num_samples = msd.num_samples; loop_start_sample = msd.loop_start_sample; @@ -464,12 +465,12 @@ VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) { } } else if (nus_codec == 0x04) { /* "XMA2" */ - xma2_parse_xma2_chunk(streamFile, chunk_offset, &channel_count,&sample_rate, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample); + xma2_parse_xma2_chunk(sf, chunk_offset, &channel_count,&sample_rate, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample); } else if (nus_codec == 0x08) { /* XMA2 "fmt " */ - channel_count = read_16bitBE(chunk_offset+0x02,streamFile); - sample_rate = read_32bitBE(chunk_offset+0x04,streamFile); - xma2_parse_fmt_chunk_extra(streamFile, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, 1); + channel_count = read_16bitBE(chunk_offset+0x02,sf); + sample_rate = read_32bitBE(chunk_offset+0x04,sf); + xma2_parse_fmt_chunk_extra(sf, chunk_offset, &loop_flag, &num_samples, &loop_start_sample, &loop_end_sample, 1); } else { goto fail; @@ -492,22 +493,22 @@ VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile) { size_t bytes; if (nus_codec == 0x04) { - bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile); + bytes = ffmpeg_make_riff_xma2_from_xma2_chunk(buf,0x100, chunk_offset,chunk_size, data_size, sf); } else { - bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, streamFile, 1); + bytes = ffmpeg_make_riff_xma_from_fmt_chunk(buf,0x100, chunk_offset,chunk_size, data_size, sf, 1); } - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size); if ( !vgmstream->codec_data ) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; - xma_fix_raw_samples(vgmstream, streamFile, start_offset, data_size, chunk_offset, 1,1); /* samples needs adjustment */ + xma_fix_raw_samples(vgmstream, sf, start_offset, data_size, chunk_offset, 1,1); /* samples needs adjustment */ } #else goto fail; #endif - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + if ( !vgmstream_open_stream(vgmstream, sf, start_offset) ) goto fail; return vgmstream; @@ -517,26 +518,26 @@ fail: } /* .nub dsp - from Namco NUB archives [Taiko no Tatsujin Wii Chou Goukanban (Wii)] */ -VGMSTREAM * init_vgmstream_nub_dsp(STREAMFILE *streamFile) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *temp_sf = NULL; +VGMSTREAM* init_vgmstream_nub_dsp(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; off_t header_offset, stream_offset; size_t header_size, stream_size; /* checks */ - if (!check_extensions(streamFile,"dsp")) + if (!check_extensions(sf,"dsp")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x64737000) /* "dsp\0" */ + if (read_32bitBE(0x00,sf) != 0x64737000) /* "dsp\0" */ goto fail; /* paste header+data together and pass to meta, which has loop info too */ header_offset = 0xBC; - stream_size = read_32bitBE(0x14, streamFile); - header_size = read_32bitBE(0x1c, streamFile); + stream_size = read_32bitBE(0x14, sf); + header_size = read_32bitBE(0x1c, sf); stream_offset = align_size_to_block(header_offset + header_size, 0x10); - temp_sf = setup_nub_streamfile(streamFile, header_offset, header_size, stream_offset, stream_size, "dsp"); + temp_sf = setup_nub_streamfile(sf, header_offset, header_size, stream_offset, stream_size, "dsp"); if (!temp_sf) goto fail; vgmstream = init_vgmstream_ngc_dsp_std(temp_sf); @@ -551,17 +552,17 @@ fail: } /* .nub idsp - from Namco NUB archives [Soul Calibur Legends (Wii), Sky Crawlers: Innocent Aces (Wii)] */ -VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE *streamFile) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *temp_sf = NULL; +VGMSTREAM* init_vgmstream_nub_idsp(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; off_t header_offset, stream_offset; size_t header_size, stream_size; /* checks */ - if (!check_extensions(streamFile,"idsp")) + if (!check_extensions(sf,"idsp")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x69647370) /* "idsp" */ + if (read_32bitBE(0x00,sf) != 0x69647370) /* "idsp" */ goto fail; /* info header */ @@ -572,11 +573,11 @@ VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE *streamFile) { /* paste header+data together and pass to meta, which has loop info too */ header_offset = 0xBC; - stream_size = read_32bitBE(0x14, streamFile); - header_size = read_32bitBE(0x1c, streamFile); + stream_size = read_32bitBE(0x14, sf); + header_size = read_32bitBE(0x1c, sf); stream_offset = align_size_to_block(header_offset + header_size, 0x10); - temp_sf = setup_nub_streamfile(streamFile, header_offset, header_size, stream_offset, stream_size, "idsp"); + temp_sf = setup_nub_streamfile(sf, header_offset, header_size, stream_offset, stream_size, "idsp"); if (!temp_sf) goto fail; vgmstream = init_vgmstream_idsp_namco(temp_sf); @@ -591,21 +592,21 @@ fail: } /* .nub is14 - from Namco NUB archives [Tales of Vesperia (PS3), Mojipittan (Wii)] */ -VGMSTREAM * init_vgmstream_nub_is14(STREAMFILE *streamFile) { - VGMSTREAM *vgmstream = NULL; - STREAMFILE *temp_sf = NULL; +VGMSTREAM* init_vgmstream_nub_is14(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; off_t header_offset, stream_offset; size_t header_size, stream_size, sdat_size; int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; /* checks */ - if (!check_extensions(streamFile,"is14")) + if (!check_extensions(sf,"is14")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x69733134) /* "is14" */ + if (read_32bitBE(0x00,sf) != 0x69733134) /* "is14" */ goto fail; - if (guess_endianness32bit(0x1c, streamFile)) { + if (guess_endianness32bit(0x1c, sf)) { read_32bit = read_32bitBE; } else{ read_32bit = read_32bitLE; @@ -615,16 +616,16 @@ VGMSTREAM * init_vgmstream_nub_is14(STREAMFILE *streamFile) { /* paste header+data together and pass to meta */ header_offset = 0xBC; - header_size = read_32bit(0x1c, streamFile); + header_size = read_32bit(0x1c, sf); /* size at 0x14 is padded, find "sdat" size BE (may move around) */ - if (!find_chunk_riff_be(streamFile, 0x73646174, 0xbc+0x0c, header_size - 0x0c, NULL, &sdat_size)) + if (!find_chunk_riff_be(sf, 0x73646174, 0xbc+0x0c, header_size - 0x0c, NULL, &sdat_size)) goto fail; stream_offset = align_size_to_block(header_offset + header_size, 0x10); stream_size = sdat_size; - temp_sf = setup_nub_streamfile(streamFile, header_offset, header_size, stream_offset, stream_size, "bnsf"); + temp_sf = setup_nub_streamfile(sf, header_offset, header_size, stream_offset, stream_size, "bnsf"); if (!temp_sf) goto fail; vgmstream = init_vgmstream_bnsf(temp_sf); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c index 5cfebf283..969935a92 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ogg_vorbis.c @@ -82,6 +82,20 @@ static void rpgmvo_ogg_decryption_callback(void* ptr, size_t size, size_t nmemb, } } +static void at4_ogg_decryption_callback(void* ptr, size_t size, size_t nmemb, void* datasource) { + static const uint8_t af4_key[0x10] = { + 0x00,0x0E,0x08,0x1E, 0x18,0x37,0x12,0x00, 0x48,0x87,0x46,0x0B, 0x9C,0x68,0xA8,0x4B + }; + uint8_t *ptr8 = ptr; + size_t bytes_read = size * nmemb; + ogg_vorbis_io *io = datasource; + int i; + + for (i = 0; i < bytes_read; i++) { + ptr8[i] -= af4_key[(io->offset + i) % sizeof(af4_key)]; + } +} + static const uint32_t xiph_mappings[] = { 0, @@ -152,31 +166,34 @@ VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) { } if (is_ogg) { - if (read_32bitBE(0x00,sf) == 0x2c444430) { /* Psychic Software [Darkwind: War on Wheels (PC)] */ + if (read_u32be(0x00,sf) == 0x2c444430) { /* Psychic Software [Darkwind: War on Wheels (PC)] */ ovmi.decryption_callback = psychic_ogg_decryption_callback; } - else if (read_32bitBE(0x00,sf) == 0x4C325344) { /* "L2SD" instead of "OggS" [Lineage II Chronicle 4 (PC)] */ + else if (read_u32be(0x00,sf) == 0x4C325344) { /* "L2SD" instead of "OggS" [Lineage II Chronicle 4 (PC)] */ cfg.is_header_swap = 1; cfg.is_encrypted = 1; } - else if (read_32bitBE(0x00,sf) == 0x048686C5) { /* "OggS" XOR'ed + bitswapped [Ys VIII (PC)] */ + else if (read_u32be(0x00,sf) == 0x048686C5) { /* "OggS" XOR'ed + bitswapped [Ys VIII (PC)] */ cfg.key[0] = 0xF0; cfg.key_len = 1; cfg.is_nibble_swap = 1; cfg.is_encrypted = 1; } - else if (read_32bitBE(0x00,sf) == 0x00000000 && /* null instead of "OggS" [Yuppie Psycho (PC)] */ - read_32bitBE(0x3a,sf) == 0x4F676753) { /* "OggS" in next page */ + else if (read_u32be(0x00,sf) == 0x00000000 && /* null instead of "OggS" [Yuppie Psycho (PC)] */ + read_u32be(0x3a,sf) == 0x4F676753) { /* "OggS" in next page */ cfg.is_header_swap = 1; cfg.is_encrypted = 1; } - else if (read_32bitBE(0x00,sf) != 0x4F676753 && /* random(?) swap instead of "OggS" [Tobi Tsukihime (PC)] */ - read_32bitBE(0x3a,sf) == 0x4F676753) { /* "OggS" in next page */ + else if (read_u32be(0x00,sf) != 0x4F676753 && /* random(?) swap instead of "OggS" [Tobi Tsukihime (PC)] */ + read_u32be(0x3a,sf) == 0x4F676753) { /* "OggS" in next page */ cfg.is_header_swap = 1; cfg.is_encrypted = 1; } - else if (read_32bitBE(0x00,sf) == 0x4F676753) { /* "OggS" (standard) */ + else if (read_u32be(0x00,sf) == 0x4F756F71) { /* "OggS" encrypted [Adventure Field 4 (PC)]*/ + ovmi.decryption_callback = at4_ogg_decryption_callback; + } + else if (read_u32be(0x00,sf) == 0x4F676753) { /* "OggS" (standard) */ ; } else { @@ -185,13 +202,13 @@ VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) { } if (is_um3) { /* ["Ultramarine3" (???)] */ - if (read_32bitBE(0x00,sf) != 0x4f676753) { /* "OggS" (optionally encrypted) */ + if (read_u32be(0x00,sf) != 0x4f676753) { /* "OggS" (optionally encrypted) */ ovmi.decryption_callback = um3_ogg_decryption_callback; } } if (is_kovs) { /* Koei Tecmo PC games */ - if (read_32bitBE(0x00,sf) != 0x4b4f5653) { /* "KOVS" */ + if (read_u32be(0x00,sf) != 0x4b4f5653) { /* "KOVS" */ goto fail; } ovmi.loop_start = read_32bitLE(0x08,sf); @@ -203,7 +220,7 @@ VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) { } if (is_sngw) { /* [Capcom's MT Framework PC games] */ - if (read_32bitBE(0x00,sf) != 0x4f676753) { /* "OggS" (optionally encrypted) */ + if (read_u32be(0x00,sf) != 0x4f676753) { /* "OggS" (optionally encrypted) */ cfg.key_len = read_streamfile(cfg.key, 0x00, 0x04, sf); cfg.is_header_swap = 1; cfg.is_nibble_swap = 1; @@ -217,7 +234,7 @@ VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) { const char *isl_name = NULL; /* check various encrypted "OggS" values */ - if (read_32bitBE(0x00,sf) == 0xAF678753) { /* Azure Striker Gunvolt (PC) */ + if (read_u32be(0x00,sf) == 0xAF678753) { /* Azure Striker Gunvolt (PC) */ static const uint8_t isd_gv_key[16] = { 0xe0,0x00,0xe0,0x00,0xa0,0x00,0x00,0x00,0xe0,0x00,0xe0,0x80,0x40,0x40,0x40,0x00 }; @@ -225,7 +242,7 @@ VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) { memcpy(cfg.key, isd_gv_key, cfg.key_len); isl_name = "GV_steam.isl"; } - else if (read_32bitBE(0x00,sf) == 0x0FE787D3) { /* Mighty Gunvolt (PC) */ + else if (read_u32be(0x00,sf) == 0x0FE787D3) { /* Mighty Gunvolt (PC) */ static const uint8_t isd_mgv_key[120] = { 0x40,0x80,0xE0,0x80,0x40,0x40,0xA0,0x00,0xA0,0x40,0x00,0x80,0x00,0x40,0xA0,0x00, 0xC0,0x40,0xE0,0x00,0x60,0x40,0x80,0x00,0xA0,0x00,0xE0,0x00,0x60,0x40,0xC0,0x00, @@ -240,7 +257,7 @@ VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) { memcpy(cfg.key, isd_mgv_key, cfg.key_len); isl_name = "MGV_steam.isl"; } - else if (read_32bitBE(0x00,sf) == 0x0FA74753) { /* Blaster Master Zero (PC) */ + else if (read_u32be(0x00,sf) == 0x0FA74753) { /* Blaster Master Zero (PC) */ static const uint8_t isd_bmz_key[120] = { 0x40,0xC0,0x20,0x00,0x40,0xC0,0xC0,0x00,0x00,0x80,0xE0,0x80,0x80,0x40,0x20,0x00, 0x60,0xC0,0xC0,0x00,0xA0,0x80,0x60,0x00,0x40,0x40,0x20,0x00,0x60,0x40,0xC0,0x00, @@ -374,7 +391,7 @@ VGMSTREAM* init_vgmstream_ogg_vorbis(STREAMFILE* sf) { uint32_t xor_be; put_32bitLE(key, (uint32_t)file_size); - xor_be = (uint32_t)get_32bitBE(key); + xor_be = get_u32be(key); if ((read_32bitBE(0x00,sf) ^ xor_be) == 0x4F676753) { /* "OggS" */ int i; cfg.key_len = 4; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/opus.c b/Frameworks/vgmstream/vgmstream/src/meta/opus.c index b2fff433f..0f5f3a420 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/opus.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/opus.c @@ -13,24 +13,24 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of size_t data_size, skip = 0; - if ((uint32_t)read_32bitLE(offset + 0x00,sf) != 0x80000001) + if (read_u32le(offset + 0x00,sf) != 0x80000001) goto fail; - channel_count = read_8bit(offset + 0x09, sf); + channel_count = read_u8(offset + 0x09, sf); /* 0x0a: packet size if CBR, 0 if VBR */ - data_offset = offset + read_32bitLE(offset + 0x10, sf); - skip = read_16bitLE(offset + 0x1c, sf); + data_offset = offset + read_u32le(offset + 0x10, sf); + skip = read_u16le(offset + 0x1c, sf); /* 0x1e: ? (seen in Lego Movie 2 (Switch)) */ /* recent >2ch info [Clannad (Switch)] */ - if ((uint32_t)read_32bitLE(offset + 0x20, sf) == 0x80000005) { + if (read_u32le(offset + 0x20, sf) == 0x80000005) { multichannel_offset = offset + 0x20; } - if ((uint32_t)read_32bitLE(data_offset, sf) != 0x80000004) + if (read_u32le(data_offset, sf) != 0x80000004) goto fail; - data_size = read_32bitLE(data_offset + 0x04, sf); + data_size = read_u32le(data_offset + 0x04, sf); start_offset = data_offset + 0x08; loop_flag = (loop_end > 0); /* -1 when not set */ @@ -41,7 +41,7 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of if (!vgmstream) goto fail; vgmstream->meta_type = meta_type; - vgmstream->sample_rate = read_32bitLE(offset + 0x0c,sf); + vgmstream->sample_rate = read_u32le(offset + 0x0c,sf); if (vgmstream->sample_rate == 16000) vgmstream->sample_rate = 48000; // Grandia HD Collection contains a false sample_rate in header vgmstream->num_samples = num_samples; @@ -59,10 +59,10 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of if (multichannel_offset && vgmstream->channels <= 8) { int i; - cfg.stream_count = read_8bit(multichannel_offset + 0x08,sf); - cfg.coupled_count = read_8bit(multichannel_offset + 0x09,sf); + cfg.stream_count = read_u8(multichannel_offset + 0x08,sf); + cfg.coupled_count = read_u8(multichannel_offset + 0x09,sf); for (i = 0; i < vgmstream->channels; i++) { - cfg.channel_mapping[i] = read_8bit(multichannel_offset + 0x0a + i,sf); + cfg.channel_mapping[i] = read_u8(multichannel_offset + 0x0a + i,sf); } } @@ -80,7 +80,7 @@ static VGMSTREAM* init_vgmstream_opus(STREAMFILE* sf, meta_t meta_type, off_t of goto fail; #endif - if ( !vgmstream_open_stream(vgmstream, sf, start_offset) ) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; @@ -92,24 +92,26 @@ fail: /* standard Switch Opus, Nintendo header + raw data (generated by opus_test.c?) [Lego City Undercover (Switch)] */ VGMSTREAM* init_vgmstream_opus_std(STREAMFILE* sf) { - STREAMFILE * PSIFile = NULL; + STREAMFILE* psi_sf = NULL; off_t offset; int num_samples, loop_start, loop_end; /* checks */ - if (!check_extensions(sf,"opus,lopus")) + /* .opus: standard + * .bgm: Cotton Reboot (Switch) */ + if (!check_extensions(sf,"opus,lopus,bgm")) goto fail; offset = 0x00; /* BlazBlue: Cross Tag Battle (Switch) PSI Metadata for corresponding Opus */ /* Maybe future Arc System Works games will use this too? */ - PSIFile = open_streamfile_by_ext(sf, "psi"); - if (PSIFile) { - num_samples = read_32bitLE(0x8C, PSIFile); - loop_start = read_32bitLE(0x84, PSIFile); - loop_end = read_32bitLE(0x88, PSIFile); - close_streamfile(PSIFile); + psi_sf = open_streamfile_by_ext(sf, "psi"); + if (psi_sf) { + num_samples = read_s32le(0x8C, psi_sf); + loop_start = read_s32le(0x84, psi_sf); + loop_end = read_s32le(0x88, psi_sf); + close_streamfile(psi_sf); } else { num_samples = 0; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ps2_2pfs.c b/Frameworks/vgmstream/vgmstream/src/meta/ps2_2pfs.c index 442c7434b..54dc9df12 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ps2_2pfs.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ps2_2pfs.c @@ -3,30 +3,30 @@ /* 2PFS - from Konami Games [Mahoromatic: Moetto - KiraKira Maid-San (PS2), GANTZ The Game (PS2)] */ -VGMSTREAM * init_vgmstream_ps2_2pfs(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ps2_2pfs(STREAMFILE *sf) { VGMSTREAM * vgmstream = NULL; off_t start_offset; - int loop_flag, channel_count, version, interleave; + int loop_flag, channels, version, interleave; int loop_start_block, loop_end_block; /* block number */ int loop_start_adjust, loop_end_adjust; /* loops start/end a few samples into the start/end block */ /* checks */ /* .sap: standard - * .2psf: header id? (Mahoromatic) */ - if (!check_extensions(streamFile, "sap,2psf")) + * .2pfs: header id? (Mahoromatic) */ + if (!check_extensions(sf, "sap,2pfs")) goto fail; - if (read_32bitBE(0x00,streamFile) != 0x32504653) /* "2PFS" */ + if (read_u32be(0x00,sf) != 0x32504653) /* "2PFS" */ goto fail; - version = read_16bitLE(0x04,streamFile); + version = read_u16le(0x04,sf); if (version != 0x01 && version != 0x02) /* v1: Mahoromatic, v2: Gantz */ goto fail; - channel_count = read_8bit(0x40,streamFile); - loop_flag = read_8bit(0x41,streamFile); + channels = read_u8(0x40,sf); + loop_flag = read_u8(0x41,sf); start_offset = 0x800; interleave = 0x1000; @@ -41,44 +41,44 @@ VGMSTREAM * init_vgmstream_ps2_2pfs(STREAMFILE *streamFile) { /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_PS2_2PFS; - vgmstream->num_samples = read_32bitLE(0x34,streamFile) * 28 / 16 / channel_count; + vgmstream->num_samples = read_u32le(0x34,sf) * 28 / 16 / channels; vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = interleave; if (version == 0x01) { - vgmstream->sample_rate = read_32bitLE(0x44,streamFile); - loop_start_adjust = read_16bitLE(0x42,streamFile); - loop_start_block = read_32bitLE(0x48,streamFile); - loop_end_block = read_32bitLE(0x4c,streamFile); + vgmstream->sample_rate = read_u32le(0x44,sf); + loop_start_adjust = read_u16le(0x42,sf); + loop_start_block = read_u32le(0x48,sf); + loop_end_block = read_u32le(0x4c,sf); } else { - vgmstream->sample_rate = read_32bitLE(0x48,streamFile); - loop_start_adjust = read_32bitLE(0x44,streamFile); - loop_start_block = read_32bitLE(0x50,streamFile); - loop_end_block = read_32bitLE(0x54,streamFile); + vgmstream->sample_rate = read_u32le(0x48,sf); + loop_start_adjust = read_u32le(0x44,sf); + loop_start_block = read_u32le(0x50,sf); + loop_end_block = read_u32le(0x54,sf); } loop_end_adjust = interleave; /* loops end after all samples in the end_block AFAIK */ if (loop_flag) { /* block to offset > offset to sample + adjust (number of frames into the block) */ vgmstream->loop_start_sample = - ps_bytes_to_samples(loop_start_block * channel_count * interleave, channel_count) - + ps_bytes_to_samples(loop_start_adjust * channel_count, channel_count); + ps_bytes_to_samples(loop_start_block * channels * interleave, channels) + + ps_bytes_to_samples(loop_start_adjust * channels, channels); vgmstream->loop_end_sample = - ps_bytes_to_samples(loop_end_block * channel_count * interleave, channel_count) - + ps_bytes_to_samples(loop_end_adjust * channel_count, channel_count); + ps_bytes_to_samples(loop_end_block * channels * interleave, channels) + + ps_bytes_to_samples(loop_end_adjust * channels, channels); } - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; fail: - if (vgmstream) close_vgmstream(vgmstream); + close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rsd.c b/Frameworks/vgmstream/vgmstream/src/meta/rsd.c index 50f7a4c37..a4060adaa 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rsd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rsd.c @@ -1,229 +1,229 @@ -#include "meta.h" -#include "../coding/coding.h" - - -/* RSD - from Radical Entertainment games */ -VGMSTREAM * init_vgmstream_rsd(STREAMFILE *sf) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset, name_offset; - size_t data_size; - int loop_flag, channel_count, sample_rate, interleave; - uint32_t codec; - uint8_t version; - - - /* checks */ - if (!check_extensions(sf,"rsd,rsp")) - goto fail; - if ((read_32bitBE(0x00,sf) & 0xFFFFFF00) != 0x52534400) /* "RSD\00" */ - goto fail; - - loop_flag = 0; - - codec = (uint32_t)read_32bitBE(0x04,sf); - channel_count = read_32bitLE(0x08, sf); - /* 0x0c: always 16? */ - sample_rate = read_32bitLE(0x10, sf); - - version = read_8bit(0x03, sf); - switch(version) { - case '2': /* known codecs: VAG/XADP/PCMB [The Simpsons: Road Rage] */ - case '3': /* known codecs: VAG/PCM/PCMB/GADP? [Dark Summit] */ - interleave = read_32bitLE(0x14,sf); /* VAG only, 0x04 otherwise */ - start_offset = read_32bitLE(0x18,sf); - name_offset = 0; - break; - - case '4': /* known codecs: VAG/PCM/RADP/PCMB [The Simpsons: Hit & Run, Tetris Worlds, Hulk] */ - /* 0x14: padding */ - /* 0x18: padding */ - interleave = 0; - start_offset = 0x800; - name_offset = 0; - - /* PCMB/PCM/GADP normally start early but sometimes have padding [The Simpsons: Hit & Run (GC/Xbox)] */ - if ((codec == 0x50434D20 || codec == 0x550434D42 || codec == 0x47414450) - && read_32bitLE(0x80,sf) != 0x2D2D2D2D) - start_offset = 0x80; - break; - - case '6': /* known codecs: VAG/XADP/WADP/RADP/OOGV/AT3+/XMA [Hulk 2, Crash Tag Team Racing, Crash: Mind over Mutant, Scarface] */ - /* 0x14: padding */ - name_offset = 0x18; /* dev file path */ - interleave = 0; - start_offset = 0x800; - break; - - default: - goto fail; - } - - data_size = get_streamfile_size(sf) - start_offset; - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_RSD; - vgmstream->sample_rate = sample_rate; - - switch(codec) { - case 0x50434D20: /* "PCM " [Dark Summit (Xbox), Hulk (PC)] */ - vgmstream->coding_type = coding_PCM16LE; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x2; - - vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16); - break; - - case 0x50434D42: /* "PCMB" [The Simpsons: Road Rage (GC), Dark Summit (GC)] */ - vgmstream->coding_type = coding_PCM16BE; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x2; - - vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16); - break; - - case 0x56414720: /* "VAG " [The Simpsons: Road Rage (PS2), Crash Tag Team Racing (PSP)] */ - vgmstream->coding_type = coding_PSX; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = (interleave == 0) ? 0x10 : interleave; - - vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); - break; - - case 0x58414450: /* "XADP" [The Simpsons: Road Rage (Xbox)], Crash Tag Team Racing (Xbox)] */ - vgmstream->coding_type = (channel_count > 2) ? coding_XBOX_IMA_mch : coding_XBOX_IMA; - vgmstream->layout_type = layout_none; - - vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels); - break; - - case 0x47414450: /* "GADP" [Hulk (GC)] */ - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x08; /* assumed, known files are mono */ - dsp_read_coefs_le(vgmstream,sf,0x14,0x2e); /* LE! */ - dsp_read_hist_le (vgmstream,sf,0x38,0x2e); - - vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); - break; - - case 0x57414450: /* "WADP" [Crash: Mind Over Mutant (Wii)] */ - vgmstream->coding_type = coding_NGC_DSP_subint; - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = 0x02; - dsp_read_coefs_be(vgmstream,sf,0x1a4,0x28); - dsp_read_hist_be (vgmstream,sf,0x1c8,0x28); - - vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); - break; - - case 0x52414450: /* "RADP" [The Simpsons: Hit & Run (GC), Scarface (Wii)] */ - vgmstream->coding_type = coding_RAD_IMA; - vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = 0x14*channel_count; - - vgmstream->num_samples = data_size / 0x14 / channel_count * 32; /* bytes-to-samples */ - break; - -#ifdef VGM_USE_VORBIS - case 0x4F4F4756: { /* "OOGV" [Scarface (PC)] */ - ogg_vorbis_meta_info_t ovmi = {0}; - - ovmi.meta_type = meta_RSD; - close_vgmstream(vgmstream); - vgmstream = init_vgmstream_ogg_vorbis_callbacks(sf, NULL, start_offset, &ovmi); - if (!vgmstream) goto fail; - break; - } -#endif - -#ifdef VGM_USE_FFMPEG - case 0x574D4120: { /* "WMA " [Scarface (Xbox)] */ - ffmpeg_codec_data *ffmpeg_data = NULL; - - /* mini header + WMA header at start_offset */ - ffmpeg_data = init_ffmpeg_offset(sf, start_offset+0x08,data_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; /* an estimation, sometimes cuts files a bit early */ - //vgmstream->num_samples = read_32bitLE(start_offset + 0x00, sf) / channel_count / 2; /* may be PCM data size, but not exact */ - vgmstream->sample_rate = read_32bitLE(start_offset + 0x04, sf); - break; - } - - case 0x4154332B: { /* "AT3+" [Crash of the Titans (PSP)] */ - int fact_samples = 0; - - vgmstream->codec_data = init_ffmpeg_atrac3_riff(sf, start_offset, &fact_samples); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - vgmstream->num_samples = fact_samples; - break; - } - - case 0x584D4120: { /* "XMA " [Crash of the Titans (X360)-v1, Crash: Mind over Mutant (X360)-v2] */ - ffmpeg_codec_data *ffmpeg_data = NULL; - uint8_t buf[0x100]; - size_t bytes, xma_size, block_size, block_count; - int xma_version; - - - /* skip mini header */ - start_offset = 0x800 + read_32bitBE(0x800, sf) + read_32bitBE(0x804, sf) + 0xc; /* assumed, seek table always at 0x800 */ - xma_size = read_32bitBE(0x808, sf); - xma_version = read_32bitBE(0x80C, sf); - - switch (xma_version) { - case 0x03010000: - vgmstream->sample_rate = read_32bitBE(0x818, sf); - vgmstream->num_samples = read_32bitBE(0x824, sf); - block_count = read_32bitBE(0x828, sf); - block_size = 0x10000; - break; - case 0x04010000: - vgmstream->num_samples = read_32bitBE(0x814, sf); - vgmstream->sample_rate = read_32bitBE(0x818, sf); - block_count = read_32bitBE(0x830, sf); - block_size = 0x10000; - break; - default: - goto fail; - } - - bytes = ffmpeg_make_riff_xma2(buf,sizeof(buf), vgmstream->num_samples, xma_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); - ffmpeg_data = init_ffmpeg_header_offset(sf, buf, bytes, start_offset, xma_size); - if (!ffmpeg_data) goto fail; - vgmstream->codec_data = ffmpeg_data; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - /* for some reason (dev trickery?) .rsd don't set skip in the bitstream, though they should */ - //xma_fix_raw_samples(vgmstream, sf, start_offset,xma_size, 0, 0,0); - ffmpeg_set_skip_samples(ffmpeg_data, 512+64); - break; - } -#endif - - default: - goto fail; - } - - if (name_offset) - read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sf); - - if (!vgmstream_open_stream(vgmstream, sf, start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + + +/* RSD - from Radical Entertainment games */ +VGMSTREAM * init_vgmstream_rsd(STREAMFILE *sf) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, name_offset; + size_t data_size; + int loop_flag, channel_count, sample_rate, interleave; + uint32_t codec; + uint8_t version; + + + /* checks */ + if (!check_extensions(sf,"rsd,rsp")) + goto fail; + if ((read_32bitBE(0x00,sf) & 0xFFFFFF00) != 0x52534400) /* "RSD\00" */ + goto fail; + + loop_flag = 0; + + codec = (uint32_t)read_32bitBE(0x04,sf); + channel_count = read_32bitLE(0x08, sf); + /* 0x0c: always 16? */ + sample_rate = read_32bitLE(0x10, sf); + + version = read_8bit(0x03, sf); + switch(version) { + case '2': /* known codecs: VAG/XADP/PCMB [The Simpsons: Road Rage] */ + case '3': /* known codecs: VAG/PCM/PCMB/GADP? [Dark Summit] */ + interleave = read_32bitLE(0x14,sf); /* VAG only, 0x04 otherwise */ + start_offset = read_32bitLE(0x18,sf); + name_offset = 0; + break; + + case '4': /* known codecs: VAG/PCM/RADP/PCMB [The Simpsons: Hit & Run, Tetris Worlds, Hulk] */ + /* 0x14: padding */ + /* 0x18: padding */ + interleave = 0; + start_offset = 0x800; + name_offset = 0; + + /* PCMB/PCM/GADP normally start early but sometimes have padding [The Simpsons: Hit & Run (GC/Xbox)] */ + if ((codec == 0x50434D20 || codec == 0x550434D42 || codec == 0x47414450) + && read_32bitLE(0x80,sf) != 0x2D2D2D2D) + start_offset = 0x80; + break; + + case '6': /* known codecs: VAG/XADP/WADP/RADP/OOGV/AT3+/XMA [Hulk 2, Crash Tag Team Racing, Crash: Mind over Mutant, Scarface] */ + /* 0x14: padding */ + name_offset = 0x18; /* dev file path */ + interleave = 0; + start_offset = 0x800; + break; + + default: + goto fail; + } + + data_size = get_streamfile_size(sf) - start_offset; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_RSD; + vgmstream->sample_rate = sample_rate; + + switch(codec) { + case 0x50434D20: /* "PCM " [Dark Summit (Xbox), Hulk (PC)] */ + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x2; + + vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16); + break; + + case 0x50434D42: /* "PCMB" [The Simpsons: Road Rage (GC), Dark Summit (GC)] */ + vgmstream->coding_type = coding_PCM16BE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x2; + + vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, 16); + break; + + case 0x56414720: /* "VAG " [The Simpsons: Road Rage (PS2), Crash Tag Team Racing (PSP)] */ + vgmstream->coding_type = coding_PSX; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = (interleave == 0) ? 0x10 : interleave; + + vgmstream->num_samples = ps_bytes_to_samples(data_size, channel_count); + break; + + case 0x58414450: /* "XADP" [The Simpsons: Road Rage (Xbox)], Crash Tag Team Racing (Xbox)] */ + vgmstream->coding_type = (channel_count > 2) ? coding_XBOX_IMA_mch : coding_XBOX_IMA; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = xbox_ima_bytes_to_samples(data_size, vgmstream->channels); + break; + + case 0x47414450: /* "GADP" [Hulk (GC)] */ + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x08; /* assumed, known files are mono */ + dsp_read_coefs_le(vgmstream,sf,0x14,0x2e); /* LE! */ + dsp_read_hist_le (vgmstream,sf,0x38,0x2e); + + vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); + break; + + case 0x57414450: /* "WADP" [Crash: Mind Over Mutant (Wii)] */ + vgmstream->coding_type = coding_NGC_DSP_subint; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = 0x02; + dsp_read_coefs_be(vgmstream,sf,0x1a4,0x28); + dsp_read_hist_be (vgmstream,sf,0x1c8,0x28); + + vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); + break; + + case 0x52414450: /* "RADP" [The Simpsons: Hit & Run (GC), Scarface (Wii)] */ + vgmstream->coding_type = coding_RAD_IMA; + vgmstream->layout_type = layout_none; + vgmstream->interleave_block_size = 0x14*channel_count; + + vgmstream->num_samples = data_size / 0x14 / channel_count * 32; /* bytes-to-samples */ + break; + +#ifdef VGM_USE_VORBIS + case 0x4F4F4756: { /* "OOGV" [Scarface (PC)] */ + ogg_vorbis_meta_info_t ovmi = {0}; + + ovmi.meta_type = meta_RSD; + close_vgmstream(vgmstream); + vgmstream = init_vgmstream_ogg_vorbis_callbacks(sf, NULL, start_offset, &ovmi); + if (!vgmstream) goto fail; + break; + } +#endif + +#ifdef VGM_USE_FFMPEG + case 0x574D4120: { /* "WMA " [Scarface (Xbox)] */ + ffmpeg_codec_data *ffmpeg_data = NULL; + + /* mini header + WMA header at start_offset */ + ffmpeg_data = init_ffmpeg_offset(sf, start_offset+0x08,data_size); + if (!ffmpeg_data) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = (int32_t)ffmpeg_data->totalSamples; /* an estimation, sometimes cuts files a bit early */ + //vgmstream->num_samples = read_32bitLE(start_offset + 0x00, sf) / channel_count / 2; /* may be PCM data size, but not exact */ + vgmstream->sample_rate = read_32bitLE(start_offset + 0x04, sf); + break; + } + + case 0x4154332B: { /* "AT3+" [Crash of the Titans (PSP)] */ + int fact_samples = 0; + + vgmstream->codec_data = init_ffmpeg_atrac3_riff(sf, start_offset, &fact_samples); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = fact_samples; + break; + } + + case 0x584D4120: { /* "XMA " [Crash of the Titans (X360)-v1, Crash: Mind over Mutant (X360)-v2] */ + ffmpeg_codec_data *ffmpeg_data = NULL; + uint8_t buf[0x100]; + size_t bytes, xma_size, block_size, block_count; + int xma_version; + + + /* skip mini header */ + start_offset = 0x800 + read_32bitBE(0x800, sf) + read_32bitBE(0x804, sf) + 0xc; /* assumed, seek table always at 0x800 */ + xma_size = read_32bitBE(0x808, sf); + xma_version = read_u8(0x80C, sf); + + switch (xma_version) { + case 0x03: + vgmstream->sample_rate = read_32bitBE(0x818, sf); + vgmstream->num_samples = read_32bitBE(0x824, sf); + block_count = read_32bitBE(0x828, sf); + block_size = 0x10000; + break; + case 0x04: + vgmstream->num_samples = read_32bitBE(0x814, sf); + vgmstream->sample_rate = read_32bitBE(0x818, sf); + block_count = read_32bitBE(0x830, sf); + block_size = 0x10000; + break; + default: + goto fail; + } + + bytes = ffmpeg_make_riff_xma2(buf,sizeof(buf), vgmstream->num_samples, xma_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_size); + ffmpeg_data = init_ffmpeg_header_offset(sf, buf, bytes, start_offset, xma_size); + if (!ffmpeg_data) goto fail; + vgmstream->codec_data = ffmpeg_data; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + /* for some reason (dev trickery?) .rsd don't set skip in the bitstream, though they should */ + //xma_fix_raw_samples(vgmstream, sf, start_offset,xma_size, 0, 0,0); + ffmpeg_set_skip_samples(ffmpeg_data, 512+64); + break; + } +#endif + + default: + goto fail; + } + + if (name_offset) + read_string(vgmstream->stream_name,STREAM_NAME_SIZE, name_offset,sf); + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/tac.c b/Frameworks/vgmstream/vgmstream/src/meta/tac.c new file mode 100644 index 000000000..1a01c97cf --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/tac.c @@ -0,0 +1,69 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* tri-Ace codec file [Star Ocean 3 (PS2), Valkyrie Profile 2 (PS2), Radiata Stories (PS2)] */ +VGMSTREAM* init_vgmstream_tac(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + int loop_flag, channel_count; + uint16_t loop_frame, frame_count, loop_discard, frame_last; + uint32_t info_offset, loop_offset, stream_size, file_size; + off_t start_offset; + + + /* checks */ + /* (extensionless): bigfiles have no known names (libs calls mention "St*" and "Sac*" though) + * .aac: fake for convenience given it's a tri-Ace AAC's grandpa (but don't use unless you must) + * .pk3/.20: extremely ugly fake extensions randomly given by an old extractor, *DON'T* */ + if (!check_extensions(sf, ",aac,laac")) + goto fail; + /* file is validated on decoder init, early catch of simple errors (see tac_decoder_lib.h for full header) */ + info_offset = read_u32le(0x00,sf); + if (info_offset > 0x4E000 || info_offset < 0x20) /* offset points to value inside first "block" */ + goto fail; + loop_frame = read_u16le(0x08,sf); + loop_discard = read_u16le(0x0a,sf); + frame_count = read_u16le(0x0c,sf); + frame_last = read_u16le(0x0e,sf); + loop_offset = read_u32le(0x10,sf); + stream_size = read_u32le(0x14,sf); + if (stream_size % 0x4E000 != 0) /* multiple of blocks */ + goto fail; + + /* actual file can truncate last block */ + file_size = get_streamfile_size(sf); + if (file_size > stream_size || file_size < stream_size - 0x4E000) + goto fail; + + channel_count = 2; /* always stereo */ + loop_flag = (loop_offset != stream_size); /* actual check may be loop_frame > 0? */ + start_offset = 0; + + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_TAC; + vgmstream->sample_rate = 48000; + + /* Frame at count/loop outputs less than full 1024 samples (thus loop or count-1 + extra). + * A few files may pop when looping, but this seems to match game/emulator. */ + vgmstream->num_samples = (frame_count - 1) * 1024 + (frame_last + 1); + vgmstream->loop_start_sample = (loop_frame - 1) * 1024 + loop_discard; + vgmstream->loop_end_sample = vgmstream->num_samples; + + { + vgmstream->codec_data = init_tac(sf); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_TAC; + vgmstream->layout_type = layout_none; + } + + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c index fe9375245..f957f0f0a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c @@ -1051,7 +1051,7 @@ static int parse_values(ubi_bao_header* bao, STREAMFILE* sf) { } bao->codec = bao->cfg.codec_map[bao->stream_type]; if (bao->codec == 0x00) { - VGM_LOG("UBI BAO: unknown codec at %x\n", (uint32_t)bao->header_offset); goto fail; + VGM_LOG("UBI BAO: unknown codec %x at %x\n", bao->stream_type, (uint32_t)bao->header_offset); goto fail; goto fail; } @@ -1327,6 +1327,27 @@ fail: /* ************************************************************************* */ +/* These are all of the languages that were referenced in Assassin's Creed exe (out of each platform). */ +/* Also, additional languages were referenced in Shawn White Skateboarding (X360) exe in this order, there may be more. */ +static const char* language_bao_formats[] = { + "English_BAO_0x%08x", + "French_BAO_0x%08x", + "Spanish_BAO_0x%08x", + "Polish_BAO_0x%08x", + "German_BAO_0x%08x", + "Chinese_BAO_0x%08x", + "Hungarian_BAO_0x%08x", + "Italian_BAO_0x%08x", + "Japanese_BAO_0x%08x", + "Czech_BAO_0x%08x", + "Korean_BAO_0x%08x", + "Russian_BAO_0x%08x", + "Dutch_BAO_0x%08x", + "Danish_BAO_0x%08x", + "Norwegian_BAO_0x%08x", + "Swedish_BAO_0x%08x", +}; + /* opens a file BAO's companion BAO (memory or stream) */ static STREAMFILE* open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int is_stream, STREAMFILE* sf) { STREAMFILE* sf_bao = NULL; @@ -1349,47 +1370,17 @@ static STREAMFILE* open_atomic_bao(ubi_bao_file file_type, uint32_t file_id, int sf_bao = open_streamfile_by_filename(sf, buf); if (sf_bao) return sf_bao; - snprintf(buf,buf_size, "English_BAO_0x%08x", file_id); - sf_bao = open_streamfile_by_filename(sf, buf); - if (sf_bao) return sf_bao; + { + int i; + int count = (sizeof(language_bao_formats) / sizeof(language_bao_formats[0])); + for (i = 0; i < count; i++) { + const char* format = language_bao_formats[i]; - snprintf(buf,buf_size, "French_BAO_0x%08x", file_id); - sf_bao = open_streamfile_by_filename(sf, buf); - if (sf_bao) return sf_bao; - - snprintf(buf,buf_size, "Spanish_BAO_0x%08x", file_id); - sf_bao = open_streamfile_by_filename(sf, buf); - if (sf_bao) return sf_bao; - - snprintf(buf,buf_size, "German_BAO_0x%08x", file_id); - sf_bao = open_streamfile_by_filename(sf, buf); - if (sf_bao) return sf_bao; - - snprintf(buf,buf_size, "Italian_BAO_0x%08x", file_id); - sf_bao = open_streamfile_by_filename(sf, buf); - if (sf_bao) return sf_bao; - - snprintf(buf,buf_size, "Japanese_BAO_0x%08x", file_id); - sf_bao = open_streamfile_by_filename(sf, buf); - if (sf_bao) return sf_bao; - - snprintf(buf,buf_size, "Korean_BAO_0x%08x", file_id); - sf_bao = open_streamfile_by_filename(sf, buf); - if (sf_bao) return sf_bao; - - snprintf(buf,buf_size, "Russian_BAO_0x%08x", file_id); - sf_bao = open_streamfile_by_filename(sf, buf); - if (sf_bao) return sf_bao; - - snprintf(buf,buf_size, "Czech_BAO_0x%08x", file_id); - sf_bao = open_streamfile_by_filename(sf, buf); - if (sf_bao) return sf_bao; - - snprintf(buf,buf_size, "Polish_BAO_0x%08x", file_id); - sf_bao = open_streamfile_by_filename(sf, buf); - if (sf_bao) return sf_bao; - - /* these are all of the languages that were referenced in Assassin's Creed exe (out of each platform), there may be more */ + snprintf(buf,buf_size, format, file_id); + sf_bao = open_streamfile_by_filename(sf, buf); + if (sf_bao) return sf_bao; + } + } } else { snprintf(buf,buf_size, "BAO_0x%08x", file_id); @@ -1746,9 +1737,11 @@ static int config_bao_version(ubi_bao_header* bao, STREAMFILE* sf) { config_bao_silence_f(bao, 0x1c); + bao->cfg.codec_map[0x00] = RAW_XMA1; bao->cfg.codec_map[0x02] = RAW_PSX; bao->cfg.codec_map[0x03] = UBI_IMA; bao->cfg.codec_map[0x04] = FMT_OGG; + bao->cfg.codec_map[0x05] = RAW_XMA1; /* same but streamed? */ bao->cfg.codec_map[0x07] = RAW_AT3_105; bao->cfg.file_type = UBI_FORGE; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_hx.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_hx.c index 627292c5b..654d85df4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_hx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_hx.c @@ -3,7 +3,7 @@ #include "../coding/coding.h" -typedef enum { PCM, UBI, PSX, DSP, XIMA, ATRAC3, XMA2 } ubi_hx_codec; +typedef enum { PCM, UBI, PSX, DSP, XIMA, ATRAC3, XMA2, MP3 } ubi_hx_codec; typedef struct { int big_endian; @@ -36,14 +36,14 @@ typedef struct { } ubi_hx_header; -static int parse_hx(ubi_hx_header * hx, STREAMFILE *sf, int target_subsong); -static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *sf); +static int parse_hx(ubi_hx_header* hx, STREAMFILE* sf, int target_subsong); +static VGMSTREAM* init_vgmstream_ubi_hx_header(ubi_hx_header* hx, STREAMFILE* sf); /* .HXx - banks from Ubisoft's HXAudio engine games [Rayman Arena, Rayman 3, XIII] */ -VGMSTREAM * init_vgmstream_ubi_hx(STREAMFILE *streamFile) { +VGMSTREAM* init_vgmstream_ubi_hx(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; ubi_hx_header hx = {0}; - int target_subsong = streamFile->stream_index; + int target_subsong = sf->stream_index; /* checks */ @@ -53,7 +53,7 @@ VGMSTREAM * init_vgmstream_ubi_hx(STREAMFILE *streamFile) { * .hxg: Rayman 3 (GC), XIII (GC) * .hxx: Rayman 3 (Xbox), Rayman 3 HD (X360) * .hx3: Rayman 3 HD (PS3) */ - if (!check_extensions(streamFile, "hxd,hxc,hx2,hxg,hxx,hx3")) + if (!check_extensions(sf, "hxd,hxc,hx2,hxg,hxx,hx3")) goto fail; /* .HXx is a slightly less bizarre bank with various resource classes (events, streams, etc, not unlike other Ubi's engines) @@ -61,14 +61,14 @@ VGMSTREAM * init_vgmstream_ubi_hx(STREAMFILE *streamFile) { * Game seems to play files by calling linked ids: EventResData (play/stop/etc) > Random/Program/Wav ResData (1..N refs) > FileIdObj */ /* HX CONFIG */ - hx.big_endian = guess_endianness32bit(0x00, streamFile); + hx.big_endian = guess_endianness32bit(0x00, sf); /* HX HEADER */ - if (!parse_hx(&hx, streamFile, target_subsong)) + if (!parse_hx(&hx, sf, target_subsong)) goto fail; /* CREATE VGMSTREAM */ - vgmstream = init_vgmstream_ubi_hx_header(&hx, streamFile); + vgmstream = init_vgmstream_ubi_hx_header(&hx, sf); return vgmstream; fail: @@ -77,7 +77,7 @@ fail: } -static void build_readable_name(char * buf, size_t buf_size, ubi_hx_header * hx) { +static void build_readable_name(char* buf, size_t buf_size, ubi_hx_header* hx) { const char *grp_name; if (hx->is_external) @@ -94,8 +94,8 @@ static void build_readable_name(char * buf, size_t buf_size, ubi_hx_header * hx) #define TXT_LINE_MAX 0x1000 /* get name */ -static int parse_name_bnh(ubi_hx_header * hx, STREAMFILE *sf, uint32_t cuuid1, uint32_t cuuid2) { - STREAMFILE *sf_t; +static int parse_name_bnh(ubi_hx_header* hx, STREAMFILE* sf, uint32_t cuuid1, uint32_t cuuid2) { + STREAMFILE* sf_t; off_t txt_offset = 0; char line[TXT_LINE_MAX]; char cuuid[40]; @@ -132,15 +132,16 @@ fail: /* get referenced name from WavRes, using the index again (abridged) */ -static int parse_name(ubi_hx_header * hx, STREAMFILE *sf) { - int32_t (*read_32bit)(off_t,STREAMFILE*) = hx->big_endian ? read_32bitBE : read_32bitLE; +static int parse_name(ubi_hx_header* hx, STREAMFILE* sf) { + uint32_t (*read_u32)(off_t,STREAMFILE*) = hx->big_endian ? read_u32be : read_u32le; + int32_t (*read_s32)(off_t,STREAMFILE*) = hx->big_endian ? read_s32be : read_s32le; off_t index_offset, offset; int i, index_entries; char class_name[255]; - index_offset = read_32bit(0x00, sf); - index_entries = read_32bit(index_offset + 0x08, sf); + index_offset = read_u32(0x00, sf); + index_entries = read_s32(index_offset + 0x08, sf); offset = index_offset + 0x0c; for (i = 0; i < index_entries; i++) { off_t header_offset; @@ -149,25 +150,25 @@ static int parse_name(ubi_hx_header * hx, STREAMFILE *sf) { uint32_t cuuid1, cuuid2; - class_size = read_32bit(offset + 0x00, sf); + class_size = read_u32(offset + 0x00, sf); if (class_size > sizeof(class_name)+1) goto fail; read_string(class_name,class_size+1, offset + 0x04, sf); /* not null-terminated */ offset += 0x04 + class_size; - cuuid1 = (uint32_t)read_32bit(offset + 0x00, sf); - cuuid2 = (uint32_t)read_32bit(offset + 0x04, sf); + cuuid1 = read_u32(offset + 0x00, sf); + cuuid2 = read_u32(offset + 0x04, sf); - header_offset = read_32bit(offset + 0x08, sf); + header_offset = read_u32(offset + 0x08, sf); offset += 0x10; - //unknown_count = read_32bit(offset + 0x00, sf); + //unknown_count = read_s32(offset + 0x00, sf); offset += 0x04; - link_count = read_32bit(offset + 0x00, sf); + link_count = read_s32(offset + 0x00, sf); offset += 0x04; for (j = 0; j < link_count; j++) { - uint32_t link_id1 = (uint32_t)read_32bit(offset + 0x00, sf); - uint32_t link_id2 = (uint32_t)read_32bit(offset + 0x04, sf); + uint32_t link_id1 = read_u32(offset + 0x00, sf); + uint32_t link_id2 = read_u32(offset + 0x04, sf); if (link_id1 == hx->cuuid1 && link_id2 == hx->cuuid2) { is_found = 1; @@ -175,11 +176,11 @@ static int parse_name(ubi_hx_header * hx, STREAMFILE *sf) { offset += 0x08; } - language_count = read_32bit(offset + 0x00, sf); + language_count = read_s32(offset + 0x00, sf); offset += 0x04; for (j = 0; j < language_count; j++) { - uint32_t link_id1 = (uint32_t)read_32bit(offset + 0x08, sf); - uint32_t link_id2 = (uint32_t)read_32bit(offset + 0x0c, sf); + uint32_t link_id1 = read_u32(offset + 0x08, sf); + uint32_t link_id2 = read_u32(offset + 0x0c, sf); if (link_id1 == hx->cuuid1 && link_id2 == hx->cuuid2) { is_found = 1; @@ -199,10 +200,10 @@ static int parse_name(ubi_hx_header * hx, STREAMFILE *sf) { off_t wavres_offset = header_offset; /* parse WavRes header */ - resclass_size = read_32bit(wavres_offset, sf); + resclass_size = read_u32(wavres_offset, sf); wavres_offset += 0x04 + resclass_size + 0x08 + 0x04; /* skip class + cuiid + flags */ - internal_size = read_32bit(wavres_offset + 0x00, sf); + internal_size = read_u32(wavres_offset + 0x00, sf); /* Xbox has some kind of big size and "flags" has a value of 2, instead of 3/4 like other platforms */ if (strcmp(class_name, "CXBoxWavResData") == 0 && internal_size > 0x100) return 1; @@ -227,9 +228,10 @@ fail: /* parse a single known header resource at offset */ -static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t size, int index) { - int32_t (*read_32bit)(off_t,STREAMFILE*) = hx->big_endian ? read_32bitBE : read_32bitLE; - int16_t (*read_16bit)(off_t,STREAMFILE*) = hx->big_endian ? read_16bitBE : read_16bitLE; +static int parse_header(ubi_hx_header* hx, STREAMFILE* sf, off_t offset, size_t size, int index) { + uint32_t (*read_u32)(off_t,STREAMFILE*) = hx->big_endian ? read_u32be : read_u32le; + int32_t (*read_s32)(off_t,STREAMFILE*) = hx->big_endian ? read_s32be : read_s32le; + uint16_t (*read_u16)(off_t,STREAMFILE*) = hx->big_endian ? read_u16be : read_u16le; off_t riff_offset, riff_size, chunk_offset, stream_adjust = 0, resource_size; size_t chunk_size; int cue_flag = 0; @@ -242,24 +244,28 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t hx->header_offset = offset; hx->header_size = size; - hx->class_size = read_32bit(offset + 0x00, sf); + hx->class_size = read_u32(offset + 0x00, sf); if (hx->class_size > sizeof(hx->class_name)+1) goto fail; read_string(hx->class_name,hx->class_size+1, offset + 0x04, sf); offset += 0x04 + hx->class_size; - hx->cuuid1 = (uint32_t)read_32bit(offset + 0x00, sf); - hx->cuuid2 = (uint32_t)read_32bit(offset + 0x04, sf); + hx->cuuid1 = read_u32(offset + 0x00, sf); + hx->cuuid2 = read_u32(offset + 0x04, sf); offset += 0x08; if (strcmp(hx->class_name, "CPCWaveFileIdObj") == 0 || strcmp(hx->class_name, "CPS2WaveFileIdObj") == 0 || strcmp(hx->class_name, "CGCWaveFileIdObj") == 0 || strcmp(hx->class_name, "CXBoxWaveFileIdObj") == 0) { - uint32_t flag_type = read_32bit(offset + 0x00, sf); + uint32_t flag_type = read_u32(offset + 0x00, sf); if (flag_type == 0x01 || flag_type == 0x02) { /* Rayman Arena */ - if (read_32bit(offset + 0x04, sf) != 0x00) goto fail; - hx->stream_mode = read_32bit(offset + 0x08, sf); /* flag: 0=internal, 1=external */ + uint32_t unk_value = read_u32(offset + 0x04, sf); + if (unk_value != 0x00 && /* common */ + unk_value != 0xbe570a3d && /* Largo Winch: Empire Under Threat (PC)-most */ + unk_value != 0xbf8e147b) /* Largo Winch: Empire Under Threat (PC)-few */ + goto fail; + hx->stream_mode = read_u32(offset + 0x08, sf); /* flag: 0=internal, 1=external */ /* 0x0c: flag: 0=static, 1=stream */ offset += 0x10; } @@ -268,12 +274,12 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t offset += 0x08; if (strcmp(hx->class_name, "CGCWaveFileIdObj") == 0) { - if (read_32bit(offset + 0x00, sf) != read_32bit(offset + 0x04, sf)) goto fail; /* meaning? */ - hx->stream_mode = read_32bit(offset + 0x04, sf); + if (read_u32(offset + 0x00, sf) != read_u32(offset + 0x04, sf)) goto fail; /* meaning? */ + hx->stream_mode = read_u32(offset + 0x04, sf); offset += 0x08; } else { - hx->stream_mode = read_8bit(offset, sf); + hx->stream_mode = read_u8(offset, sf); offset += 0x01; } } @@ -284,7 +290,7 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t /* get bizarro adjust (found in XIII external files) */ if (hx->stream_mode == 0x0a) { - stream_adjust = read_32bit(offset, sf); /* what */ + stream_adjust = read_u32(offset, sf); /* what */ offset += 0x04; } @@ -292,66 +298,71 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t switch(hx->stream_mode) { case 0x00: /* memory (internal file) */ riff_offset = offset; - riff_size = read_32bit(riff_offset + 0x04, sf) + 0x08; + riff_size = read_u32(riff_offset + 0x04, sf) + 0x08; break; case 0x01: /* static (smaller external file) */ case 0x03: /* stream (bigger external file) */ case 0x07: /* static? */ case 0x0a: /* static? */ - resource_size = read_32bit(offset + 0x00, sf); + resource_size = read_u32(offset + 0x00, sf); if (resource_size > sizeof(hx->resource_name)+1) goto fail; read_string(hx->resource_name,resource_size+1, offset + 0x04, sf); riff_offset = offset + 0x04 + resource_size; - riff_size = read_32bit(riff_offset + 0x04, sf) + 0x08; + riff_size = read_u32(riff_offset + 0x04, sf) + 0x08; hx->is_external = 1; break; default: + VGM_LOG("UBI HX: %x\n", hx->stream_mode); goto fail; } - /* parse pseudo-RIFF "fmt" */ - if (read_32bit(riff_offset, sf) != 0x46464952) /* "RIFF" in machine endianness */ + if (read_u32(riff_offset, sf) != 0x46464952) /* "RIFF" in machine endianness */ goto fail; - hx->codec_id = read_16bit(riff_offset + 0x14 , sf); + hx->codec_id = read_u16(riff_offset + 0x14 , sf); switch(hx->codec_id) { case 0x01: hx->codec = PCM; break; case 0x02: hx->codec = UBI; break; case 0x03: hx->codec = PSX; break; case 0x04: hx->codec = DSP; break; case 0x05: hx->codec = XIMA; break; + case 0x55: hx->codec = MP3; break; /* Largo Winch: Empire Under Threat (PC) */ default: - VGM_LOG("UBI HX: unknown codec %i\n", hx->codec_id); + VGM_LOG("UBI HX: unknown codec %x\n", hx->codec_id); goto fail; } - hx->channels = read_16bit(riff_offset + 0x16, sf); - hx->sample_rate = read_32bit(riff_offset + 0x18, sf); + hx->channels = read_u16(riff_offset + 0x16, sf); + hx->sample_rate = read_u32(riff_offset + 0x18, sf); /* find "datx" (external) or "data" (internal) also in machine endianness */ if (hx->is_external) { + if (find_chunk_riff_ve(sf, 0x78746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,NULL, hx->big_endian)) { - hx->stream_size = read_32bit(chunk_offset + 0x00, sf); - hx->stream_offset = read_32bit(chunk_offset + 0x04, sf) + stream_adjust; + hx->stream_size = read_u32(chunk_offset + 0x00, sf); + hx->stream_offset = read_u32(chunk_offset + 0x04, sf) + stream_adjust; } else if ((flag_type == 0x01 || flag_type == 0x02) && /* Rayman M (not Arena) uses "data" instead */ find_chunk_riff_ve(sf, 0x61746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,&chunk_size, hx->big_endian)) { hx->stream_size = chunk_size; - hx->stream_offset = read_32bit(chunk_offset + 0x00, sf) + stream_adjust; + hx->stream_offset = read_u32(chunk_offset + 0x00, sf) + stream_adjust; } else { goto fail; } } else { - if (!find_chunk_riff_ve(sf, 0x61746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,NULL, hx->big_endian)) + if (!find_chunk_riff_ve(sf, 0x61746164,riff_offset + 0x0c,riff_size - 0x0c, &chunk_offset,&chunk_size, hx->big_endian)) goto fail; hx->stream_offset = chunk_offset; - hx->stream_size = riff_size - (chunk_offset - riff_offset); + + if (chunk_size > riff_size - (chunk_offset - riff_offset) || !chunk_size) + chunk_size = riff_size - (chunk_offset - riff_offset); /* just in case */ + hx->stream_size = chunk_size; } /* can contain other RIFF stuff like "cue ", "labl" and "ump3" @@ -362,13 +373,13 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t strcmp(hx->class_name, "CPS3StaticAC3WaveFileIdObj") == 0 || strcmp(hx->class_name, "CPS3StreamAC3WaveFileIdObj") == 0) { - hx->stream_offset = read_32bit(offset + 0x00, sf); - hx->stream_size = read_32bit(offset + 0x04, sf); + hx->stream_offset = read_u32(offset + 0x00, sf); + hx->stream_size = read_u32(offset + 0x04, sf); offset += 0x08; //todo some dummy files have 0 size - if (read_32bit(offset + 0x00, sf) != 0x01) goto fail; + if (read_u32(offset + 0x00, sf) != 0x01) goto fail; /* 0x04: some kind of parent id shared by multiple Waves, or 0 */ offset += 0x08; @@ -385,27 +396,27 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t case 0x90: hx->channels = 2; break; default: goto fail; } - hx->sample_rate = (uint16_t)(read_16bit(offset + 0x02, sf) & 0x7FFF) << 1; /* ??? */ - cue_flag = (uint8_t) read_8bit (offset + 0x03, sf) & (1<<7); + hx->sample_rate = (read_u16(offset + 0x02, sf) & 0x7FFFu) << 1u; /* ??? */ + cue_flag = read_u8(offset + 0x03, sf) & (1<<7); offset += 0x04; } else if ((strcmp(hx->class_name, "CXBoxStaticHWWaveFileIdObj") == 0 || strcmp(hx->class_name, "CXBoxStreamHWWaveFileIdObj") == 0) && hx->big_endian) { /* fake fmt chunk */ hx->codec = XMA2; - hx->channels = (uint16_t)read_16bit(offset + 0x02, sf); - hx->sample_rate = read_32bit(offset + 0x04, sf); - hx->num_samples = read_32bit(offset + 0x18, sf) / 0x02 / hx->channels; - cue_flag = read_32bit(offset + 0x34, sf); + hx->channels = read_u16(offset + 0x02, sf); + hx->sample_rate = read_u32(offset + 0x04, sf); + hx->num_samples = read_s32(offset + 0x18, sf) / 0x02 / hx->channels; + cue_flag = read_u32(offset + 0x34, sf); offset += 0x38; } else { /* MSFC header */ hx->codec = ATRAC3; - hx->codec_id = read_32bit(offset + 0x04, sf); - hx->channels = read_32bit(offset + 0x08, sf); - hx->sample_rate = read_32bit(offset + 0x10, sf); - cue_flag = read_32bit(offset + 0x40, sf); + hx->codec_id = read_u32(offset + 0x04, sf); + hx->channels = read_u32(offset + 0x08, sf); + hx->sample_rate = read_u32(offset + 0x10, sf); + cue_flag = read_u32(offset + 0x40, sf); offset += 0x44; } @@ -413,11 +424,11 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t if (cue_flag) { int j; - size_t cue_count = read_32bit(offset, sf); + size_t cue_count = read_s32(offset, sf); offset += 0x04; for (j = 0; j < cue_count; j++) { /* 0x00: id? */ - size_t description_size = read_32bit(offset + 0x04, sf); /* for next string */ + size_t description_size = read_u32(offset + 0x04, sf); /* for next string */ offset += 0x08 + description_size; } } @@ -427,7 +438,7 @@ static int parse_header(ubi_hx_header * hx, STREAMFILE *sf, off_t offset, size_t case 0x01: /* static (smaller external file) */ case 0x03: /* stream (bigger external file) */ case 0x07: /* stream? */ - resource_size = read_32bit(offset + 0x00, sf); + resource_size = read_u32(offset + 0x00, sf); if (resource_size > sizeof(hx->resource_name)+1) goto fail; read_string(hx->resource_name,resource_size+1, offset + 0x04, sf); @@ -450,22 +461,23 @@ fail: /* parse a bank index and its possible audio headers (some info from Droolie's .bms) */ -static int parse_hx(ubi_hx_header * hx, STREAMFILE *sf, int target_subsong) { - int32_t (*read_32bit)(off_t,STREAMFILE*) = hx->big_endian ? read_32bitBE : read_32bitLE; +static int parse_hx(ubi_hx_header* hx, STREAMFILE* sf, int target_subsong) { + uint32_t (*read_u32)(off_t,STREAMFILE*) = hx->big_endian ? read_u32be : read_u32le; + int32_t (*read_s32)(off_t,STREAMFILE*) = hx->big_endian ? read_s32be : read_s32le; off_t index_offset, offset; int i, index_entries; char class_name[255]; - index_offset = read_32bit(0x00, sf); - if (read_32bit(index_offset + 0x00, sf) != 0x58444E49) /* "XDNI" (INDX in given endianness) */ + index_offset = read_u32(0x00, sf); + if (read_u32(index_offset + 0x00, sf) != 0x58444E49) /* "XDNI" (INDX in given endianness) */ goto fail; - if (read_32bit(index_offset + 0x04, sf) != 0x02) /* type? */ + if (read_u32(index_offset + 0x04, sf) != 0x02) /* type? */ goto fail; if (target_subsong == 0) target_subsong = 1; - index_entries = read_32bit(index_offset + 0x08, sf); + index_entries = read_s32(index_offset + 0x08, sf); offset = index_offset + 0x0c; for (i = 0; i < index_entries; i++) { off_t header_offset; @@ -476,19 +488,19 @@ static int parse_hx(ubi_hx_header * hx, STREAMFILE *sf, int target_subsong) { /* parse index entries: offset to actual header plus some extra info also in the header */ - class_size = read_32bit(offset + 0x00, sf); + class_size = read_u32(offset + 0x00, sf); if (class_size > sizeof(class_name)+1) goto fail; read_string(class_name,class_size+1, offset + 0x04, sf); /* not null-terminated */ offset += 0x04 + class_size; /* 0x00: id1+2 */ - header_offset = read_32bit(offset + 0x08, sf); - header_size = read_32bit(offset + 0x0c, sf); + header_offset = read_u32(offset + 0x08, sf); + header_size = read_u32(offset + 0x0c, sf); offset += 0x10; /* not seen */ - unknown_count = read_32bit(offset + 0x00, sf); + unknown_count = read_s32(offset + 0x00, sf); if (unknown_count != 0) { VGM_LOG("UBI HX: found unknown near %lx\n", offset); goto fail; @@ -496,18 +508,18 @@ static int parse_hx(ubi_hx_header * hx, STREAMFILE *sf, int target_subsong) { offset += 0x04; /* ids that this object directly points to (ex. Event > Random) */ - link_count = read_32bit(offset + 0x00, sf); + link_count = read_s32(offset + 0x00, sf); offset += 0x04 + 0x08 * link_count; /* localized id list of WavRes (can use this list instead of the prev one) */ - language_count = read_32bit(offset + 0x00, sf); + language_count = read_s32(offset + 0x00, sf); offset += 0x04; for (j = 0; j < language_count; j++) { /* 0x00: lang code, in reverse endianness: "en ", "fr ", etc */ /* 0x04: possibly count of ids for this lang */ /* 0x08: id1+2 */ - if (read_32bit(offset + 0x04, sf) != 1) { + if (read_u32(offset + 0x04, sf) != 1) { VGM_LOG("UBI HX: wrong lang count near %lx\n", offset); goto fail; /* WavRes doesn't have this field */ } @@ -575,39 +587,39 @@ fail: } -static STREAMFILE * open_hx_streamfile(ubi_hx_header *hx, STREAMFILE *sf) { - STREAMFILE *streamData = NULL; +static STREAMFILE* open_hx_streamfile(ubi_hx_header* hx, STREAMFILE* sf) { + STREAMFILE* sb = NULL; if (!hx->is_external) return NULL; - streamData = open_streamfile_by_filename(sf, hx->resource_name); - if (streamData == NULL) { + sb = open_streamfile_by_filename(sf, hx->resource_name); + if (sb == NULL) { VGM_LOG("UBI HX: external stream '%s' not found\n", hx->resource_name); goto fail; } /* streams often have a "RIFF" with "fmt" and "data" but stream offset/size is already adjusted to skip them */ - return streamData; + return sb; fail: return NULL; } -static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *streamFile) { - STREAMFILE *streamTemp = NULL; - STREAMFILE *streamData = NULL; +static VGMSTREAM* init_vgmstream_ubi_hx_header(ubi_hx_header* hx, STREAMFILE* sf) { + STREAMFILE* temp_sf = NULL; + STREAMFILE* sb = NULL; VGMSTREAM* vgmstream = NULL; if (hx->is_external) { - streamTemp = open_hx_streamfile(hx, streamFile); - if (streamTemp == NULL) goto fail; - streamData = streamTemp; + temp_sf = open_hx_streamfile(hx, sf); + if (temp_sf == NULL) goto fail; + sb = temp_sf; } else { - streamData = streamFile; + sb = sf; } @@ -630,7 +642,7 @@ static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *s break; case UBI: - vgmstream->codec_data = init_ubi_adpcm(streamData, hx->stream_offset, vgmstream->channels); + vgmstream->codec_data = init_ubi_adpcm(sb, hx->stream_offset, vgmstream->channels); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_UBI_ADPCM; vgmstream->layout_type = layout_none; @@ -653,9 +665,9 @@ static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *s vgmstream->interleave_block_size = 0x08; /* dsp header at start offset */ - vgmstream->num_samples = read_32bitBE(hx->stream_offset + 0x00, streamData); - dsp_read_coefs_be(vgmstream, streamData, hx->stream_offset + 0x1c, 0x60); - dsp_read_hist_be (vgmstream, streamData, hx->stream_offset + 0x40, 0x60); + vgmstream->num_samples = read_s32be(hx->stream_offset + 0x00, sb); + dsp_read_coefs_be(vgmstream, sb, hx->stream_offset + 0x1c, 0x60); + dsp_read_hist_be (vgmstream, sb, hx->stream_offset + 0x40, 0x60); hx->stream_offset += 0x60 * hx->channels; hx->stream_size -= 0x60 * hx->channels; break; @@ -676,14 +688,14 @@ static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *s block_count = hx->stream_size / block_size; bytes = ffmpeg_make_riff_xma2(buf,0x200, hx->num_samples, hx->stream_size, hx->channels, hx->sample_rate, block_count, block_size); - vgmstream->codec_data = init_ffmpeg_header_offset(streamData, buf,bytes, hx->stream_offset,hx->stream_size); + vgmstream->codec_data = init_ffmpeg_header_offset(sb, buf,bytes, hx->stream_offset,hx->stream_size); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; vgmstream->num_samples = hx->num_samples; - xma_fix_raw_samples_ch(vgmstream, streamData, hx->stream_offset,hx->stream_size, hx->channels, 0,0); + xma_fix_raw_samples_ch(vgmstream, sb, hx->stream_offset,hx->stream_size, hx->channels, 0,0); break; } @@ -700,7 +712,7 @@ static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *s vgmstream->num_samples = atrac3_bytes_to_samples(hx->stream_size, block_align) - encoder_delay; - vgmstream->codec_data = init_ffmpeg_atrac3_raw(streamData, hx->stream_offset,hx->stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); + vgmstream->codec_data = init_ffmpeg_atrac3_raw(sb, hx->stream_offset, hx->stream_size, vgmstream->num_samples,vgmstream->channels,vgmstream->sample_rate, block_align, encoder_delay); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_FFmpeg; vgmstream->layout_type = layout_none; @@ -708,19 +720,34 @@ static VGMSTREAM * init_vgmstream_ubi_hx_header(ubi_hx_header *hx, STREAMFILE *s } #endif +#ifdef VGM_USE_MPEG + case MP3: { + mpeg_custom_config cfg = {0}; + + cfg.skip_samples = 0; /* ? */ + + vgmstream->codec_data = init_mpeg_custom(sb, hx->stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = mpeg_get_samples_clean(sb, hx->stream_offset, hx->stream_size, NULL, NULL, 1); + + break; + } +#endif default: goto fail; } strcpy(vgmstream->stream_name, hx->readable_name); - if (!vgmstream_open_stream(vgmstream, streamData, hx->stream_offset)) + if (!vgmstream_open_stream(vgmstream, sb, hx->stream_offset)) goto fail; - close_streamfile(streamTemp); + close_streamfile(temp_sf); return vgmstream; fail: - close_streamfile(streamTemp); + close_streamfile(temp_sf); close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_raki.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_raki.c index c039d649b..790a0497d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_raki.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_raki.c @@ -1,268 +1,269 @@ -#include "meta.h" -#include "../coding/coding.h" - - -/* RAKI - Ubisoft audio format [Rayman Legends, Just Dance 2017 (multi)] */ -VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset, offset, fmt_offset; - size_t header_size, data_size; - int big_endian; - int loop_flag, channel_count, block_align, bits_per_sample; - uint32_t platform, type; - - int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; - int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; - - - /* checks */ - /* .rak: Just Dance 2017 - * .ckd: Rayman Legends (technically .wav.ckd/rak) */ - if (!check_extensions(streamFile,"rak,ckd")) - goto fail; - - /* some games (ex. Rayman Legends PS3) have a 32b file type before the RAKI data. However - * offsets are absolute and expect the type exists, so it's part of the file and not an extraction defect. */ - if ((read_32bitBE(0x00,streamFile) == 0x52414B49)) /* "RAKI" */ - offset = 0x00; - else if ((read_32bitBE(0x04,streamFile) == 0x52414B49)) /* type varies between platforms (0x09, 0x0b) so ignore */ - offset = 0x04; - else - goto fail; - - /* 0x04: version? (0x00, 0x07, 0x0a, etc); */ - platform = read_32bitBE(offset+0x08,streamFile); /* string */ - type = read_32bitBE(offset+0x0c,streamFile); /* string */ - - switch(platform) { - case 0x57696920: /* "Wii " */ - case 0x43616665: /* "Cafe" */ - case 0x50533320: /* "PS3 " */ - case 0x58333630: /* "X360" */ - big_endian = 1; - read_32bit = read_32bitBE; - read_16bit = read_16bitBE; - break; - default: - big_endian = 0; - read_32bit = read_32bitLE; - read_16bit = read_16bitLE; - break; - } - - header_size = read_32bit(offset+0x10,streamFile); - start_offset = read_32bit(offset+0x14,streamFile); - /* 0x18: number of chunks */ - /* 0x1c: unk */ - - /* the format has a chunk offset table, and the first one always "fmt" and points - * to a RIFF "fmt"-style chunk (even for WiiU or PS3) */ - if (read_32bitBE(offset+0x20,streamFile) != 0x666D7420) goto fail; /* "fmt " */ - fmt_offset = read_32bit(offset+0x24,streamFile); - //fmt_size = read_32bit(off+0x28,streamFile); - - loop_flag = 0; /* not seen */ - channel_count = read_16bit(fmt_offset+0x2,streamFile); - block_align = read_16bit(fmt_offset+0xc,streamFile); - bits_per_sample = read_16bit(fmt_offset+0xe,streamFile); - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); - if (!vgmstream) goto fail; - - vgmstream->sample_rate = read_32bit(fmt_offset+0x4,streamFile); - vgmstream->meta_type = meta_UBI_RAKI; - - /* codecs have a "data" or equivalent chunk with the size/start_offset, but always agree with this */ - data_size = get_streamfile_size(streamFile) - start_offset; - - /* parse compound codec to simplify */ - switch(((uint64_t)platform << 32) | type) { - - case 0x57696E2070636D20: /* "Win pcm " */ - case 0x4F72626970636D20: /* "Orbipcm " (Orbis = PS4) */ - case 0x4E78202070636D20: /* "Nx pcm " (Nx = Switch) */ - /* chunks: "data" */ - vgmstream->coding_type = coding_PCM16LE; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x2; - - vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bits_per_sample); - break; - - case 0x57696E2061647063: /* "Win adpc" */ - /* chunks: "data" */ - vgmstream->coding_type = coding_MSADPCM; - vgmstream->layout_type = layout_none; - vgmstream->frame_size = block_align; - - vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, vgmstream->frame_size, channel_count); - - if (!msadpcm_check_coefs(streamFile, fmt_offset + 0x14)) - goto fail; - break; - - case 0x5769692061647063: /* "Wii adpc" */ - case 0x4361666561647063: /* "Cafeadpc" (Cafe = WiiU) */ - /* chunks: "datS" (stereo), "datL" (mono or full interleave), "datR" (full interleave), "data" equivalents */ - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x8; - - /* we need to know if the file uses "datL" and is full-interleave */ - if (channel_count > 1) { - off_t chunk_offset = offset+ 0x20 + 0xc; /* after "fmt" */ - while (chunk_offset < header_size) { - if (read_32bitBE(chunk_offset,streamFile) == 0x6461744C) { /* "datL" found */ - size_t chunk_size = read_32bit(chunk_offset+0x8,streamFile); - data_size = chunk_size * channel_count; /* to avoid counting the "datR" chunk */ - vgmstream->interleave_block_size = (4+4) + chunk_size; /* don't forget to skip the "datR"+size chunk */ - break; - } - chunk_offset += 0xc; - } - - /* not found? probably "datS" (regular stereo interleave) */ - } - - { - /* get coef offsets; could check "dspL" and "dspR" chunks after "fmt " better but whatevs (only "dspL" if mono) */ - off_t dsp_coefs = read_32bitBE(offset+0x30,streamFile); /* after "dspL"; spacing is consistent but could vary */ - dsp_read_coefs(vgmstream,streamFile, dsp_coefs+0x1c, 0x60, big_endian); - /* dsp_coefs + 0x00-0x1c: ? (special coefs or adpcm history?) */ - } - - vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); - break; - - case 0x4354520061647063: /* "CTR\0adpc" (Citrus = 3DS) */ - /* chunks: "dspL" (CWAV-L header), "dspR" (CWAV-R header), "cwav" ("data" equivalent) */ - vgmstream->coding_type = coding_NGC_DSP; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x8; - - /* reading could be improved but should work with some luck since most values are semi-fixed */ - if (channel_count > 1) { - /* find "dspL" pointing to "CWAV" header and read coefs (separate from data at start_offset) */ - off_t chunk_offset = offset+ 0x20 + 0xc; /* after "fmt" */ - while (chunk_offset < header_size) { - if (read_32bitBE(chunk_offset,streamFile) == 0x6473704C) { /* "dspL" found */ - off_t cwav_offset = read_32bit(chunk_offset+0x4,streamFile); - size_t cwav_size = read_32bit(chunk_offset+0x8,streamFile); - - dsp_read_coefs(vgmstream,streamFile, cwav_offset + 0x7c, cwav_size, big_endian); - break; - } - chunk_offset += 0xc; - } - } - else { - /* CWAV at start (a full CWAV, unlike the above) */ - dsp_read_coefs(vgmstream,streamFile, start_offset + 0x7c, 0x00, big_endian); - start_offset += 0xE0; - data_size = get_streamfile_size(streamFile) - start_offset; - } - - vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); - break; - - -#ifdef VGM_USE_MPEG - case 0x505333206D703320: { /* "PS3 mp3 " */ - /* chunks: "MARK" (optional seek table), "STRG" (optional description), "Msf " ("data" equivalent) */ - vgmstream->codec_data = init_mpeg(streamFile, start_offset, &vgmstream->coding_type, vgmstream->channels); - if (!vgmstream->codec_data) goto fail; - vgmstream->layout_type = layout_none; - - vgmstream->num_samples = mpeg_bytes_to_samples(data_size, vgmstream->codec_data); - break; - } -#endif - -#ifdef VGM_USE_FFMPEG - case 0x58333630786D6132: { /* "X360xma2" */ - /* chunks: "seek" (XMA2 seek table), "data" */ - uint8_t buf[100]; - int bytes, block_count; - - block_count = data_size / block_align + (data_size % block_align ? 1 : 0); - - bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_align); - vgmstream->codec_data = init_ffmpeg_header_offset(streamFile, buf,bytes, start_offset,data_size); - if ( !vgmstream->codec_data ) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - vgmstream->num_samples = read_32bit(fmt_offset+0x18,streamFile); - - xma_fix_raw_samples(vgmstream, streamFile, start_offset,data_size, 0, 0,0); /* should apply to num_samples? */ - break; - } -#endif - -#ifdef VGM_USE_ATRAC9 - case 0x5649544161743920: { /* "VITAat9 " */ - /* chunks: "fact" (equivalent to a RIFF "fact", num_samples + skip_samples), "data" */ - atrac9_config cfg = {0}; - - cfg.channels = vgmstream->channels; - cfg.config_data = read_32bitBE(fmt_offset+0x2c,streamFile); - cfg.encoder_delay = read_32bit(fmt_offset+0x3c,streamFile); - - vgmstream->codec_data = init_atrac9(&cfg); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_ATRAC9; - vgmstream->layout_type = layout_none; - - /* could get the "fact" offset but seems it always follows "fmt " */ - vgmstream->num_samples = read_32bit(fmt_offset+0x34,streamFile); - break; - } -#endif - -#ifdef VGM_USE_FFMPEG - case 0x4E7820204E782020: { /* "Nx Nx " */ - /* chunks: "MARK" (optional seek table), "STRG" (optional description) */ - size_t skip, opus_size; - - /* a standard Switch Opus header */ - skip = read_32bitLE(start_offset + 0x1c, streamFile); - opus_size = read_32bitLE(start_offset + 0x10, streamFile) + 0x08; - start_offset += opus_size; - data_size -= opus_size; - - vgmstream->codec_data = init_ffmpeg_switch_opus(streamFile, start_offset,data_size, vgmstream->channels, skip, vgmstream->sample_rate); - if (!vgmstream->codec_data) goto fail; - vgmstream->coding_type = coding_FFmpeg; - vgmstream->layout_type = layout_none; - - { - off_t chunk_offset = offset + 0x20 + 0xc; /* after "fmt" */ - while (chunk_offset < header_size) { - if (read_32bitBE(chunk_offset,streamFile) == 0x4164496E) { /*"AdIn" additional info */ - off_t adin_offset = read_32bitLE(chunk_offset+0x04,streamFile); - vgmstream->num_samples = read_32bitLE(adin_offset,streamFile); - break; - } - chunk_offset += 0xc; - } - } - - break; - } -#endif - - default: - VGM_LOG("RAKI: unknown platform %x and type %x\n", platform, type); - goto fail; - } - - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} +#include "meta.h" +#include "../coding/coding.h" + + +/* RAKI - Ubisoft audio format [Rayman Legends, Just Dance 2017 (multi)] */ +VGMSTREAM * init_vgmstream_ubi_raki(STREAMFILE *sf) { + VGMSTREAM * vgmstream = NULL; + off_t start_offset, offset, fmt_offset; + size_t header_size, data_size; + int big_endian; + int loop_flag, channel_count, block_align, bits_per_sample; + uint32_t platform, type; + + int32_t (*read_32bit)(off_t,STREAMFILE*) = NULL; + int16_t (*read_16bit)(off_t,STREAMFILE*) = NULL; + + + /* checks */ + /* .rak: Just Dance 2017 + * .ckd: Rayman Legends (technically .wav.ckd/rak) */ + if (!check_extensions(sf,"rak,ckd")) + goto fail; + + /* some games (ex. Rayman Legends PS3) have a 32b file type before the RAKI data. However + * offsets are absolute and expect the type exists, so it's part of the file and not an extraction defect. */ + if ((read_32bitBE(0x00,sf) == 0x52414B49)) /* "RAKI" */ + offset = 0x00; + else if ((read_32bitBE(0x04,sf) == 0x52414B49)) /* type varies between platforms (0x09, 0x0b) so ignore */ + offset = 0x04; + else + goto fail; + + /* 0x04: version? (0x00, 0x07, 0x0a, etc); */ + platform = read_32bitBE(offset+0x08,sf); /* string */ + type = read_32bitBE(offset+0x0c,sf); /* string */ + + switch(platform) { + case 0x57696920: /* "Wii " */ + case 0x43616665: /* "Cafe" */ + case 0x50533320: /* "PS3 " */ + case 0x58333630: /* "X360" */ + big_endian = 1; + read_32bit = read_32bitBE; + read_16bit = read_16bitBE; + break; + default: + big_endian = 0; + read_32bit = read_32bitLE; + read_16bit = read_16bitLE; + break; + } + + header_size = read_32bit(offset+0x10,sf); + start_offset = read_32bit(offset+0x14,sf); + /* 0x18: number of chunks */ + /* 0x1c: unk */ + + /* the format has a chunk offset table, and the first one always "fmt" and points + * to a RIFF "fmt"-style chunk (even for WiiU or PS3) */ + if (read_32bitBE(offset+0x20,sf) != 0x666D7420) goto fail; /* "fmt " */ + fmt_offset = read_32bit(offset+0x24,sf); + //fmt_size = read_32bit(off+0x28,sf); + + loop_flag = 0; /* not seen */ + channel_count = read_16bit(fmt_offset+0x2,sf); + block_align = read_16bit(fmt_offset+0xc,sf); + bits_per_sample = read_16bit(fmt_offset+0xe,sf); + + /* build the VGMSTREAM */ + vgmstream = allocate_vgmstream(channel_count,loop_flag); + if (!vgmstream) goto fail; + + vgmstream->sample_rate = read_32bit(fmt_offset+0x4,sf); + vgmstream->meta_type = meta_UBI_RAKI; + + /* codecs have a "data" or equivalent chunk with the size/start_offset, but always agree with this */ + data_size = get_streamfile_size(sf) - start_offset; + + /* parse compound codec to simplify */ + switch(((uint64_t)platform << 32) | type) { + + case 0x57696E2070636D20: /* "Win pcm " */ + case 0x4F72626970636D20: /* "Orbipcm " (Orbis = PS4) */ + case 0x4E78202070636D20: /* "Nx pcm " (Nx = Switch) */ + /* chunks: "data" */ + vgmstream->coding_type = coding_PCM16LE; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x2; + + vgmstream->num_samples = pcm_bytes_to_samples(data_size, channel_count, bits_per_sample); + break; + + case 0x57696E2061647063: /* "Win adpc" */ + /* chunks: "data" */ + vgmstream->coding_type = coding_MSADPCM; + vgmstream->layout_type = layout_none; + vgmstream->frame_size = block_align; + + vgmstream->num_samples = msadpcm_bytes_to_samples(data_size, vgmstream->frame_size, channel_count); + + if (!msadpcm_check_coefs(sf, fmt_offset + 0x14)) + goto fail; + break; + + case 0x5769692061647063: /* "Wii adpc" */ + case 0x4361666561647063: /* "Cafeadpc" (Cafe = WiiU) */ + /* chunks: "datS" (stereo), "datL" (mono or full interleave), "datR" (full interleave), "data" equivalents */ + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x8; + + /* we need to know if the file uses "datL" and is full-interleave */ + if (channel_count > 1) { + off_t chunk_offset = offset+ 0x20 + 0xc; /* after "fmt" */ + while (chunk_offset < header_size) { + if (read_32bitBE(chunk_offset,sf) == 0x6461744C) { /* "datL" found */ + size_t chunk_size = read_32bit(chunk_offset+0x8,sf); + data_size = chunk_size * channel_count; /* to avoid counting the "datR" chunk */ + vgmstream->interleave_block_size = (4+4) + chunk_size; /* don't forget to skip the "datR"+size chunk */ + break; + } + chunk_offset += 0xc; + } + + /* not found? probably "datS" (regular stereo interleave) */ + } + + { + /* get coef offsets; could check "dspL" and "dspR" chunks after "fmt " better but whatevs (only "dspL" if mono) */ + off_t dsp_coefs = read_32bitBE(offset+0x30,sf); /* after "dspL"; spacing is consistent but could vary */ + dsp_read_coefs(vgmstream,sf, dsp_coefs+0x1c, 0x60, big_endian); + /* dsp_coefs + 0x00-0x1c: ? (special coefs or adpcm history?) */ + } + + vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); + break; + + case 0x4354520061647063: /* "CTR\0adpc" (Citrus = 3DS) */ + /* chunks: "dspL" (CWAV-L header), "dspR" (CWAV-R header), "cwav" ("data" equivalent) */ + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x8; + + /* reading could be improved but should work with some luck since most values are semi-fixed */ + if (channel_count > 1) { + /* find "dspL" pointing to "CWAV" header and read coefs (separate from data at start_offset) */ + off_t chunk_offset = offset+ 0x20 + 0xc; /* after "fmt" */ + while (chunk_offset < header_size) { + if (read_32bitBE(chunk_offset,sf) == 0x6473704C) { /* "dspL" found */ + off_t cwav_offset = read_32bit(chunk_offset+0x4,sf); + size_t cwav_size = read_32bit(chunk_offset+0x8,sf); + + dsp_read_coefs(vgmstream,sf, cwav_offset + 0x7c, cwav_size, big_endian); + break; + } + chunk_offset += 0xc; + } + } + else { + /* CWAV at start (a full CWAV, unlike the above) */ + dsp_read_coefs(vgmstream,sf, start_offset + 0x7c, 0x00, big_endian); + start_offset += 0xE0; + data_size = get_streamfile_size(sf) - start_offset; + } + + vgmstream->num_samples = dsp_bytes_to_samples(data_size, channel_count); + break; + + +#ifdef VGM_USE_MPEG + case 0x505333206D703320: { /* "PS3 mp3 " */ + /* chunks: "MARK" (optional seek table), "STRG" (optional description), "Msf " ("data" equivalent) */ + vgmstream->codec_data = init_mpeg(sf, start_offset, &vgmstream->coding_type, vgmstream->channels); + if (!vgmstream->codec_data) goto fail; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = mpeg_bytes_to_samples(data_size, vgmstream->codec_data); + break; + } +#endif + +#ifdef VGM_USE_FFMPEG + case 0x58333630786D6132: { /* "X360xma2" */ + /* chunks: "seek" (XMA2 seek table), "data" */ + uint8_t buf[100]; + int bytes, block_count; + if (!block_align) goto fail; + + block_count = data_size / block_align + (data_size % block_align ? 1 : 0); + + bytes = ffmpeg_make_riff_xma2(buf, 100, vgmstream->num_samples, data_size, vgmstream->channels, vgmstream->sample_rate, block_count, block_align); + vgmstream->codec_data = init_ffmpeg_header_offset(sf, buf,bytes, start_offset,data_size); + if ( !vgmstream->codec_data ) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + vgmstream->num_samples = read_32bit(fmt_offset+0x18,sf); + + xma_fix_raw_samples(vgmstream, sf, start_offset,data_size, 0, 0,0); /* should apply to num_samples? */ + break; + } +#endif + +#ifdef VGM_USE_ATRAC9 + case 0x5649544161743920: { /* "VITAat9 " */ + /* chunks: "fact" (equivalent to a RIFF "fact", num_samples + skip_samples), "data" */ + atrac9_config cfg = {0}; + + cfg.channels = vgmstream->channels; + cfg.config_data = read_32bitBE(fmt_offset+0x2c,sf); + cfg.encoder_delay = read_32bit(fmt_offset+0x3c,sf); + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + + /* could get the "fact" offset but seems it always follows "fmt " */ + vgmstream->num_samples = read_32bit(fmt_offset+0x34,sf); + break; + } +#endif + +#ifdef VGM_USE_FFMPEG + case 0x4E7820204E782020: { /* "Nx Nx " */ + /* chunks: "MARK" (optional seek table), "STRG" (optional description) */ + size_t skip, opus_size; + + /* a standard Switch Opus header */ + skip = read_32bitLE(start_offset + 0x1c, sf); + opus_size = read_32bitLE(start_offset + 0x10, sf) + 0x08; + start_offset += opus_size; + data_size -= opus_size; + + vgmstream->codec_data = init_ffmpeg_switch_opus(sf, start_offset,data_size, vgmstream->channels, skip, vgmstream->sample_rate); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + { + off_t chunk_offset = offset + 0x20 + 0xc; /* after "fmt" */ + while (chunk_offset < header_size) { + if (read_32bitBE(chunk_offset,sf) == 0x4164496E) { /*"AdIn" additional info */ + off_t adin_offset = read_32bitLE(chunk_offset+0x04,sf); + vgmstream->num_samples = read_32bitLE(adin_offset,sf); + break; + } + chunk_offset += 0xc; + } + } + + break; + } +#endif + + default: + VGM_LOG("RAKI: unknown platform %x and type %x\n", platform, type); + goto fail; + } + + + if (!vgmstream_open_stream(vgmstream,sf,start_offset)) + goto fail; + return vgmstream; + +fail: + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c index fe0f79938..a5ab0501c 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_sb.c @@ -1,3 +1,4 @@ +#include #include "meta.h" #include "../layout/layout.h" #include "../coding/coding.h" diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xa.c b/Frameworks/vgmstream/vgmstream/src/meta/xa.c index 49417cc57..5f17f21d8 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xa.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xa.c @@ -3,17 +3,17 @@ #include "../coding/coding.h" -static int xa_read_subsongs(STREAMFILE *sf, int target_subsong, off_t start, uint16_t *p_stream_config, off_t *p_stream_offset, size_t *p_stream_size, int *p_form2); -static int xa_check_format(STREAMFILE *sf, off_t offset, int is_blocked); +static int xa_read_subsongs(STREAMFILE* sf, int target_subsong, off_t start, uint16_t* p_stream_config, off_t* p_stream_offset, size_t* p_stream_size, int* p_form2); +static int xa_check_format(STREAMFILE* sf, off_t offset, int is_blocked); /* XA - from Sony PS1 and Philips CD-i CD audio, also Saturn streams */ -VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; +VGMSTREAM* init_vgmstream_xa(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; off_t start_offset; - int loop_flag = 0, channel_count, sample_rate; + int loop_flag = 0, channels, sample_rate, bps; int is_riff = 0, is_blocked = 0, is_form2 = 0; size_t stream_size = 0; - int total_subsongs = 0, target_subsong = streamFile->stream_index; + int total_subsongs = 0, target_subsong = sf->stream_index; uint16_t target_config = 0; @@ -23,26 +23,27 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) { * .str: often videos and sometimes speech/music * .adp: Phantasy Star Collection (SAT) raw XA * .pxa: Mortal Kombat 4 (PS1) + * .grn: Micro Machines (CDi) * (extensionless): bigfiles [Castlevania: Symphony of the Night (PS1)] */ - if (!check_extensions(streamFile,"xa,str,adp,pxa,")) + if (!check_extensions(sf,"xa,str,adp,pxa,grn,")) goto fail; /* Proper XA comes in raw (BIN 2352 mode2/form2) CD sectors, that contain XA subheaders. * Also has minimal support for headerless (ISO 2048 mode1/data) mode. */ /* check RIFF header = raw (optional, added when ripping and not part of the CD data) */ - if (read_u32be(0x00,streamFile) == 0x52494646 && /* "RIFF" */ - read_u32be(0x08,streamFile) == 0x43445841 && /* "CDXA" */ - read_u32be(0x0C,streamFile) == 0x666D7420) { /* "fmt " */ + if (read_u32be(0x00,sf) == 0x52494646 && /* "RIFF" */ + read_u32be(0x08,sf) == 0x43445841 && /* "CDXA" */ + read_u32be(0x0C,sf) == 0x666D7420) { /* "fmt " */ is_blocked = 1; is_riff = 1; start_offset = 0x2c; /* after "data", ignore RIFF values as often are wrong */ } else { /* sector sync word = raw */ - if (read_u32be(0x00,streamFile) == 0x00FFFFFF && - read_u32be(0x04,streamFile) == 0xFFFFFFFF && - read_u32be(0x08,streamFile) == 0xFFFFFF00) { + if (read_u32be(0x00,sf) == 0x00FFFFFF && + read_u32be(0x04,sf) == 0xFFFFFFFF && + read_u32be(0x08,sf) == 0xFFFFFF00) { is_blocked = 1; start_offset = 0x00; } @@ -53,26 +54,26 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) { } /* test for XA data, since format is raw-ish (with RIFF it's assumed to be ok) */ - if (!is_riff && !xa_check_format(streamFile, start_offset, is_blocked)) + if (!is_riff && !xa_check_format(sf, start_offset, is_blocked)) goto fail; /* find subsongs as XA can interleave sectors using 'file' and 'channel' makers (see blocked_xa.c) */ if (/*!is_riff &&*/ is_blocked) { - total_subsongs = xa_read_subsongs(streamFile, target_subsong, start_offset, &target_config, &start_offset, &stream_size, &is_form2); + total_subsongs = xa_read_subsongs(sf, target_subsong, start_offset, &target_config, &start_offset, &stream_size, &is_form2); if (total_subsongs <= 0) goto fail; } else { - stream_size = get_streamfile_size(streamFile) - start_offset; + stream_size = get_streamfile_size(sf) - start_offset; } /* data is ok: parse header */ if (is_blocked) { /* parse 0x18 sector header (also see blocked_xa.c) */ - uint8_t xa_header = read_u8(start_offset + 0x13,streamFile); + uint8_t xa_header = read_u8(start_offset + 0x13,sf); switch((xa_header >> 0) & 3) { /* 0..1: mono/stereo */ - case 0: channel_count = 1; break; - case 1: channel_count = 2; break; + case 0: channels = 1; break; + case 1: channels = 2; break; default: goto fail; } switch((xa_header >> 2) & 3) { /* 2..3: sample rate */ @@ -80,11 +81,10 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) { case 1: sample_rate = 18900; break; default: goto fail; } - switch((xa_header >> 4) & 3) { /* 4..5: bits per sample (0=4-bit ADPCM, 1=8-bit ADPCM) */ - case 0: break; - default: /* PS1 games only do 4-bit */ - VGM_LOG("XA: unknown bits per sample found\n"); - goto fail; + switch((xa_header >> 4) & 3) { /* 4..5: bits per sample */ + case 0: bps = 4; break; /* PS1 games only do 4-bit ADPCM */ + case 1: bps = 8; break; /* Micro Machines (CDi) */ + default: goto fail; } switch((xa_header >> 6) & 1) { /* 6: emphasis (applies a filter) */ case 0: break; @@ -101,38 +101,44 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) { } else { /* headerless */ - if (check_extensions(streamFile,"adp")) { + if (check_extensions(sf,"adp")) { /* Phantasy Star Collection (SAT) raw files */ /* most are stereo, though a few (mainly sfx banks, sometimes using .bin) are mono */ char filename[PATH_LIMIT] = {0}; - get_streamfile_filename(streamFile, filename,PATH_LIMIT); + get_streamfile_filename(sf, filename,PATH_LIMIT); /* detect PS1 mono files, very lame but whatevs, no way to detect XA mono/stereo */ if (filename[0]=='P' && filename[1]=='S' && filename[2]=='1' && filename[3]=='S') { - channel_count = 1; + channels = 1; sample_rate = 22050; } else { - channel_count = 2; + channels = 2; sample_rate = 44100; } + bps = 4; } else { /* incorrectly ripped standard XA */ - channel_count = 2; + channels = 2; sample_rate = 37800; + bps = 4; } } + /* untested */ + if (bps == 8 && channels == 1) + goto fail; + /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count,loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_XA; vgmstream->sample_rate = sample_rate; - vgmstream->coding_type = coding_XA; + vgmstream->coding_type = bps == 8 ? coding_XA8 : coding_XA; vgmstream->layout_type = is_blocked ? layout_blocked_xa : layout_none; if (is_blocked) { vgmstream->codec_config = target_config; @@ -144,9 +150,9 @@ VGMSTREAM * init_vgmstream_xa(STREAMFILE *streamFile) { } } - vgmstream->num_samples = xa_bytes_to_samples(stream_size, channel_count, is_blocked, is_form2); + vgmstream->num_samples = xa_bytes_to_samples(stream_size, channels, is_blocked, is_form2, bps); - if ( !vgmstream_open_stream(vgmstream, streamFile, start_offset) ) + if (!vgmstream_open_stream(vgmstream, sf, start_offset)) goto fail; return vgmstream; @@ -230,7 +236,7 @@ typedef struct xa_subsong_t { * * Bigfiles that paste tons of XA together are slow to parse since we need to read every sector to * count totals, but XA subsong handling is mainly for educational purposes. */ -static int xa_read_subsongs(STREAMFILE *sf, int target_subsong, off_t start, uint16_t *p_stream_config, off_t *p_stream_offset, size_t *p_stream_size, int *p_form2) { +static int xa_read_subsongs(STREAMFILE* sf, int target_subsong, off_t start, uint16_t* p_stream_config, off_t* p_stream_offset, size_t* p_stream_size, int* p_form2) { xa_subsong_t *cur_subsong = NULL; xa_subsong_t subsongs[XA_SUBSONG_MAX] = {0}; const size_t sector_size = 0x930; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c index 2eb63586d..3383ce844 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb.c @@ -351,10 +351,11 @@ VGMSTREAM* init_vgmstream_xwb(STREAMFILE* sf) { /* Stardew Valley (Switch), Skulls of the Shogun (Switch): full interleaved DSPs (including headers) */ xwb.codec = DSP; } - else if (xwb.version == XACT3_0_MAX && xwb.codec == XMA2 - && xwb.bits_per_sample == 0x01 && xwb.block_align == 0x04 - && xwb.data_size == 0x4e0a1000) { /* some kind of id? */ - /* Stardew Valley (Vita), standard RIFF with ATRAC9 */ + else if (xwb.version == XACT3_0_MAX && (xwb.codec == XMA2 || xwb.codec == PCM) + && xwb.bits_per_sample == 0x01 && xwb.block_align == 0x02*xwb.channels + && is_id32be(xwb.stream_offset, sf, "RIFF") /* clashes with XMA2 */ + /*&& xwb.data_size == 0x4e0a1000*/) { /* some kind of id in Stardew Valley? */ + /* Stardew Valley (Vita), Owlboy (PS4): standard RIFF with ATRAC9 */ xwb.codec = ATRAC9_RIFF; } diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 0e52a00f0..e74bab1dd 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -522,6 +522,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_mjb_mjh, init_vgmstream_mzrt_v1, init_vgmstream_bsnf, + init_vgmstream_tac, /* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */ init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */ diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 78cb69cf1..9cb5dfdeb 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -107,7 +107,8 @@ typedef enum { coding_G721, /* CCITT G.721 */ - coding_XA, /* CD-ROM XA */ + coding_XA, /* CD-ROM XA 4-bit */ + coding_XA8, /* CD-ROM XA 8-bit */ coding_PSX, /* Sony PS ADPCM (VAG) */ coding_PSX_badflags, /* Sony PS ADPCM with custom flag byte */ coding_PSX_cfg, /* Sony PS ADPCM with configurable frame size (int math) */ @@ -198,6 +199,7 @@ typedef enum { coding_CIRCUS_VQ, /* Circus VQ */ coding_RELIC, /* Relic Codec (DCT-based) */ coding_CRI_HCA, /* CRI High Compression Audio (MDCT-based) */ + coding_TAC, /* tri-Ace Codec (MDCT-based) */ #ifdef VGM_USE_VORBIS coding_OGG_VORBIS, /* Xiph Vorbis with Ogg layer (MDCT-based) */ @@ -759,6 +761,7 @@ typedef enum { meta_KTAC, meta_MJB_MJH, meta_BSNF, + meta_TAC, } meta_t; /* standard WAVEFORMATEXTENSIBLE speaker positions */ @@ -1053,7 +1056,7 @@ typedef struct { uint64_t size; // max size within the streamfile uint64_t logical_offset; // computed offset FFmpeg sees (including fake header) uint64_t logical_size; // computed size FFmpeg sees (including fake header) - + uint64_t header_size; // fake header (parseable by FFmpeg) prepended on reads uint8_t* header_block; // fake header data (ie. RIFF) @@ -1066,7 +1069,7 @@ typedef struct { int64_t totalSamples; // estimated count (may not be accurate for some demuxers) int64_t skipSamples; // number of start samples that will be skipped (encoder delay), for looping adjustments int streamCount; // number of FFmpeg audio streams - + /*** internal state ***/ // config int channel_remap_set; @@ -1078,7 +1081,7 @@ typedef struct { // FFmpeg context used for metadata AVCodec *codec; - + // FFmpeg decoder state unsigned char *buffer; AVIOContext *ioCtx;