From a2f64ac18ee5c51cbb8226e72e8c69e4563ae17e Mon Sep 17 00:00:00 2001 From: faxx1080 Date: Sat, 20 Jan 2024 11:25:05 -0500 Subject: [PATCH] Sync with r1896 --- .../vgmstream/vgmstream/src/base/decode.c | 11 +- .../vgmstream/src/coding/circus_decoder_lib.c | 2 +- .../vgmstream/vgmstream/src/coding/coding.h | 1 + .../vgmstream/src/coding/ima_decoder.c | 70 +- .../src/coding/mpeg_custom_utils_ahx.c | 25 +- Frameworks/vgmstream/vgmstream/src/formats.c | 9 +- .../vgmstream/src/layout/blocked_awc.c | 52 +- Frameworks/vgmstream/vgmstream/src/meta/acb.c | 126 +++- Frameworks/vgmstream/vgmstream/src/meta/adm.c | 37 +- .../src/meta/{pc_adp_otns.c => adp_qd.c} | 0 .../vgmstream/vgmstream/src/meta/adx_keys.h | 7 +- .../src/meta/{dsp_adx.c => adx_monster.c} | 0 Frameworks/vgmstream/vgmstream/src/meta/awb.c | 20 +- Frameworks/vgmstream/vgmstream/src/meta/awc.c | 613 ++++++++++-------- .../vgmstream/src/meta/awc_streamfile.h | 253 ++++++++ .../vgmstream/src/meta/awc_xma_streamfile.h | 114 ---- .../vgmstream/vgmstream/src/meta/bkhd.c | 5 + .../vgmstream/vgmstream/src/meta/bnk_sony.c | 192 +++--- Frameworks/vgmstream/vgmstream/src/meta/cpk.c | 4 +- .../vgmstream/vgmstream/src/meta/encrypted.c | 4 +- .../vgmstream/vgmstream/src/meta/fsb_keys.h | 9 +- .../vgmstream/vgmstream/src/meta/hca_keys.h | 18 + .../vgmstream/vgmstream/src/meta/idtech.c | 17 +- .../vgmstream/vgmstream/src/meta/ktsc.c | 3 +- .../vgmstream/vgmstream/src/meta/ktsr.c | 205 ++++-- .../vgmstream/src/meta/ktsr_streamfile.h | 152 +++++ .../vgmstream/vgmstream/src/meta/meta.h | 12 +- Frameworks/vgmstream/vgmstream/src/meta/nub.c | 130 +++- .../vgmstream/src/meta/{nxa.c => nxa1.c} | 10 +- .../vgmstream/vgmstream/src/meta/nxof.c | 53 ++ Frameworks/vgmstream/vgmstream/src/meta/psb.c | 2 +- .../vgmstream/vgmstream/src/meta/riff.c | 14 +- .../vgmstream/src/meta/rstm_rockstar.c | 33 +- .../vgmstream/vgmstream/src/meta/sps_n1.c | 82 ++- .../vgmstream/vgmstream/src/meta/sthd.c | 1 - .../vgmstream/vgmstream/src/meta/str_wav.c | 111 +++- .../vgmstream/src/meta/str_wav_streamfile.h | 21 + .../vgmstream/vgmstream/src/meta/txth.c | 2 +- .../vgmstream/vgmstream/src/meta/ubi_bao.c | 2 +- .../vgmstream/vgmstream/src/meta/wwise.c | 1 + .../vgmstream/vgmstream/src/meta/xa_xa30.c | 8 +- .../vgmstream/src/util/bitstream_lsb.h | 9 +- .../vgmstream/src/util/bitstream_msb.h | 35 +- .../vgmstream/src/util/cipher_blowfish.c | 303 +++++++++ .../vgmstream/src/util/cipher_blowfish.h | 14 + .../vgmstream/src/util/layout_utils.c | 86 ++- .../vgmstream/src/util/layout_utils.h | 9 + Frameworks/vgmstream/vgmstream/src/util/log.h | 2 +- .../vgmstream/vgmstream/src/util/reader_put.h | 4 - .../vgmstream/vgmstream/src/vgmstream.c | 34 +- .../vgmstream/vgmstream/src/vgmstream_types.h | 4 +- 51 files changed, 2219 insertions(+), 712 deletions(-) rename Frameworks/vgmstream/vgmstream/src/meta/{pc_adp_otns.c => adp_qd.c} (100%) rename Frameworks/vgmstream/vgmstream/src/meta/{dsp_adx.c => adx_monster.c} (100%) create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/awc_streamfile.h delete mode 100644 Frameworks/vgmstream/vgmstream/src/meta/awc_xma_streamfile.h create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/ktsr_streamfile.h rename Frameworks/vgmstream/vgmstream/src/meta/{nxa.c => nxa1.c} (92%) create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/nxof.c create mode 100644 Frameworks/vgmstream/vgmstream/src/meta/str_wav_streamfile.h create mode 100644 Frameworks/vgmstream/vgmstream/src/util/cipher_blowfish.c create mode 100644 Frameworks/vgmstream/vgmstream/src/util/cipher_blowfish.h diff --git a/Frameworks/vgmstream/vgmstream/src/base/decode.c b/Frameworks/vgmstream/vgmstream/src/base/decode.c index cd92ca140..af29823e8 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/decode.c +++ b/Frameworks/vgmstream/vgmstream/src/base/decode.c @@ -418,7 +418,8 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) { case coding_XBOX_IMA_int: case coding_FSB_IMA: case coding_WWISE_IMA: - case coding_CD_IMA: + case coding_CD_IMA: /* (0x24 - 0x04) * 2 */ + case coding_CRANKCASE_IMA: /* (0x23 - 0x3) * 2 */ return 64; case coding_APPLE_IMA4: return 64; @@ -654,6 +655,8 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) { case coding_WWISE_IMA: case coding_CD_IMA: return 0x24; +case coding_CRANKCASE_IMA: + return 0x23; case coding_XBOX_IMA_mch: case coding_FSB_IMA: return 0x24 * vgmstream->channels; @@ -1264,6 +1267,12 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_ vgmstream->channels, vgmstream->samples_into_block, samples_to_do); } break; +case coding_CRANKCASE_IMA: + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_crankcase_ima(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do); + } + break; case coding_WS: for (ch = 0; ch < vgmstream->channels; ch++) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/circus_decoder_lib.c b/Frameworks/vgmstream/vgmstream/src/coding/circus_decoder_lib.c index e0c8a1fb2..a9e108f89 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/circus_decoder_lib.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/circus_decoder_lib.c @@ -330,7 +330,7 @@ circus_handle_t* circus_init(off_t start, uint8_t codec, uint8_t flags) { handle->flags = flags; //(config >> 8) & 0xFF; scale_index = (handle->flags & 0xF); - if (scale_index > 5) goto fail; + if (scale_index >= 5) goto fail; handle->scales = scale_table[scale_index]; if (handle->codec == XPCM_CODEC_VQ_DEFLATE) { diff --git a/Frameworks/vgmstream/vgmstream/src/coding/coding.h b/Frameworks/vgmstream/vgmstream/src/coding/coding.h index 7425dcf79..d1642dab3 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/coding.h +++ b/Frameworks/vgmstream/vgmstream/src/coding/coding.h @@ -45,6 +45,7 @@ void decode_ubi_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspaci void decode_ubi_sce_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel); void decode_h4m_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, uint16_t frame_format); void decode_cd_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); +void decode_crankcase_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); size_t ima_bytes_to_samples(size_t bytes, int channels); size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels); size_t xbox_ima_bytes_to_samples(size_t bytes, int channels); diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c index f5533581c..a511e47eb 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c @@ -35,8 +35,8 @@ static const int IMA_IndexTable[16] = { /* Original IMA expansion, using shift+ADDs to avoid MULs (slow back then) */ -static void std_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { - int sample_nibble, sample_decoded, step, delta; +static void std_ima_expand_nibble_data(uint8_t byte, int shift, int32_t* hist1, int32_t* index) { + int code, sample, step, delta; /* simplified through math from: * - diff = (code + 1/2) * (step / 4) @@ -44,21 +44,26 @@ static void std_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, * > diff = (step * nibble / 4) + (step / 8) * final diff = [signed] (step / 8) + (step / 4) + (step / 2) + (step) [when code = 4+2+1] */ - sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; /* ADPCM code */ - sample_decoded = *hist1; /* predictor value */ - step = ADPCMTable[*step_index]; /* current step */ + code = (byte >> shift) & 0xf; + sample = *hist1; /* predictor value */ + step = ADPCMTable[*index]; /* current step */ delta = step >> 3; - if (sample_nibble & 1) delta += step >> 2; - if (sample_nibble & 2) delta += step >> 1; - if (sample_nibble & 4) delta += step; - if (sample_nibble & 8) delta = -delta; - sample_decoded += delta; + if (code & 1) delta += step >> 2; + if (code & 2) delta += step >> 1; + if (code & 4) delta += step; + if (code & 8) delta = -delta; + sample += delta; - *hist1 = clamp16(sample_decoded); - *step_index += IMA_IndexTable[sample_nibble]; - if (*step_index < 0) *step_index=0; - if (*step_index > 88) *step_index=88; + *hist1 = clamp16(sample); + *index += IMA_IndexTable[code]; + if (*index < 0) *index = 0; + if (*index > 88) *index = 88; +} + +static void std_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { + uint8_t byte = read_u8(byte_offset,stream->streamfile); + std_ima_expand_nibble_data(byte, nibble_shift, hist1, step_index); } /* Apple's IMA variation. Exactly the same except it uses 16b history (probably more sensitive to overflow/sign extend?) */ @@ -1287,6 +1292,43 @@ void decode_cd_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacin stream->adpcm_step_index = step_index; } +/* Crankcase Audio IMA, from libs (internally CrankcaseAudio::ADPCM and revadpcm) */ +void decode_crankcase_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { + uint8_t frame[0x23] = {0}; + int i, frames_in, sample_pos = 0, block_samples, frame_size; + int32_t hist1 = stream->adpcm_history1_32; + int step_index = stream->adpcm_step_index; + uint32_t frame_offset; + + /* external interleave (fixed size), mono */ + frame_size = 0x23; + block_samples = (frame_size - 0x3) * 2; + frames_in = first_sample / block_samples; + first_sample = first_sample % block_samples; + + frame_offset = stream->offset + frame_size * frames_in; + read_streamfile(frame, frame_offset, frame_size, stream->streamfile); /* ignore EOF errors */ + + /* normal header (hist+step), mono */ + if (first_sample == 0) { + hist1 = get_s16be(frame + 0x00); + step_index = get_u8(frame + 0x02); /* no reserved value at 0x03 unlike other IMAs (misaligned reads?) */ + step_index = _clamp_s32(step_index, 0, 88); + } + + /* decode nibbles (layout: straight in mono) */ + for (i = first_sample; i < first_sample + samples_to_do; i++) { + int pos = 0x03 + (i/2); + int shift = (i & 1 ? 4:0); /* low first */ + + std_ima_expand_nibble_data(frame[pos], shift, &hist1, &step_index); + outbuf[sample_pos] = (short)(hist1); /* internally output to float using "sample / 32767.0" */ + sample_pos += channelspacing; + } + + stream->adpcm_history1_32 = hist1; + stream->adpcm_step_index = step_index; +} /* ************************************************************* */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ahx.c b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ahx.c index 2a9a01f42..dc49ec13f 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ahx.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/mpeg_custom_utils_ahx.c @@ -188,28 +188,33 @@ fail: } -#define AHX_KEY_BUFFER 0x2000 -#define AHX_KEY_TEST_FRAMES 20 /* wrong keys may work ok in some frames */ +#define AHX_KEY_BUFFER 0x1000 /* not too big since it's read per new key */ +#define AHX_KEY_TEST_FRAMES 25 /* wrong keys may work ok in some frames (specially blank) */ /* check if current key ends properly in frame syncs */ int test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey) { - int bytes; + int bytes = 0; uint8_t buf[AHX_KEY_BUFFER]; const int buf_size = sizeof(buf); int pos = 0; uint32_t base_sync, curr_sync; - bytes = read_streamfile(buf, offset, buf_size, sf); - //if (bytes != buf_size) goto fail; /* possible in small AHX */ - base_sync = get_u32be(buf + 0x00); for (int i = 0; i < AHX_KEY_TEST_FRAMES; i++) { + if (bytes < MPEG_AHX_EXPECTED_FRAME_SIZE) { + offset += pos; + pos = 0; + bytes = read_streamfile(buf, offset, buf_size, sf); + //if (bytes != buf_size) goto fail; /* possible in small AHX */ + base_sync = get_u32be(buf + 0x00); + } - int size = ahx_decrypt(buf + pos, bytes, crikey); - if (size <= 0 || size >= bytes - 0x04) goto fail; + int frame_size = ahx_decrypt(buf + pos, bytes, crikey); + if (frame_size <= 0 || frame_size >= bytes - 0x04) + goto fail; - bytes -= size; - pos += size; + bytes -= frame_size; + pos += frame_size; curr_sync = get_u32be(buf + pos); if (curr_sync == 0x00800100) /* EOF tag */ diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index 80ea619b1..9e513a48e 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -392,6 +392,7 @@ static const char* extension_list[] = { "nwa", "nwav", "nxa", +"nxopus", //"ogg", //common "ogg_", @@ -474,6 +475,7 @@ static const char* extension_list[] = { "sbr", "sbv", "sig", +"slb", //txth/reserved [Vingt-et-un Systems PS2 games (Last Escort, etc] "sm0", "sm1", "sm2", @@ -522,6 +524,7 @@ static const char* extension_list[] = { "sps", "spsd", "spw", +"srsa", "ss2", "ssd", //txth/reserved [Zack & Wiki (Wii)] "ssm", @@ -535,6 +538,7 @@ static const char* extension_list[] = { "stream", "strm", "sts", +"stv", //txth/reserved [Socio Art Logic PS2 games (Zero no Tsukaima games, Cambrian QTS, Shirogane no Soleil, etc)] "sts_cp3", "stx", "svag", @@ -628,6 +632,7 @@ static const char* extension_list[] = { "wd", "wem", "wii", +"wiive", //txth/semi [Rubik World (Wii)] "wic", //txth/reserved [Road Rash (SAT)-videos] "wip", //txth/reserved [Colin McRae DiRT (PC)] "wlv", //txth/reserved [ToeJam & Earl III: Mission to Earth (DC)] @@ -831,6 +836,7 @@ static const coding_info coding_info_list[] = { {coding_UBI_SCE_IMA, "Ubisoft 4-bit SCE IMA ADPCM"}, {coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"}, {coding_CD_IMA, "Crystal Dynamics 4-bit IMA ADPCM"}, +{coding_CRANKCASE_IMA, "CrankcaseAudio REV 4-bit IMA ADPCM"}, {coding_MSADPCM, "Microsoft 4-bit ADPCM"}, {coding_MSADPCM_int, "Microsoft 4-bit ADPCM (mono/interleave)"}, @@ -1325,7 +1331,7 @@ static const meta_info meta_info_list[] = { {meta_XAU_KONAMI, "Konami XAU header"}, {meta_DERF, "Xilam DERF header"}, {meta_UTK, "Maxis UTK header"}, - {meta_NXA, "Entergram NXA header"}, + {meta_NXA1, "Entergram NXA1 header"}, {meta_ADPCM_CAPCOM, "Capcom .ADPCM header"}, {meta_UE4OPUS, "Epic Games UE4OPUS header"}, {meta_XWMA, "Microsoft XWMA RIFF header"}, @@ -1415,6 +1421,7 @@ static const meta_info meta_info_list[] = { {meta_SQUEAKSTREAM, "Torus SqueakStream header"}, {meta_SQUEAKSAMPLE, "Torus SqueakSample header"}, {meta_SNDS, "Sony SNDS header"}, +{meta_NXOF, "Nihon Falcom FDK Opus Header"}, }; void get_vgmstream_coding_description(VGMSTREAM* vgmstream, char* out, size_t out_size) { diff --git a/Frameworks/vgmstream/vgmstream/src/layout/blocked_awc.c b/Frameworks/vgmstream/vgmstream/src/layout/blocked_awc.c index a2dda8154..01f72ac03 100644 --- a/Frameworks/vgmstream/vgmstream/src/layout/blocked_awc.c +++ b/Frameworks/vgmstream/vgmstream/src/layout/blocked_awc.c @@ -11,13 +11,19 @@ static size_t get_block_header_size(STREAMFILE* sf, off_t offset, size_t channel void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream) { STREAMFILE* sf = vgmstream->ch[0].streamfile; int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE; - size_t header_size, entries, block_size, block_samples; - size_t channel_header_size; + size_t header_size, entries, block_size, block_samples, frame_size; + size_t channel_header_size; int i; - /* assumed only AWC_IMA enters here, MPEG/XMA2 need special parsing as blocked layout is too limited */ - entries = read_32bit(block_offset + 0x04, sf); /* se first channel, assume all are the same */ - //block_samples = entries * (0x800-4)*2; //todo use + /* assumes only AWC_IMA/DSP enters here, MPEG/XMA2 need special parsing as blocked layout is too limited. + * Block header (see awc.c for a complete description): + * - per channel: header table (size 0x18 or 0x10) + * - per channel: seek table (32b * entries = global samples per frame in each block) (not in DSP/Vorbis) + * - per channel: extra table (DSP only) + * - padding (not in ATRAC9/DSP) + */ + + entries = read_32bit(block_offset + 0x04, sf); /* se first channel, assume all are the same (not true in MPEG/XMA) */ block_samples = read_32bit(block_offset + 0x0c, sf); block_size = vgmstream->full_block_size; @@ -25,24 +31,32 @@ void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream) { vgmstream->next_block_offset = block_offset + block_size; vgmstream->current_block_samples = block_samples; - /* starts with a header block */ - /* for each channel - * 0x00: start entry within channel (ie. entries * ch) but may be off by +1/+2 - * 0x04: entries - * 0x08: samples to discard in the beginning of this block (MPEG only?) - * 0x0c: samples in channel (for MPEG/XMA2 can vary between channels) - * (next fields don't exist in later versions for IMA) - * 0x10: (MPEG only, empty otherwise) close to number of frames but varies a bit? - * 0x14: (MPEG only, empty otherwise) channel usable data size (not counting padding) - * for each channel - * 32b * entries = global samples per frame in each block (for MPEG probably per full frame) - */ + switch(vgmstream->coding_type) { + case coding_NGC_DSP: + channel_header_size = 0x10; + frame_size = 0x08; + /* coefs on every block but it's always the same */ + dsp_read_coefs_le(vgmstream, sf, block_offset + channel_header_size * vgmstream->channels + 0x10 + 0x1c + 0x00, 0x10 + 0x60); + dsp_read_hist_le (vgmstream, sf, block_offset + channel_header_size * vgmstream->channels + 0x10 + 0x1c + 0x20, 0x10 + 0x60); + + header_size = 0; + header_size += channel_header_size * vgmstream->channels; /* header table */ + /* no seek table */ + header_size += 0x70 * vgmstream->channels; /* extra table */ + /* no padding */ + + break; + + default: channel_header_size = get_channel_header_size(sf, block_offset, vgmstream->codec_endian); header_size = get_block_header_size(sf, block_offset, channel_header_size, vgmstream->channels, vgmstream->codec_endian); +frame_size = 0x800; + break; + } + for (i = 0; i < vgmstream->channels; i++) { - vgmstream->ch[i].offset = block_offset + header_size + 0x800*entries*i; - VGM_ASSERT(entries != read_32bit(block_offset + channel_header_size*i + 0x04, sf), "AWC: variable number of entries found at %lx\n", block_offset); + vgmstream->ch[i].offset = block_offset + header_size + frame_size * entries * i; } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/acb.c b/Frameworks/vgmstream/vgmstream/src/meta/acb.c index 9375ce680..280187f03 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/acb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/acb.c @@ -92,6 +92,7 @@ fail: #define ACB_TABLE_BUFFER_TRACKCOMMAND 0x2000 #define ACB_TABLE_BUFFER_SYNTH 0x4000 #define ACB_TABLE_BUFFER_WAVEFORM 0x4000 +#define ACB_TABLE_BUFFER_WAVEFORMEXTENSIONDATA 0x1000 #define ACB_MAX_NAMELIST 255 #define ACB_MAX_NAME 1024 /* even more is possible in rare cases [Senran Kagura Burst Re:Newal (PC)] */ @@ -159,8 +160,15 @@ typedef struct { uint16_t Id; uint16_t PortNo; uint8_t Streaming; + uint8_t LoopFlag; + uint16_t ExtensionData; } Waveform_t; +typedef struct { + uint32_t LoopStart; + uint32_t LoopEnd; +} WaveformExtensionData_t; + typedef struct { STREAMFILE* acbFile; /* original reference, don't close */ @@ -178,6 +186,7 @@ typedef struct { STREAMFILE* TrackCommandSf; STREAMFILE* SynthSf; STREAMFILE* WaveformSf; + STREAMFILE* WaveformExtensionDataSf; Cue_t* Cue; CueName_t* CueName; @@ -188,6 +197,7 @@ typedef struct { TrackCommand_t* TrackCommand; Synth_t* Synth; Waveform_t* Waveform; + WaveformExtensionData_t* WaveformExtensionData; int Cue_rows; int CueName_rows; @@ -198,6 +208,7 @@ typedef struct { int TrackCommand_rows; int Synth_rows; int Waveform_rows; + int WaveformExtensionData_rows; /* config */ int is_memory; @@ -208,7 +219,8 @@ typedef struct { int synth_depth; int sequence_depth; - /* name stuff */ + /* name/config stuff */ + int16_t waveform_index; int16_t cuename_index; const char* cuename_name; int awbname_count; @@ -297,7 +309,7 @@ static void add_acb_name(acb_header* acb, int8_t Streaming) { static int preload_acb_waveform(acb_header* acb) { utf_context* Table = NULL; int* p_rows = &acb->Waveform_rows; - int i, c_Id, c_MemoryAwbId, c_StreamAwbId, c_StreamAwbPortNo, c_Streaming; + int i, c_Id, c_MemoryAwbId, c_StreamAwbId, c_StreamAwbPortNo, c_Streaming, c_LoopFlag, c_ExtensionData; if (*p_rows) return 1; @@ -315,6 +327,8 @@ static int preload_acb_waveform(acb_header* acb) { c_StreamAwbId = utf_get_column(Table, "StreamAwbId"); c_StreamAwbPortNo = utf_get_column(Table, "StreamAwbPortNo"); c_Streaming = utf_get_column(Table, "Streaming"); + c_LoopFlag = utf_get_column(Table, "LoopFlag"); + c_ExtensionData = utf_get_column(Table, "ExtensionData"); for (i = 0; i < *p_rows; i++) { Waveform_t* r = &acb->Waveform[i]; @@ -332,6 +346,10 @@ static int preload_acb_waveform(acb_header* acb) { r->PortNo = 0xFFFF; } utf_query_col_u8(Table, i, c_Streaming, &r->Streaming); + utf_query_col_u8(Table, i, c_LoopFlag, &r->LoopFlag); + + r->ExtensionData = -1; + utf_query_col_u16(Table, i, c_ExtensionData, &r->ExtensionData); /* optional for newer/Switch acb */ } utf_close(Table); @@ -346,7 +364,7 @@ static int load_acb_waveform(acb_header* acb, uint16_t Index) { Waveform_t* r; if (!preload_acb_waveform(acb)) goto fail; - if (Index > acb->Waveform_rows) goto fail; + if (Index >= acb->Waveform_rows) goto fail; r = &acb->Waveform[Index]; //;VGM_LOG("acb: Waveform[%i]: Id=%i, PortNo=%i, Streaming=%i\n", Index, r->Id, r->PortNo, r->Streaming); @@ -362,6 +380,9 @@ static int load_acb_waveform(acb_header* acb, uint16_t Index) { if ((acb->is_memory && r->Streaming == 1) || (!acb->is_memory && r->Streaming == 0)) return 1; + /* save waveid <> Index translation */ + acb->waveform_index = Index; + /* aaand finally get name (phew) */ add_acb_name(acb, r->Streaming); @@ -417,7 +438,7 @@ static int load_acb_synth(acb_header* acb, uint16_t Index) { int i, count; if (!preload_acb_synth(acb)) goto fail; - if (Index > acb->Synth_rows) goto fail; + if (Index >= acb->Synth_rows) goto fail; r = &acb->Synth[Index]; //;VGM_LOG("acb: Synth[%i]: Type=%x, ReferenceItems={%x,%x}\n", Index, r->Type, r->ReferenceItems_offset, r->ReferenceItems_size); @@ -615,7 +636,7 @@ static int load_acb_trackcommand(acb_header* acb, uint16_t Index) { TrackCommand_t* r; if (!preload_acb_trackcommand(acb)) goto fail; - if (Index > acb->TrackCommand_rows) goto fail; + if (Index >= acb->TrackCommand_rows) goto fail; r = &acb->TrackCommand[Index]; //;VGM_LOG("acb: TrackEvent/Command[%i]: Command={%x,%x}\n", Index, r->Command_offset, r->Command_size); @@ -669,7 +690,7 @@ static int load_acb_track(acb_header* acb, uint16_t Index) { Track_t* r; if (!preload_acb_track(acb)) goto fail; - if (Index > acb->Track_rows) goto fail; + if (Index >= acb->Track_rows) goto fail; r = &acb->Track[Index]; //;VGM_LOG("acb: Track[%i]: EventIndex=%i\n", Index, r->EventIndex); @@ -736,7 +757,7 @@ static int load_acb_sequence(acb_header* acb, uint16_t Index) { int i; if (!preload_acb_sequence(acb)) goto fail; - if (Index > acb->Sequence_rows) goto fail; + if (Index >= acb->Sequence_rows) goto fail; r = &acb->Sequence[Index]; //;VGM_LOG("acb: Sequence[%i]: NumTracks=%i, TrackIndex={%x, %x}, Type=%x\n", Index, r->NumTracks, r->TrackIndex_offset, r->TrackIndex_size, r->Type); @@ -832,7 +853,7 @@ static int load_acb_block(acb_header* acb, uint16_t Index) { int i; if (!preload_acb_block(acb)) goto fail; - if (Index > acb->Block_rows) goto fail; + if (Index >= acb->Block_rows) goto fail; r = &acb->Block[Index]; //;VGM_LOG("acb: Block[%i]: NumTracks=%i, TrackIndex={%x, %x}\n", Index, r->NumTracks, r->TrackIndex_offset, r->TrackIndex_size); @@ -900,7 +921,7 @@ static int load_acb_blocksequence(acb_header* acb, uint16_t Index) { int i; if (!preload_acb_blocksequence(acb)) goto fail; - if (Index > acb->BlockSequence_rows) goto fail; + if (Index >= acb->BlockSequence_rows) goto fail; r = &acb->BlockSequence[Index]; //;VGM_LOG("acb: BlockSequence[%i]: NumTracks=%i, TrackIndex={%x, %x}, NumBlocks=%i, BlockIndex={%x, %x}\n", Index, r->NumTracks, r->TrackIndex_offset,r->TrackIndex_size, r->NumBlocks, r->BlockIndex_offset, r->BlockIndex_size); @@ -977,7 +998,7 @@ static int load_acb_cue(acb_header* acb, uint16_t Index) { /* read Cue[Index] */ if (!preload_acb_cue(acb)) goto fail; - if (Index > acb->Cue_rows) goto fail; + if (Index >= acb->Cue_rows) goto fail; r = &acb->Cue[Index]; //;VGM_LOG("acb: Cue[%i]: ReferenceType=%i, ReferenceIndex=%i\n", Index, r->ReferenceType, r->ReferenceIndex); @@ -1064,7 +1085,7 @@ static int load_acb_cuename(acb_header* acb, uint16_t Index) { CueName_t* r; if (!preload_acb_cuename(acb)) goto fail; - if (Index > acb->CueName_rows) goto fail; + if (Index >= acb->CueName_rows) goto fail; r = &acb->CueName[Index]; //;VGM_LOG("acb: CueName[%i]: CueIndex=%i, CueName=%s\n", Index, r->CueIndex, r->CueName); @@ -1081,6 +1102,79 @@ fail: return 0; } +static int preload_acb_waveformextensiondata(acb_header* acb) { + utf_context* Table = NULL; + int* p_rows = &acb->WaveformExtensionData_rows; + int i, c_LoopStart, c_LoopEnd; + + if (*p_rows) + return 1; + if (!open_utf_subtable(acb, &acb->WaveformExtensionDataSf, &Table, "WaveformExtensionDataTable", p_rows, ACB_TABLE_BUFFER_WAVEFORMEXTENSIONDATA)) + goto fail; + if (!*p_rows) + return 1; + //;VGM_LOG("acb: preload WaveformExtensionData=%i\n", *p_rows); + + acb->WaveformExtensionData = malloc(*p_rows * sizeof(WaveformExtensionData_t)); + if (!acb->WaveformExtensionData) goto fail; + + c_LoopStart = utf_get_column(Table, "LoopStart"); + c_LoopEnd = utf_get_column(Table, "LoopEnd"); + + for (i = 0; i < *p_rows; i++) { + WaveformExtensionData_t* r = &acb->WaveformExtensionData[i]; + + utf_query_col_u32(Table, i, c_LoopStart, &r->LoopStart); + utf_query_col_u32(Table, i, c_LoopEnd, &r->LoopEnd); + } + + utf_close(Table); + return 1; +fail: + VGM_LOG("acb: failed WaveformExtensionData preload\n"); + utf_close(Table); + return 0; +} + +/* for Switch Opus that has loop info in a separate "WaveformExtensionData" table (pointed by a field in Waveform) */ +static int load_acb_loops(acb_header* acb, VGMSTREAM* vgmstream) { + Waveform_t* rw; + WaveformExtensionData_t* r; + uint16_t WaveIndex = acb->waveform_index; + uint16_t ExtensionIndex = -1; + + if (vgmstream->loop_flag) + return 0; + + /* assumes that will be init'd before while searching for names */ + if (WaveIndex < 0) goto fail; + //if (!preload_acb_waveform(acb)) goto fail; + if (WaveIndex >= acb->Waveform_rows) goto fail; + rw = &acb->Waveform[WaveIndex]; + + /* 1=no loop, 2=loop, ignore others/0(default)/255 just in case */ + if (rw->LoopFlag != 2) + return 0; + + ExtensionIndex = rw->ExtensionData; + if (ExtensionIndex < 0) goto fail; /* not init'd? */ + + if (!preload_acb_waveformextensiondata(acb)) goto fail; + if (ExtensionIndex >= acb->WaveformExtensionData_rows) goto fail; + + r = &acb->WaveformExtensionData[ExtensionIndex]; + + //;VGM_LOG("acb: WaveformExtensionData[%i]: LoopStart=%i, LoopEnd=%i\n", Index, r->LoopStart, r->LoopEnd); + + vgmstream_force_loop(vgmstream, 1, r->LoopStart, r->LoopEnd); + + return 1; +fail: + VGM_LOG("acb: failed WaveformExtensionData %i\n", ExtensionIndex); + return 0; +} + + /*****************************************************************************/ /* Normally games load a .acb + .awb, and asks the .acb to play a cue by name or index. @@ -1111,7 +1205,7 @@ fail: * per table, meaning it uses a decent chunk of memory, but having to re-read with streamfiles is much slower. */ -void load_acb_wave_name(STREAMFILE* sf, VGMSTREAM* vgmstream, int waveid, int port, int is_memory) { +void load_acb_wave_info(STREAMFILE* sf, VGMSTREAM* vgmstream, int waveid, int port, int is_memory, int load_loops) { acb_header acb = {0}; int i; @@ -1129,6 +1223,7 @@ void load_acb_wave_name(STREAMFILE* sf, VGMSTREAM* vgmstream, int waveid, int po acb.target_waveid = waveid; acb.target_port = port; acb.is_memory = is_memory; + acb.waveform_index = -1; /* read all possible cue names and find which waveids are referenced by it */ preload_acb_cuename(&acb); @@ -1143,6 +1238,11 @@ void load_acb_wave_name(STREAMFILE* sf, VGMSTREAM* vgmstream, int waveid, int po vgmstream->stream_name[STREAM_NAME_SIZE - 1] = '\0'; } + /* uncommon */ + if (load_loops) { + load_acb_loops(&acb, vgmstream); + } + /* done */ fail: utf_close(acb.Header); @@ -1157,6 +1257,7 @@ fail: close_streamfile(acb.TrackCommandSf); close_streamfile(acb.SynthSf); close_streamfile(acb.WaveformSf); + close_streamfile(acb.WaveformExtensionDataSf); free(acb.CueName); free(acb.Cue); @@ -1167,4 +1268,5 @@ fail: free(acb.TrackCommand); free(acb.Synth); free(acb.Waveform); + free(acb.WaveformExtensionData); } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adm.c b/Frameworks/vgmstream/vgmstream/src/meta/adm.c index d38fd5989..55835e5c9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/adm.c @@ -5,7 +5,8 @@ typedef struct { int total_subsongs; int target_subsong; - int version; + int file_version; + int header_version; /* major.minor in hex */ uint32_t stream_offset; uint32_t stream_size; @@ -18,7 +19,7 @@ typedef struct { static int parse_adm(adm_header_t* adm, STREAMFILE* sf); -static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int version); +static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int file_version); /* ADM2 - Crankcase Audio REV plugin file [The Grand Tour Game (PC)] */ VGMSTREAM* init_vgmstream_adm2(STREAMFILE* sf) { @@ -38,26 +39,24 @@ VGMSTREAM* init_vgmstream_adm3(STREAMFILE* sf) { /* checks */ if (!is_id32be(0x00,sf, "ADM3")) return NULL; - if (!check_extensions(sf, "wem")) + if (!check_extensions(sf, "wem,bnk")) return NULL; return init_vgmstream_adm(sf, 3); } -static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int version) { +static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int file_version) { VGMSTREAM* vgmstream = NULL; adm_header_t adm = {0}; /* ADMx are files used with the Wwise Crankaudio plugin, that simulate engine noises with * base internal samples and some internal RPM config (probably). Actual file seems to - * define some combo of samples, this only plays those separate samples. - * Decoder is basically Apple's IMA (internally just "ADPCMDecoder") but transforms to float - * each sample during decode by multiplying by 0.000030518509 */ + * define some combo of samples, this only plays those separate samples. */ adm.target_subsong = sf->stream_index; if (adm.target_subsong == 0) adm.target_subsong = 1; - adm.version = version; + adm.file_version = file_version; if (!parse_adm(&adm, sf)) goto fail; @@ -73,9 +72,21 @@ static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int version) { vgmstream->num_streams = adm.total_subsongs; vgmstream->stream_size = adm.stream_size; - vgmstream->coding_type = coding_APPLE_IMA4; - vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = 0x22; + switch(adm.header_version) { + case 0x00070000: /* The Crew Motorfest (PC) */ + vgmstream->coding_type = coding_CRANKCASE_IMA; + //vgmstream->layout_type = layout_interleave; + //vgmstream->interleave_block_size = 0x23; + break; + + default: /* The Grand Tour Game (PC) [0x00050000], MotoGP 21 (PC) [0x00060000] */ + /* Basically Apple's IMA (internally just "ADPCMDecoder") but transforms to float + * each sample during decode by multiplying by 0.000030518509 */ + vgmstream->coding_type = coding_APPLE_IMA4; + vgmstream->layout_type = layout_interleave; + vgmstream->interleave_block_size = 0x22; + break; + } if (!vgmstream_open_stream(vgmstream, sf, adm.stream_offset)) goto fail; @@ -169,12 +180,12 @@ static int parse_adm(adm_header_t* adm, STREAMFILE* sf) { uint32_t offset; /* 0x04: null */ - /* 0x08: version? (ADM2: 0x00050000, ADM3: 0x00060000) */ + adm->header_version = read_u32le(0x08, sf); /* ADM2: 0x00050000, ADM3: 0x00060000 (older) / 0x00070000 (2023) */ /* 0x0c: header size */ /* 0x10: data start */ /* rest unknown, looks mostly the same between files (some floats and stuff) */ - switch(adm->version) { + switch(adm->file_version) { case 2: /* low to high */ offset = read_u32le(0x104, sf); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/pc_adp_otns.c b/Frameworks/vgmstream/vgmstream/src/meta/adp_qd.c similarity index 100% rename from Frameworks/vgmstream/vgmstream/src/meta/pc_adp_otns.c rename to Frameworks/vgmstream/vgmstream/src/meta/adp_qd.c diff --git a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h index f74f8cf84..fa30c3210 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/adx_keys.h @@ -202,12 +202,15 @@ static const adxkey_info adxkey8_list[] = { /* Mirai Nikki: 13-ninme no Nikki Shoyuusha Re-Write (PSP) */ {0x58a3,0x66f5,0x599f, "FDRW17th",0}, - /* Shoujo Yoshitsune-den Ni - Toki wo Koeru Chigiri (PS2) */ + /* Shoujo Yoshitsune-den Ni: Toki wo Koeru Chigiri (PS2) */ {0x62d7,0x483d,0x4fb7, "YOSHI2",0}, - /* Junjou Romantica - Koi no Doki Doki Daisakusen (PS2) (Marvelous) */ + /* Junjou Romantica: Koi no Doki Doki Daisakusen (PS2) */ {0x5827,0x612d,0x5585, "Endress-SETSUNAI!",0}, +/* Corpse Party: Book of Shadows (PSP) */ + {0x60ad,0x5689,0x5281, "\x83\x76\x83\x89\x83\x60\x83\x69Lovers_Day",0}, // "プラチナLovers_Day" in SHIFT-JIS + }; static const adxkey_info adxkey9_list[] = { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/dsp_adx.c b/Frameworks/vgmstream/vgmstream/src/meta/adx_monster.c similarity index 100% rename from Frameworks/vgmstream/vgmstream/src/meta/dsp_adx.c rename to Frameworks/vgmstream/vgmstream/src/meta/adx_monster.c diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awb.c b/Frameworks/vgmstream/vgmstream/src/meta/awb.c index d21fd6a94..710d04941 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/awb.c @@ -4,7 +4,7 @@ //typedef enum { ADX, HCA, VAG, RIFF, CWAV, DSP, CWAC, M4A } awb_type_t; -static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid); +static void load_acb_info(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid, int load_loops); /* AFS2/AWB (Atom Wave Bank) - CRI container of streaming audio, often together with a .acb cue sheet */ VGMSTREAM* init_vgmstream_awb(STREAMFILE* sf) { @@ -19,6 +19,7 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) { uint8_t offset_size; uint16_t waveid_alignment, offset_alignment, subkey; int waveid; +int load_loops = 0; /* checks */ @@ -126,6 +127,11 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) { extension = "m4a"; } #endif +else if (read_u32be(subfile_offset + 0x00,sf) == 0x01000080) { /* (type 24=NXOpus) */ + init_vgmstream =init_vgmstream_opus_std; /* Super Mario RPG (Switch) */ + extension = "opus"; + load_loops = 1; /* loops not in Opus (rare) but in .acb */ + } else { /* 12=XMA? */ vgm_logi("AWB: unknown codec (report)\n"); goto fail; @@ -144,8 +150,8 @@ VGMSTREAM* init_vgmstream_awb_memory(STREAMFILE* sf, STREAMFILE* sf_acb) { vgmstream->num_streams = total_subsongs; } - /* try to load cue names */ - load_awb_name(sf, sf_acb, vgmstream, waveid); + /* try to load cue names+etc */ + load_acb_info(sf, sf_acb, vgmstream, waveid, load_loops); close_streamfile(temp_sf); return vgmstream; @@ -157,7 +163,7 @@ fail: } -static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid) { +static void load_acb_info(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstream, int waveid, int load_loops) { int is_memory = (sf_acb != NULL); int port = 0; @@ -170,7 +176,7 @@ static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstre /* try parsing TXTM if present */ sf_acb = read_filemap_file_pos(sf, 0, &port); - /* try (name).awb + (name).awb */ + /* try (name).awb + (name).acb */ if (!sf_acb) { sf_acb = open_streamfile_by_ext(sf, "acb"); } @@ -204,11 +210,11 @@ static void load_awb_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstre } /* probably loaded */ - load_acb_wave_name(sf_acb, vgmstream, waveid, port, is_memory); + load_acb_wave_info(sf_acb, vgmstream, waveid, port, is_memory, load_loops); close_streamfile(sf_acb); } else { - load_acb_wave_name(sf_acb, vgmstream, waveid, port, is_memory); + load_acb_wave_info(sf_acb, vgmstream, waveid, port, is_memory, load_loops); } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awc.c b/Frameworks/vgmstream/vgmstream/src/meta/awc.c index be6ebf8f9..8f46adc90 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/awc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/awc.c @@ -1,26 +1,35 @@ #include "meta.h" #include "../coding/coding.h" #include "../layout/layout.h" -#include "awc_xma_streamfile.h" +#include "../util/endianness.h" +#include "awc_streamfile.h" typedef struct { int big_endian; int is_encrypted; - int is_music; + int is_streamed; /* implicit: streams=music, sfx=memory */ int total_subsongs; int channels; int sample_rate; - int codec; int num_samples; + uint8_t codec; + int block_count; int block_chunk; - off_t stream_offset; - size_t stream_size; - off_t vorbis_offset[VGMSTREAM_MAX_CHANNELS]; + uint32_t tags_offset; + uint32_t stream_offset; + uint32_t stream_size; + uint32_t vorbis_offset[AWC_MAX_MUSIC_CHANNELS]; + /* stream+music only */ + uint32_t channel_hash[AWC_MAX_MUSIC_CHANNELS]; + struct { + uint32_t hash_id; + int tag_count; + } stream_info[AWC_MAX_MUSIC_CHANNELS]; } awc_header; static int parse_awc_header(STREAMFILE* sf, awc_header* awc); @@ -28,17 +37,17 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc); static layered_layout_data* build_layered_awc(STREAMFILE* sf, awc_header* awc); -/* AWC - from RAGE (Rockstar Advanced Game Engine) audio [Red Dead Redemption, Max Payne 3, GTA5 (multi)] */ +/* AWC - Audio Wave Container from RAGE (Rockstar Advanced Game Engine) [Red Dead Redemption, Max Payne 3, GTA5 (multi)] */ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; awc_header awc = {0}; /* checks */ - if (!check_extensions(sf,"awc")) - goto fail; if (!parse_awc_header(sf, &awc)) - goto fail; + return NULL; + if (!check_extensions(sf,"awc")) + return NULL; /* build the VGMSTREAM */ @@ -55,7 +64,7 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) { switch(awc.codec) { case 0x00: /* PCM (PC) sfx, very rare, lower sample rates? [Max Payne 3 (PC)] */ case 0x01: /* PCM (PC/PS3) sfx, rarely */ - if (awc.is_music) goto fail; /* blocked_awc needs to be prepared */ + if (awc.is_streamed) goto fail; /* blocked_awc needs to be prepared */ vgmstream->coding_type = awc.big_endian ? coding_PCM16BE : coding_PCM16LE; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x02; @@ -63,59 +72,18 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) { case 0x04: /* IMA (PC) */ vgmstream->coding_type = coding_AWC_IMA; - vgmstream->layout_type = awc.is_music ? layout_blocked_awc : layout_none; + vgmstream->layout_type = awc.is_streamed ? layout_blocked_awc : layout_none; vgmstream->full_block_size = awc.block_chunk; vgmstream->codec_endian = awc.big_endian; break; #ifdef VGM_USE_FFMPEG case 0x05: { /* XMA2 (X360) */ - uint32_t substream_size, substream_offset; - - if (awc.is_music) { - /* 1ch XMAs in blocks, we'll use layered layout + custom IO to get multi-FFmpegs working */ - int i; - layered_layout_data * data = NULL; - - /* init layout */ - data = init_layout_layered(awc.channels); - if (!data) goto fail; - vgmstream->layout_data = data; + if (awc.is_streamed) { + vgmstream->layout_data = build_layered_awc(sf, &awc); + if (!vgmstream->layout_data) goto fail; vgmstream->layout_type = layout_layered; vgmstream->coding_type = coding_FFmpeg; - - /* open each layer subfile */ - for (i = 0; i < awc.channels; i++) { - STREAMFILE* temp_sf = NULL; - int layer_channels = 1; - - /* build the layer VGMSTREAM */ - data->layers[i] = allocate_vgmstream(layer_channels, 0); - if (!data->layers[i]) goto fail; - - data->layers[i]->meta_type = meta_AWC; - data->layers[i]->coding_type = coding_FFmpeg; - data->layers[i]->layout_type = layout_none; - data->layers[i]->sample_rate = awc.sample_rate; - data->layers[i]->num_samples = awc.num_samples; - - /* setup custom IO streamfile, pass to FFmpeg and hope it's fooled */ - temp_sf = setup_awc_xma_streamfile(sf, awc.stream_offset, awc.stream_size, awc.block_chunk, awc.channels, i); - if (!temp_sf) goto fail; - - substream_offset = 0x00; /* where FFmpeg thinks data starts, which our custom sf will clamp */ - substream_size = get_streamfile_size(temp_sf); /* data of one XMA substream without blocks */ - - data->layers[i]->codec_data = init_ffmpeg_xma2_raw(temp_sf, substream_offset, substream_size, awc.num_samples, layer_channels, awc.sample_rate, 0, 0); - if (data->layers[i]) - xma_fix_raw_samples(data->layers[i], temp_sf, substream_offset, substream_size, 0, 0,0); /* samples are ok? */ - close_streamfile(temp_sf); - if (!data->layers[i]->codec_data) goto fail; - } - - /* setup layered VGMSTREAMs */ - if (!setup_layout_layered(data)) - goto fail; } else { /* regular XMA for sfx */ @@ -129,9 +97,8 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) { break; } - - #endif + #ifdef VGM_USE_MPEG case 0x07: { /* MPEG (PS3) */ mpeg_custom_config cfg = {0}; @@ -146,9 +113,10 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) { break; } #endif + #ifdef VGM_USE_VORBIS - case 0x08: { /* Vorbis (PC) [Red Dead Redemption 2 (PC)] */ - if (awc.is_music) { + case 0x08: { /* Vorbis (PC) [Red Dead Redemption 2 (PC)] */ + if (awc.is_streamed) { vgmstream->layout_data = build_layered_awc(sf, &awc); if (!vgmstream->layout_data) goto fail; vgmstream->layout_type = layout_layered; @@ -169,6 +137,62 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) { break; } #endif + +#ifdef VGM_USE_ATRAC9 + case 0x0F: { /* ATRAC9 (PC) [Red Dead Redemption (PS4)] */ + if (awc.is_streamed) { + vgmstream->layout_data = build_layered_awc(sf, &awc); + if (!vgmstream->layout_data) goto fail; + vgmstream->layout_type = layout_layered; + vgmstream->coding_type = coding_ATRAC9; + } + else { + VGMSTREAM* temp_vs = NULL; + STREAMFILE* temp_sf = NULL; + + temp_sf = setup_subfile_streamfile(sf, awc.stream_offset, awc.stream_size, "at9"); + if (!temp_sf) goto fail; + + temp_vs = init_vgmstream_riff(temp_sf); + close_streamfile(temp_sf); + if (!temp_vs) goto fail; + + temp_vs->num_streams = vgmstream->num_streams; + temp_vs->stream_size = vgmstream->stream_size; + temp_vs->meta_type = vgmstream->meta_type; + strcpy(temp_vs->stream_name, vgmstream->stream_name); + + close_vgmstream(vgmstream); + //vgmstream = temp_vs; + return temp_vs; + } + break; + } +#endif + + case 0x0C: /* DSP-sfx (Switch) */ + case 0x10: /* DSP-music (Switch) */ + vgmstream->coding_type = coding_NGC_DSP; + vgmstream->layout_type = awc.is_streamed ? layout_blocked_awc : layout_none; + vgmstream->full_block_size = awc.block_chunk; + + if (!awc.is_streamed) { + /* dsp header */ + dsp_read_coefs_le(vgmstream, sf, awc.stream_offset + 0x1c + 0x00, 0x00); + dsp_read_hist_le (vgmstream, sf, awc.stream_offset + 0x1c + 0x20, 0x00); + awc.stream_offset += 0x60; + + /* shouldn't be possible since it's only used for sfx anyway */ + if (awc.channels > 1) + goto fail; + } + break; + + case 0xFF: + vgmstream->coding_type = coding_SILENCE; + snprintf(vgmstream->stream_name, STREAM_NAME_SIZE, "[%s]", "midi"); + break; + default: VGM_LOG("AWC: unknown codec 0x%02x\n", awc.codec); goto fail; @@ -185,24 +209,42 @@ fail: } -/* Parse Rockstar's AWC header (much info from LibertyV: https://github.com/koolkdev/libertyv). - * Made of entries for N streams, each with a number of tags pointing to chunks (header, data, events, etc). */ +/* Parse Rockstar's AWC header (much info from LibertyV: https://github.com/koolkdev/libertyv). + * + * AWC defines logical streams/tracks, each with N tags (type+offset+size) that point to headers/tables with info. + * First stream may be a "music" type, then other streams are used as channels and not always define tags. + * When the "stream" flag is set data is divided into "blocks" (used for music), described later. + * Streams are ordered by hash/id and its tags go in order, but data may be unordered (1st stream audio + * or headers could go after others). Defined streams also may be unused/dummy. + * Hashes are actually reversable and more or less stream names (see other tools). + * + * Rough file format: + * - base header + * - stream tag starts [optional] + * - stream hash ids and tag counts (stream N has M tags) + * - tags per stream + * - data from tags (headers, tables, audio data, etc) + */ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) { - uint64_t (*read_u64)(off_t,STREAMFILE*) = NULL; - uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL; - uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL; - int i, ch, entries; - uint32_t flags, info_header, tag_count = 0, tags_skip = 0; - off_t offset; + read_u64_t read_u64 = NULL; + read_u32_t read_u32 = NULL; + read_u16_t read_u16 = NULL; + int entries; + uint32_t flags, tag_count = 0, tags_skip = 0; + uint32_t offset; int target_subsong = sf->stream_index; + /** base header **/ + if (is_id32be(0x00,sf,"ADAT")) { + awc->big_endian = false; + } + else if (is_id32be(0x00,sf,"TADA")) { + awc->big_endian = true; + } + else { + return false; + } - /* check header */ - if (read_u32be(0x00,sf) != 0x41444154 && /* "ADAT" (LE) */ - read_u32be(0x00,sf) != 0x54414441) /* "TADA" (BE) */ - goto fail; - - awc->big_endian = read_u32be(0x00,sf) == 0x54414441; if (awc->big_endian) { read_u64 = read_u64be; read_u32 = read_u32be; @@ -213,25 +255,32 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) { read_u16 = read_u16le; } - flags = read_u32(0x04,sf); entries = read_u32(0x08,sf); - //header_size = read_u32(0x0c,sf); /* after to stream id/tags, not including chunks */ + //header_size = read_u32(0x0c,sf); /* after stream id+tags */ offset = 0x10; + /* flags = 8b (always FF) + 8b (actual flags) + 16b (version, 00=rarely, 01=common) */ if ((flags & 0xFF00FFFF) != 0xFF000001 || (flags & 0x00F00000)) { VGM_LOG("AWC: unknown flags 0x%08x\n", flags); goto fail; } - if (flags & 0x00010000) /* some kind of mini offset table */ + /* stream tag starts (ex. stream#0 = 0, stream#1 = 4, stream#2 = 7: to read tags from stream#2 skip to 7th tag) */ + if (flags & 0x00010000) offset += 0x2 * entries; - //if (flags % 0x00020000) /* seems to indicate chunks are not ordered (ie. header may go after data) */ - // ... - //if (flags % 0x00040000) /* music/multichannel flag? (GTA5, not seen in RDR) */ - // awc->is_music = 1; - if (flags & 0x00080000) /* encrypted data chunk (most of GTA5 PC) */ + + /* seems to indicate chunks are not ordered (ie. header structures from tags may go after data), usually for non-streams */ + //if (flags % 0x00020000) + // awc->is_unordered = 1; + + /* stream/multichannel flag (rare, GTA5/RDR2) */ + //if (flags % 0x00040000) + // awc->is_multichannel = 1; + + /* encrypted data chunk (most of GTA5 PC for licensed audio) */ + if (flags & 0x00080000) awc->is_encrypted = 1; if (awc->is_encrypted) { @@ -239,48 +288,84 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) { goto fail; } - /* Music when the first id is 0 (base/fake entry with info for all channels), sfx pack otherwise. - * sfx = N single streams, music = N-1 interleaved mono channels (even for MP3/XMA). - * Music seems layered (N-1/2 stereo pairs), maybe set with events? */ - awc->is_music = (read_u32(offset + 0x00,sf) & 0x1FFFFFFF) == 0x00000000; - if (awc->is_music) { /* all streams except id 0 is a channel */ + + /* When first stream hash/id is 0 AWC it has fake entry with info for all channels = music, sfx pack otherwise. + * sfx = N single streams, music = N interleaved mono channels (even for MP3/XMA/Vorbis/etc). + * Channels set a stream hash/id that typically is one of the defined ones and its tags do apply to that + * channel, but rarely may not exist. Ex.: + * + * - bgm01.awc + * Stream ID 00000000 (implicit: music stream, all others aren't used) + * Tag: music header + * Channel 0: ID 9d66fe4c + * Channel 1: ID 7a3837ef + * Channel 2: ID 032c57e9 (not actually defined) + * Tag: data chunk + * #Tag: sfx header (only in buggy files) + * Stream ID 7a3837ef (no tags) + * Stream ID 9d66fe4c (notice this is channel 0 but streams are ordered by hash) + * Tag: Event config + * + * - sfx01.awc + * Stream ID 9d66fe4c + * Tag: sfx header + * Tag: data chunk + * Stream ID 7a3837ef + * Tag: sfx header + * Tag: data chunk + * + * Music 'stream' defines it's own (streamed/blocked) data chunk, so other stream's data or headers aren't used, + * but in rare cases they actually define a useless sfx header or even a separate cloned data chunk. That seems + * to be a bug and are ignored (ex. RDR's ftr_harmonica_01, or RDR SW's countdown_song_01). + */ + + awc->is_streamed = (read_u32(offset + 0x00,sf) & 0x1FFFFFFF) == 0x00000000; /* first stream's hash/id is 0 */ + if (awc->is_streamed) { /* music with N channels, other streams aren't used ignored */ awc->total_subsongs = 1; - target_subsong = 1; /* we only need id 0, though channels may have its own tags/chunks */ + target_subsong = 1; + /* array access below */ + if (entries >= AWC_MAX_MUSIC_CHANNELS) + goto fail; } - else { /* each stream is a single sound */ + else { /* sfx pack, each stream is a sound */ awc->total_subsongs = entries; if (target_subsong == 0) target_subsong = 1; if (target_subsong < 0 || target_subsong > awc->total_subsongs || awc->total_subsongs < 1) goto fail; } - /* get stream base info */ - for (i = 0; i < entries; i++) { - info_header = read_u32(offset + 0x04*i, sf); - tag_count = (info_header >> 29) & 0x7; /* 3b */ - //id = (info_header >> 0) & 0x1FFFFFFF; /* 29b */ - if (target_subsong-1 == i) - break; - tags_skip += tag_count; /* tags to skip to reach target's tags, in the next header */ + /** stream ids and tag counts **/ + for (int i = 0; i < entries; i++) { + uint32_t info_header = read_u32(offset + 0x00, sf); + int entry_count = (info_header >> 29) & 0x7; /* 3b */ + uint32_t hash_id = (info_header >> 0) & 0x1FFFFFFF; /* 29b */ + + if (i + 1 < target_subsong) + tags_skip += entry_count; /* tags to skip to reach target's tags, in the next header */ + if (target_subsong == i + 1) + tag_count = entry_count; + + if (awc->is_streamed) { + awc->stream_info[i].hash_id = hash_id; + awc->stream_info[i].tag_count = entry_count; + } + + offset += 0x04; } - offset += 0x04*entries; - offset += 0x08*tags_skip; + awc->tags_offset = offset; /* where tags for stream start */ - /* get stream tags */ - for (i = 0; i < tag_count; i++) { - uint64_t tag_header; - uint8_t tag_type; - size_t tag_size; - off_t tag_offset; + offset += 0x08 * tags_skip; /* ignore tags for other streams */ - tag_header = read_u64(offset + 0x08*i,sf); - tag_type = (uint8_t)((tag_header >> 56) & 0xFF); /* 8b */ - tag_size = (size_t)((tag_header >> 28) & 0x0FFFFFFF); /* 28b */ - tag_offset = (off_t)((tag_header >> 0) & 0x0FFFFFFF); /* 28b */ - ;VGM_LOG("AWC: tag%i/%i at %lx: t=%x, o=%lx, s=%x\n", i, tag_count, offset + 0x08*i, tag_type, tag_offset, tag_size); - /* Tags are apparently part of a hash derived from a word ("data", "format", etc). - * If music + 1ch, the header and data chunks can repeat for no reason (sometimes not even pointed). */ + + /** tags per stream **/ + for (int i = 0; i < tag_count; i++) { + uint64_t tag_header = read_u64(offset + 0x08*i,sf); + uint8_t tag_type = ((tag_header >> 56) & 0xFF); /* 8b */ + uint32_t tag_size = ((tag_header >> 28) & 0x0FFFFFFF); /* 28b */ + uint32_t tag_offset = ((tag_header >> 0) & 0x0FFFFFFF); /* 28b */ + + /* types are apparently part of a hash derived from a word ("data", "format", etc). */ switch(tag_type) { case 0x55: /* data */ awc->stream_offset = tag_offset; @@ -288,30 +373,31 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) { break; case 0x48: /* music header */ - - if (!awc->is_music) { - VGM_LOG("AWC: music header found in sfx\n"); + if (!awc->is_streamed) { + VGM_LOG("AWC: music header found but not streamed\n"); goto fail; } - /* 0x00(32): unknown (some count?) */ + awc->block_count = read_u32(tag_offset + 0x00,sf); awc->block_chunk = read_u32(tag_offset + 0x04,sf); - awc->channels = read_u32(tag_offset + 0x08,sf); + awc->channels = read_u32(tag_offset + 0x08,sf); if (awc->channels != entries - 1) { /* not counting id-0 */ VGM_LOG("AWC: number of music channels doesn't match entries\n"); - goto fail; + /* extremely rare but doesn't seem to matter, some streams are dummies (RDR2 STREAMS/ABIGAIL_HUMMING_*) */ + //goto fail; } - for (ch = 0; ch < awc->channels; ch++) { + for (int ch = 0; ch < awc->channels; ch++) { int num_samples, sample_rate, codec; - /* 0x00): stream id (not always in the header entries order) */ + + awc->channel_hash[ch] = read_u32(tag_offset + 0x0c + 0x10*ch + 0x00,sf); /* reference, for vorbis */ num_samples = read_u32(tag_offset + 0x0c + 0x10*ch + 0x04,sf); /* 0x08: headroom */ sample_rate = read_u16(tag_offset + 0x0c + 0x10*ch + 0x0a,sf); codec = read_u8(tag_offset + 0x0c + 0x10*ch + 0x0c,sf); /* 0x0d(8): round size? */ - /* 0x0e: unknown (zero/-1) */ + /* 0x0e: unknown (zero/-1, loop flag? BOB_FINALE_1_A.awc, but also set in stingers) */ /* validate channels differences */ if ((awc->num_samples && !(awc->num_samples >= num_samples - 10 && awc->num_samples <= num_samples + 10)) || @@ -336,77 +422,120 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) { break; case 0xFA: /* sfx header */ - if (awc->is_music) { - VGM_LOG("AWC: sfx header found in music\n"); - goto fail; + if (awc->is_streamed) { + VGM_LOG("AWC: sfx header found but streamed\n"); + break; //goto fail; /* rare (RDR PC/Switch) */ } awc->num_samples = read_u32(tag_offset + 0x00,sf); /* 0x04: -1? */ awc->sample_rate = read_u16(tag_offset + 0x08,sf); - /* 0x0a: unknown x4 */ + /* 0x0a: headroom */ + /* 0x0c: unknown */ + /* 0x0e: unknown */ + /* 0x10: unknown */ /* 0x12: null? */ awc->codec = read_u8(tag_offset + 0x13, sf); + /* 0x14: ? (PS3 only, for any codec) */ + awc->channels = 1; break; case 0x76: /* sfx header for vorbis */ - if (awc->is_music) { - VGM_LOG("AWC: sfx header found in music\n"); + if (awc->is_streamed) { + VGM_LOG("AWC: sfx header found but streamed\n"); goto fail; } awc->num_samples = read_u32(tag_offset + 0x00,sf); /* 0x04: -1? */ awc->sample_rate = read_u16(tag_offset + 0x08,sf); - /* 0x0a: granule start? (negative) */ - /* 0x0c: granule max? */ + /* 0x0a: headroom */ + /* 0x0c: unknown */ + /* 0x0e: unknown */ /* 0x10: unknown */ awc->codec = read_u8(tag_offset + 0x1c, sf); /* 16b? */ - /* 0x1e: vorbis header size */ - awc->channels = 1; + /* 0x1e: vorbis setup size */ + if (read_u16(tag_offset + 0x1e,sf))/* rarely not set and uses a tag below */ + awc->vorbis_offset[0] = tag_offset + 0x20; /* data up to vorbis setup size */ - awc->vorbis_offset[0] = tag_offset + 0x20; + awc->channels = 1; break; - case 0xA3: /* block-to-sample table (32b x number of blocks w/ num_samples at the start of each block) */ + case 0x7F: /* vorbis setup */ + if (awc->is_streamed) { + /* music stream doesn't have this (instead every channel-strem have one, parsed later) */ + VGM_LOG("AWC: vorbis setup found but streamed\n"); + goto fail; + } + + /* very rarely used for sfx: SS_AM/GESTURE01.awc */ + awc->vorbis_offset[0] = tag_offset; + break; + + case 0x68: /* midi data [Red Dead Redemption 2 (PC)] */ + /* set fake info so awc doesn't break */ + awc->stream_offset = tag_offset; + awc->stream_size = tag_size; + + awc->num_samples = 48000; + awc->sample_rate = 48000; + awc->codec = 0xFF; + awc->channels = 1; + break; + + + case 0xA3: /* block-to-sample table (32b x number of blocks w/ num_samples at the start of each block) + * or frame-size table (16b x number of frames) in some cases (ex. sfx+mpeg but not sfx+vorbis) */ case 0xBD: /* events (32bx4): type_hash, params_hash, timestamp_ms, flags */ - case 0x5C: /* animation/RSC config? */ - default: /* 0x68=midi?, 0x36=hash thing?, 0x2B=sizes, 0x5A/0xD9=? */ + case 0x5C: /* animation/RSC info? */ + case 0x81: /* animation/CSR info? */ + case 0x36: /* list of hash-things? */ + case 0x2B: /* events/sizes? */ + default: /* 0x68=midi?, 0x5A/0xD9=? */ //VGM_LOG("AWC: ignoring unknown tag 0x%02x\n", tag); break; } } + /* in music mode there tags for other streams we don't use, except for vorbis. streams have vorbis setup info for channels, but + * channel<>stream order doesn't match, so we need to assign setup to channels. All setups seem to be the same though. */ + if (awc->is_streamed && awc->codec == 0x08) { + offset = awc->tags_offset; + offset += 0x08 * awc->stream_info[0].tag_count; /* ignore 1st/music stream */ + + for (int stream = 1; stream < entries; stream++) { + for (int tag = 0; tag < awc->stream_info[stream].tag_count; tag++) { + uint64_t tag_header = read_u64(offset,sf); + uint8_t tag_type = ((tag_header >> 56) & 0xFF); /* 8b */ + //uint32_t tag_size = ((tag_header >> 28) & 0x0FFFFFFF); /* 28b */ + uint32_t tag_offset = ((tag_header >> 0) & 0x0FFFFFFF); /* 28b */ + + switch(tag_type) { + case 0x7f: /* vorbis setup */ + /* find which channel uses this stream's data */ + for (int ch = 0; ch < awc->channels; ch++) { + if (awc->channel_hash[ch] == awc->stream_info[stream].hash_id) { + awc->vorbis_offset[ch] = tag_offset; + //awc->vorbis_size[ch] = tag_size; /* not needed (implicit)*/ + break; + } + } + break; + default: + break; + } + + offset += 0x08; + } + } + } + if (!awc->stream_offset) { VGM_LOG("AWC: stream offset not found\n"); goto fail; } - /* vorbis offset table, somehow offsets are unordered and can go before tags */ - if (awc->is_music && awc->codec == 0x08) { - offset += 0x08 * tag_count; - - for (ch = 0; ch < awc->channels; ch++) { - awc->vorbis_offset[ch] = read_u16(offset + 0x08*ch + 0x00, sf); - /* 0x02: always 0xB000? */ - /* 0x04: always 0x00CD? */ - /* 0x06: always 0x7F00? */ - } - } - - - /* In music mode, data is divided into blocks of block_chunk size with padding. - * Each block has a header/seek table and interleaved data for all channels */ - { - int32_t seek_start = read_u32(awc->stream_offset, sf); /* -1 in later (RDR2) versions */ - if (awc->is_music && !(seek_start == 0 || seek_start == -1)) { - VGM_LOG("AWC: music found, but block doesn't start with seek table at %x\n", (uint32_t)awc->stream_offset); - goto fail; - } - } - - return 1; fail: return 0; @@ -414,140 +543,113 @@ fail: /* ************************************************************************* */ -//TODO: this method won't work properly, needs internal handling of blocks. -// -// This setups a decoder per block, but seems Vorbis' uses first frame as setup so it -// returns samples (576 vs 1024), making num_samples count in each block being off + causing -// gaps. So they must be using a single encoder + setting decode_to_discard per block -// to ge the thing working. -// -// However since blocks are probably also used for seeking, maybe they aren't resetting -// the decoder when seeking? or they force first frame to be 1024? -// -// In case of Vorvis, when setting skip samples seems repeated data from last block is -// exactly last 0x800 bytes of that channel. - -static VGMSTREAM* build_block_vgmstream(STREAMFILE* sf, awc_header* awc, int channel, int32_t num_samples, int32_t skip_samples, off_t block_start, size_t block_size) { - STREAMFILE* temp_sf = NULL; +static VGMSTREAM* build_blocks_vgmstream(STREAMFILE* sf, awc_header* awc, int channel) { VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; int block_channels = 1; + uint32_t substream_size, substream_offset; + /* setup custom IO streamfile that removes AWC's odd blocks (not perfect but serviceable) */ + temp_sf = setup_awc_streamfile(sf, awc->stream_offset, awc->stream_size, awc->block_chunk, awc->channels, channel, awc->codec, awc->big_endian); + if (!temp_sf) goto fail; + + substream_offset = 0x00; + substream_size = get_streamfile_size(temp_sf); + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(block_channels, 0); if (!vgmstream) goto fail; - vgmstream->sample_rate = awc->sample_rate; - vgmstream->num_samples = num_samples - skip_samples; - vgmstream->stream_size = block_size; vgmstream->meta_type = meta_AWC; + vgmstream->sample_rate = awc->sample_rate; + vgmstream->num_samples = awc->num_samples; + vgmstream->stream_size = awc->stream_size; + + vgmstream->stream_size = substream_size; + switch(awc->codec) { +#ifdef VGM_USE_FFMPEG + case 0x05: { /* XMA2 (X360) */ + vgmstream->codec_data = init_ffmpeg_xma2_raw(temp_sf, substream_offset, substream_size, awc->num_samples, block_channels, awc->sample_rate, 0, 0); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_FFmpeg; + vgmstream->layout_type = layout_none; + + xma_fix_raw_samples(vgmstream, temp_sf, substream_offset, substream_size, 0, 0,0); /* samples are ok? */ + break; + } +#endif #ifdef VGM_USE_VORBIS - case 0x08: { /* Vorbis (PC) [Red Dead Redemption 2 (PC)] */ + case 0x08: { vorbis_custom_config cfg = {0}; cfg.channels = 1; cfg.sample_rate = awc->sample_rate; - cfg.header_offset = awc->vorbis_offset[channel]; - //cfg.skip_samples = skip_samples; //todo + cfg.header_offset = awc->vorbis_offset[channel]; /* setup page goes separate */ - vgmstream->codec_data = init_vorbis_custom(sf, block_start, VORBIS_AWC, &cfg); + /* note that it needs sf on init to read the header + start offset for later, and temp_sf on decode to read data */ + vgmstream->codec_data = init_vorbis_custom(sf, substream_offset, VORBIS_AWC, &cfg); if (!vgmstream->codec_data) goto fail; vgmstream->layout_type = layout_none; vgmstream->coding_type = coding_VORBIS_custom; + break; + } +#endif +#ifdef VGM_USE_ATRAC9 + case 0x0F: { + atrac9_config cfg = {0}; + + /* read from first block (all blocks have it but same thing), see awc_streamfile.h */ + uint32_t extradata_offset = awc->stream_offset + 0x10 * awc->channels + 0x70 * channel + 0x0c; + + cfg.channels = block_channels; + cfg.encoder_delay = 0; //? + cfg.config_data = read_u32be(extradata_offset, sf); + + vgmstream->codec_data = init_atrac9(&cfg); + if (!vgmstream->codec_data) goto fail; + vgmstream->coding_type = coding_ATRAC9; + vgmstream->layout_type = layout_none; + + break; } - break; #endif default: goto fail; } - if (!vgmstream_open_stream(vgmstream, sf, block_start)) + if (!vgmstream_open_stream(vgmstream, temp_sf, substream_offset)) goto fail; close_streamfile(temp_sf); return vgmstream; fail: + ;VGM_LOG("AWB: can't open decoder\n"); + close_vgmstream(vgmstream); close_streamfile(temp_sf); - close_vgmstream(vgmstream); - return NULL; -} - -static VGMSTREAM* build_blocks_vgmstream(STREAMFILE* sf, awc_header* awc, int channel) { - VGMSTREAM* vgmstream = NULL; - segmented_layout_data* data = NULL; - int i, ch; - int blocks = awc->stream_size / awc->block_chunk + (awc->stream_size % awc->block_chunk ? 1 : 0) ; - - - /* init layout */ - data = init_layout_segmented(blocks); - if (!data) goto fail; - - /* one segment per block of this channel */ - for (i = 0; i < blocks; i++) { - off_t block_offset = awc->stream_offset + i * awc->block_chunk; - int32_t num_samples = 0, skip_samples = 0; - uint32_t header_skip = 0, block_skip = 0, block_start = 0, block_data = 0; - - /* read stupid block crap to get proper offsets and whatnot, format: - * - per channel: number of channel entries + skip samples + num samples - * - per channel: seek table with N entries */ - for (ch = 0; ch < awc->channels; ch++) { - /* 0x00: -1 */ - int entries = read_u32le(block_offset + 0x18 * ch + 0x04, sf); - int32_t entry_skip = read_u32le(block_offset + 0x18 * ch + 0x08, sf); - int32_t entry_samples = read_u32le(block_offset + 0x18 * ch + 0x0c, sf); - - if (ch == channel) { - num_samples = entry_samples; - skip_samples = entry_skip; - - block_start = block_offset + block_skip; - block_data = entries * 0x800; - } - - header_skip += 0x18 + entries * 0x04; - block_skip += entries * 0x800; - } - - if (!block_start) - goto fail; - - header_skip = align_size_to_block(header_skip, 0x800); - block_start += header_skip; - //;VGM_LOG("AWC: build ch%i, block=%i at %lx, o=%x, s=%x, ns=%i, ss=%i\n", channel, i, block_offset, block_start, block_data, num_samples, skip_samples); - - data->segments[i] = build_block_vgmstream(sf, awc, channel, num_samples, skip_samples, block_start, block_data); - if (!data->segments[i]) goto fail; - } - - /* setup VGMSTREAMs */ - if (!setup_layout_segmented(data)) - goto fail; - - /* build the layout VGMSTREAM */ - vgmstream = allocate_segmented_vgmstream(data, 0, 0, 0); - if (!vgmstream) goto fail; - - return vgmstream; -fail: - close_vgmstream(vgmstream); - if (!vgmstream) - free_layout_segmented(data); return NULL; } /* ************************************************************************* */ -/* Make layers per channel for AWC's abhorrent blocks. +/* Make layers per channel for AWC's abhorrent blocks (see read_awb_block). * - * File has N channels = N streams, that use their own mono decoder. - * Each block then has header + seek table for all channels. But in each block there is - * a "skip samples" value per channel, and blocks repeat some data from last block - * for this, so PCM must be discarded. Also, channels in a block don't need to have - * the same number of samples. + * A "music" .awc has N channels = N streams (each using their own mono decoder) chunked in "blocks". + * Each block then has header + seek table + etc for all channels. But when blocks change, each channel + * may have a "skip samples" value and blocks repeat some data from last block, so output PCM must be + * discarded to avoid channels desyncing. Channels in a block don't need to have the same number of samples. + * (mainly seen in MPEG). */ +//TODO: this method won't fully work, needs feed decoder + block handler that interacts with decoder(s?) +// (doesn't use multiple decoders since default encoder delay in Vorbis would discard too much per block) +// +// When blocks change presumably block handler needs to tell decoder to finish decoding all from prev block +// then skip samples from next decodes. Also since samples may vary per channel, each would handle blocks +// independently. +// +// This can be simulated by making one decoder per block (segmented, but opens too many SFs and can't skip +// samples correctly), or with a custom STREAMFILE that skips repeated block (works ok-ish but not all codecs). static layered_layout_data* build_layered_awc(STREAMFILE* sf, awc_header* awc) { int i; layered_layout_data* data = NULL; @@ -571,4 +673,3 @@ fail: free_layout_layered(data); return NULL; } - diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awc_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/awc_streamfile.h new file mode 100644 index 000000000..942ab5fc8 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/awc_streamfile.h @@ -0,0 +1,253 @@ +#ifndef _AWC_STREAMFILE_H_ +#define _AWC_STREAMFILE_H_ +#include "deblock_streamfile.h" +#include "../util/endianness.h" + +#define AWC_MAX_MUSIC_CHANNELS 32 /* seen ~24 */ + +/* ************************************************************************* */ + +typedef struct { + int start_entry; /* innacurate! */ + int entries; + int32_t channel_skip; + int32_t channel_samples; + + uint32_t frame_size; + + /* derived */ + uint32_t chunk_start; /* relative to block offset */ + uint32_t chunk_size; /* size of this channel's data (not including padding) */ +} awc_block_t; + +typedef struct { + int big_endian; + uint8_t codec; + int channels; + uint32_t block_offset; + awc_block_t blk[AWC_MAX_MUSIC_CHANNELS]; +} awc_block_info_t; + +/* Block format: + * - block header for all channels (needed to find frame start) + * - frames from channel 1 + * - ... + * - frames from channel N + * - usually there is padding between channels or blocks (usually 0s but seen 0x97 in AT9) + * + * Header format: + * - per channel (frame start table) + * 0x00: start entry for that channel? (-1 in vorbis) + * may be off by +1/+2? + * ex. on block 0, ch0/1 have 0x007F frames, a start entry is: ch0=0x0000, ch1=0x007F (MP3) + * ex. on block 0, ch0/1 have 0x02A9 frames, a start entry is: ch0=0x0000, ch1=0x02AA (AT9) !! + * (sum of all values from all channels may go beyond all posible frames, no idea) + * 0x04: frames in this channel (may be different between channels) + * 'frames' here may be actual single decoder frames or a chunk of frames + * 0x08: samples to discard in the beginning of this block (MPEG/XMA2/Vorbis only?) + * 0x0c: samples in channel (for MPEG/XMA2 can vary between channels) + * full samples without removing samples to discard + * (next fields only exists for MPEG, Vorbis or some IMA versions) + * 0x10: (MPEG only, empty otherwise) close to number of frames but varies a bit? + * 0x14: (MPEG only, empty otherwise) channel chunk size (not counting padding) + * - for each channel (seek table) + * 32b * entries = global samples per frame in each block (for MPEG probably per full frame) + * (AT9 doesn't have a seek table as it's CBR) + * - per channel (ATRAC9/DSP extra info): + * 0x00: "D11A" + * 0x04: frame size + * 0x06: frame samples + * 0x08: flags? (0x0103=AT9, 0x0104=DSP) + * 0x0a: sample rate + * 0x0c: ATRAC9 config (repeated but same for all blocks) or "D11E" (DSP) + * 0x10-0x70: padding with 0x77 (ATRAC3) or standard DSP header for original full file (DSP) + * - padding until channel data start, depending on codec (DSP/ATRAC9: one, others: aligned to 0x800) + * - per channel: + * 0xNN: channel frames + * 0xNN: may have padding between channels depending on codec (mainly MPEG/XMA) + * - padding until this block's end + */ +static bool read_awb_block(STREAMFILE* sf, awc_block_info_t* bi) { + read_s32_t read_s32 = bi->big_endian ? read_s32be : read_s32le; + read_u16_t read_u16 = bi->big_endian ? read_u16be : read_u16le; + + uint32_t channel_entry_size, seek_entry_size, extra_entry_size, header_padding; + uint32_t offset = bi->block_offset; + int channels = bi->channels; + /* read stupid block crap + derived info at once so hopefully it's a bit easier to understand */ + + switch(bi->codec) { + case 0x05: /* XMA2 */ + channel_entry_size = 0x10; + seek_entry_size = 0x04; + extra_entry_size = 0x00; + header_padding = 0x800; + break; + case 0x08: /* Vorbis */ + channel_entry_size = 0x18; + seek_entry_size = 0x04; + extra_entry_size = 0x00; + header_padding = 0x800; + break; + case 0x0F: /* ATRAC9 */ + channel_entry_size = 0x10; + seek_entry_size = 0x00; + extra_entry_size = 0x70; + header_padding = 0x00; + break; + default: + goto fail; + } + + /* channel info table */ + for (int ch = 0; ch < bi->channels; ch++) { + bi->blk[ch].start_entry = read_s32(offset + 0x00, sf); + bi->blk[ch].entries = read_s32(offset + 0x04, sf); + bi->blk[ch].channel_skip = read_s32(offset + 0x08, sf); + bi->blk[ch].channel_samples = read_s32(offset + 0x0c, sf); + /* others: optional */ + + offset += channel_entry_size; + } + + /* seek table */ + for (int ch = 0; ch < channels; ch++) { + offset += bi->blk[ch].entries * seek_entry_size; + } + + /* extra table and derived info */ + for (int ch = 0; ch < channels; ch++) { + switch(bi->codec) { + case 0x05: /* XMA2 */ + case 0x08: /* Vorbis */ + /* each 'frame'/entry in Vorbis is actually N vorbis frames then padding up to 0x800 + * (more or less like a big Ogg page or XMA 'frame'). Padding is considered part of + * the data and handled by the decoder, since sfx (non-blocked) algo have it. */ + bi->blk[ch].frame_size = 0x800; + bi->blk[ch].chunk_size = bi->blk[ch].entries * bi->blk[ch].frame_size; + break; + + case 0x0F: /* ATRAC9 */ + bi->blk[ch].frame_size = read_u16(offset + 0x04, sf); + bi->blk[ch].chunk_size = bi->blk[ch].entries * bi->blk[ch].frame_size; + break; + + default: + goto fail; + } + offset += extra_entry_size; + } + + /* header done, move into data start */ + if (header_padding) { + /* padding on the current size rather than file offset (block meant to be read into memory, probably) */ + uint32_t header_size = offset - bi->block_offset; + offset = bi->block_offset + align_size_to_block(header_size, header_padding); + } + + /* set frame starts per channel */ + for (int ch = 0; ch < channels; ch++) { + bi->blk[ch].chunk_start = offset - bi->block_offset; + offset += bi->blk[ch].chunk_size; + } + + /* beyond this is padding until chunk_start */ + + return true; +fail: + return false; +} + +/* Find data that repeats in the beginning of a new block at the end of last block. + * When a new block starts there is some repeated data + channel_skip (for seeking + encoder delay?). + * Detect it so decoder may ignore it. */ +static uint32_t get_block_repeated_size(STREAMFILE* sf, awc_block_info_t* bi, int channel) { + + if (bi->blk[channel].channel_skip == 0) + return 0; + + switch(bi->codec) { + case 0x05: { /* XMA2 */ + const uint32_t samples_per_subframe = 512; + uint32_t samples_this_frame; + uint8_t subframes; + uint32_t offset = bi->block_offset + bi->blk[channel].chunk_start; + int repeat_samples = bi->blk[channel].channel_skip; + + //TODO: fix (needs proper decoder + sample discard) + /* Repeat samples are the number of decoded samples to discard, but in this streamfile we can't do that. + * Also XMA is VBR, and may encode silent frames with up to 63 subframes yet we may have few repeat samples. + * We could find out how many subframes of 512 samples to skip, then adjust the XMA frame header, though + * output will be slightly off since subframes are related. + * + * For now just skip a full frame depending on the number of subframes vs repeat samples. + * Most files work ok-ish but channels may desync slightly. */ + + subframes = (read_u8(offset,sf) >> 2) & 0x3F; /* peek into frame header */ + samples_this_frame = subframes * samples_per_subframe; + if (repeat_samples >= (int)(samples_this_frame * 0.13)) { /* skip mosts */ + return bi->blk[channel].frame_size; + } + else { + return 0; + } + } + + case 0x08: /* Vorbis */ + /* when data repeats seems to clone exactly the last super-frame */ + return bi->blk[channel].frame_size; + + case 0x0F: /* ATRAC9 */ + default: + VGM_LOG("AWC: found channel skip in codec %x\n", bi->codec); /* not seen */ + return 0; + } +} + +/* ************************************************************************* */ + +static void block_callback(STREAMFILE *sf, deblock_io_data* data) { + int channel = data->cfg.track_number; + awc_block_info_t bi = {0}; + + bi.big_endian = data->cfg.big_endian; + bi.block_offset = data->physical_offset; + bi.channels = data->cfg.track_count; + bi.codec = data->cfg.track_type; + + if (!read_awb_block(sf, &bi)) + return; //??? + + uint32_t repeat_size = get_block_repeated_size(sf, &bi, channel); + + data->block_size = data->cfg.chunk_size; + data->skip_size = bi.blk[channel].chunk_start + repeat_size; + data->data_size = bi.blk[channel].chunk_size - repeat_size; +} + +/* deblocks AWC blocks */ +static STREAMFILE* setup_awc_streamfile(STREAMFILE* sf, uint32_t stream_offset, uint32_t stream_size, uint32_t block_size, int channels, int channel, uint8_t codec, int big_endian) { + STREAMFILE* new_sf = NULL; + deblock_config_t cfg = {0}; + + if (channels >= AWC_MAX_MUSIC_CHANNELS) + return NULL; + + cfg.track_number = channel; + cfg.track_count = channels; + cfg.stream_start = stream_offset; + cfg.stream_size = stream_size; + cfg.chunk_size = block_size; + cfg.track_type = codec; + cfg.big_endian = big_endian; + //cfg.physical_offset = stream_offset; + //cfg.logical_size = awc_xma_io_size(sf, &cfg); /* force init */ + cfg.block_callback = block_callback; + + new_sf = open_wrap_streamfile(sf); + new_sf = open_io_deblock_streamfile_f(new_sf, &cfg); + //new_sf = open_buffer_streamfile_f(new_sf, 0); + return new_sf; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/awc_xma_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/awc_xma_streamfile.h deleted file mode 100644 index 17b9799c6..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/awc_xma_streamfile.h +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef _AWC_XMA_STREAMFILE_H_ -#define _AWC_XMA_STREAMFILE_H_ -#include "deblock_streamfile.h" - - -static size_t get_block_header_size(STREAMFILE* sf, off_t offset, int channels); -static size_t get_repeated_data_size(STREAMFILE* sf, off_t next_offset, size_t repeat_samples); -static size_t get_block_skip_count(STREAMFILE* sf, off_t offset, int channel); - -static void block_callback(STREAMFILE *sf, deblock_io_data* data) { - const size_t frame_size = 0x800; - int channel = data->cfg.track_number; - int channels = data->cfg.track_count; - - /* Blocks have a header then data per channel, each with a different num_samples/frames, - * separate (first all frames of ch0, then ch1, etc), padded, and sometimes the last few - * frames of a channel are repeated in the new block (marked with "repeat samples"). */ - size_t header_size = get_block_header_size(sf, data->physical_offset, channels); - /* header table entries = frames... I hope */ - size_t others_size = get_block_skip_count(sf, data->physical_offset, channel) * frame_size; - //size_t skip_size = read_u32be(data->physical_offset + 0x10*channel + 0x00, sf) * frame_size; - size_t data_size = read_u32be(data->physical_offset + 0x10*channel + 0x04, sf) * frame_size; - size_t repeat_samples = read_u32be(data->physical_offset + 0x10*channel + 0x08, sf); - size_t repeat_size = 0; - - data->block_size = data->cfg.chunk_size; - - /* if there are repeat samples current block repeats some frames from last block, find out size */ - if (repeat_samples) { - off_t data_offset = data->physical_offset + header_size + others_size; - repeat_size = get_repeated_data_size(sf, data_offset, repeat_samples); - } - - data->skip_size = header_size + others_size + repeat_size; - data->data_size = data_size - repeat_size; -} - -/* block header size, aligned/padded to 0x800 */ -static size_t get_block_header_size(STREAMFILE* sf, off_t offset, int channels) { - size_t header_size = 0; - int i; - - for (i = 0; i < channels; i++) { - header_size += 0x10; - header_size += read_u32be(offset + 0x10*i + 0x04, sf) * 0x04; /* entries in the table */ - } - - if (header_size % 0x800) /* padded */ - header_size += 0x800 - (header_size % 0x800); - - return header_size; -} - -/* find data that repeats in the beginning of a new block at the end of last block */ -static size_t get_repeated_data_size(STREAMFILE* sf, off_t next_offset, size_t repeat_samples) { - const size_t frame_size = 0x800; - const size_t samples_per_subframe = 512; - size_t samples_this_frame; - uint8_t subframes; - - //todo: fix this - /* Repeat samples are the number of decoded samples to discard, but in this streamfile we can't do that. - * Also XMA is VBR, and may encode silent frames with up to 63 subframes yet we may have few repeat samples. - * We could find out how many subframes of 512 samples to skip, then adjust the XMA frame header, though - * output will be slightly off since subframes are related. - * - * For now just skip a full frame depending on the number of subframes vs repeat samples. - * Most files work ok-ish but channels may desync slightly. */ - - subframes = ((uint8_t)read_8bit(next_offset,sf) >> 2) & 0x3F; /* peek into frame header */ - samples_this_frame = subframes*samples_per_subframe; - if (repeat_samples >= (int)(samples_this_frame*0.13)) { /* skip mosts */ - return frame_size; - } - else { - return 0; - } -} - -/* header has a skip value, but somehow it's sometimes bigger than expected (WHY!?!?) so just sum all */ -static size_t get_block_skip_count(STREAMFILE* sf, off_t offset, int channel) { - size_t skip_count = 0; - int i; - - //skip_size = read_u32be(offset + 0x10*channel + 0x00, sf); /* wrong! */ - for (i = 0; i < channel; i++) { - skip_count += read_u32be(offset + 0x10*i + 0x04, sf); /* number of frames of this channel */ - } - - return skip_count; -} - - -/* Deblocks interleaved XMA in AWC blocks */ -static STREAMFILE* setup_awc_xma_streamfile(STREAMFILE *sf, off_t stream_offset, size_t stream_size, size_t block_size, int channel_count, int channel) { - STREAMFILE *new_sf = NULL; - deblock_config_t cfg = {0}; - - cfg.track_number = channel; - cfg.track_count = channel_count; - cfg.stream_start = stream_offset; - cfg.stream_size = stream_size; - cfg.chunk_size = block_size; - //cfg.physical_offset = stream_offset; - //cfg.logical_size = awc_xma_io_size(sf, &cfg); /* force init */ - cfg.block_callback = block_callback; - - new_sf = open_wrap_streamfile(sf); - new_sf = open_io_deblock_streamfile_f(new_sf, &cfg); - //new_sf = open_buffer_streamfile_f(new_sf, 0); - return new_sf; -} - -#endif /* _AWC_XMA_STREAMFILE_H_ */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c b/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c index f18debac4..810c44e53 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bkhd.c @@ -143,6 +143,11 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) { vgmstream = init_vgmstream_wwise_bnk(temp_sf, &prefetch); if (!vgmstream) goto fail; } +else if (is_id32be(0x00, temp_sf, "ADM3")) { + // TODO: these may have multiple subsongs + vgmstream = init_vgmstream_adm3(temp_sf); + if (!vgmstream) goto fail; + } else if (read_f32(subfile_offset + 0x02, temp_sf) >= 30.0 && read_f32(subfile_offset + 0x02, temp_sf) <= 250.0) { is_wmid = 1; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c b/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c index 0e10d4795..e3885c070 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c @@ -3,7 +3,7 @@ #include "../coding/coding.h" #include "../util/endianness.h" -typedef enum { NONE, DUMMY, PSX, PCM16, ATRAC9, HEVAG, RIFF_ATRAC9 } bnk_codec; +typedef enum { NONE, DUMMY, PSX, PCM16, MPEG, ATRAC9, HEVAG, RIFF_ATRAC9 } bnk_codec; typedef struct { bnk_codec codec; @@ -41,6 +41,7 @@ typedef struct { int32_t num_samples; int32_t loop_start; int32_t loop_end; + int32_t encoder_delay; uint32_t start_offset; uint32_t stream_offset; @@ -81,7 +82,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { vgmstream->meta_type = meta_BNK_SONY; - if (!h.stream_name_size) + if (h.stream_name_size >= STREAM_NAME_SIZE || h.stream_name_size <= 0) h.stream_name_size = STREAM_NAME_SIZE; if (!h.bank_name_offset && h.stream_name_offset) { @@ -90,7 +91,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { else if (h.bank_name_offset && h.stream_name_offset) { read_string(bank_name, h.stream_name_size, h.bank_name_offset, sf); read_string(stream_name, h.stream_name_size, h.stream_name_offset, sf); - snprintf(vgmstream->stream_name, h.stream_name_size, "%s/%s", bank_name, stream_name); + snprintf(vgmstream->stream_name, h.stream_name_size, "%s%s%s", bank_name, bank_name[0] == '\0' ? "" : "/", stream_name); } @@ -147,6 +148,20 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { return temp_vs; } #endif +#ifdef VGM_USE_MPEG + case MPEG: { + mpeg_custom_config cfg = {0}; + cfg.skip_samples = h.encoder_delay; + + vgmstream->layout_type = layout_none; + vgmstream->codec_data = init_mpeg_custom(sf, h.start_offset, &vgmstream->coding_type, h.channels, MPEG_STANDARD, &cfg); + if (!vgmstream->codec_data) goto fail; + + vgmstream->num_samples = h.num_samples; + break; + } +#endif + case PCM16: vgmstream->coding_type = h.big_endian ? coding_PCM16BE : coding_PCM16LE; vgmstream->layout_type = layout_interleave; @@ -417,12 +432,12 @@ static bool process_headers(STREAMFILE* sf, bnk_header_t* h) { switch(h->sblk_version) { case 0x01: /* table2/3 has size 0x28 entries, seemingly: - * 0x00: subtype(01=sound) - * 0x08: same as other versions (pitch, flags, offset...) - * rest: padding - * 0x18: stream offset - * there is no stream size like in v0x03 - */ + * 0x00: subtype(01=sound) + * 0x08: same as other versions (pitch, flags, offset...) + * rest: padding + * 0x18: stream offset + * there is no stream size like in v0x03 + */ for (i = 0; i < h->grains_entries; i++) { uint32_t table2_type = read_u32(h->table2_offset + (i*0x28) + 0x00, sf); @@ -527,22 +542,22 @@ static bool process_headers(STREAMFILE* sf, bnk_header_t* h) { h->sample_rate = (pitch * 48000) / 0x1000; /* waves can set base sample rate (48/44/22/11/8khz) + pitch in semitones, then converted to center+fine - * 48000 + pitch 0.00 > center=0xc4, fine=0x00 - * 48000 + pitch 0.10 > center=0xc4, fine=0x0c - * 48000 + pitch 0.50 > center=0xc4, fine=0x3f - * 48000 + pitch 0.99 > center=0xc4, fine=0x7d - * 48000 + pitch 1.00 > center=0xc5, fine=0x00 - * 48000 + pitch 12.00 > center=0xd0, fine=0x00 - * 48000 + pitch 24.00 > center=0xdc, fine=0x00 - * 48000 + pitch 56.00 > center=0xfc, fine=0x00 - * 48000 + pitch 68.00 > center=0x08, fine=0x00 > ? - * 48000 + pitch -12.00 > center=0xb8, fine=0x00 - * 48000 + pitch -0.10 > center=0xc3, fine=0x72 - * 48000 + pitch -0.001 > not allowed - * 8000 + pitch 1.00 > center=0xa4, fine=0x7c - * 8000 + pitch -12.00 > center=0x98, fine=0x7c - * 8000 + pitch -48.00 > center=0x74, fine=0x7c - */ + * 48000 + pitch 0.00 > center=0xc4, fine=0x00 + * 48000 + pitch 0.10 > center=0xc4, fine=0x0c + * 48000 + pitch 0.50 > center=0xc4, fine=0x3f + * 48000 + pitch 0.99 > center=0xc4, fine=0x7d + * 48000 + pitch 1.00 > center=0xc5, fine=0x00 + * 48000 + pitch 12.00 > center=0xd0, fine=0x00 + * 48000 + pitch 24.00 > center=0xdc, fine=0x00 + * 48000 + pitch 56.00 > center=0xfc, fine=0x00 + * 48000 + pitch 68.00 > center=0x08, fine=0x00 > ? + * 48000 + pitch -12.00 > center=0xb8, fine=0x00 + * 48000 + pitch -0.10 > center=0xc3, fine=0x72 + * 48000 + pitch -0.001 > not allowed + * 8000 + pitch 1.00 > center=0xa4, fine=0x7c + * 8000 + pitch -12.00 > center=0x98, fine=0x7c + * 8000 + pitch -48.00 > center=0x74, fine=0x7c + */ break; } @@ -567,7 +582,7 @@ static bool process_headers(STREAMFILE* sf, bnk_header_t* h) { goto fail; } - //;VGM_LOG("BNK: stream at %lx + %x\n", h->stream_offset, h->stream_size); + ;VGM_LOG("BNK: header %x, stream at %x + %x\n", sndh_offset, h->data_offset + h->stream_offset, h->stream_size); return true; fail: @@ -602,17 +617,17 @@ static bool process_names(STREAMFILE* sf, bnk_header_t* h) { } /* table4: - * 0x00: bank name (optional) - * 0x08: name section offset - * 0x0C-0x14: 3 null pointers (reserved?) - * 0x18-0x58: 32 name chunk offset indices - */ + * 0x00: bank name (optional) + * 0x08: name section offset + * 0x0C-0x18: 3 null pointers (reserved?) + * 0x18-0x58: 32 name chunk offset indices + */ /* Name chunks are organised as - * (name[0] + name[4] + name[8] + name[12]) & 0x1F; - * and using that as the index for the chunk offsets - * name_sect_offset + (chunk_idx[result] * 0x14); - */ + * (name[0] + name[4] + name[8] + name[12]) % 32; + * and using that as the index for the chunk offsets + * name_sect_offset + (chunk_idx[result] * 0x14); + */ if (read_u8(h->table4_offset, sf)) h->bank_name_offset = h->table4_offset; @@ -626,7 +641,7 @@ static bool process_names(STREAMFILE* sf, bnk_header_t* h) { while (read_u8(h->stream_name_offset, sf)) { /* in case it goes somewhere out of bounds unexpectedly */ if (((read_u8(h->stream_name_offset + 0x00, sf) + read_u8(h->stream_name_offset + 0x04, sf) + - read_u8(h->stream_name_offset + 0x08, sf) + read_u8(h->stream_name_offset + 0x0C, sf)) & 0x1F) != i) + read_u8(h->stream_name_offset + 0x08, sf) + read_u8(h->stream_name_offset + 0x0C, sf)) & 0x1F) != i) goto fail; if (read_u16(h->stream_name_offset + 0x10, sf) == table4_entry_id) goto loop_break; /* to break out of the for+while loop simultaneously */ @@ -653,16 +668,16 @@ static bool process_names(STREAMFILE* sf, bnk_header_t* h) { } /* table4: - * 0x00: bank name (optional) - * 0x08: name entries offset - * 0x0C: name section offset - * - * name entries offset: - * 0x00: name offset in name section - * 0x04: name hash(?) - * 0x08: ? (2x int16) - * 0x0C: section index (int16) - */ + * 0x00: bank name (optional) + * 0x08: name entries offset + * 0x0C: name section offset + * + * name entries offset: + * 0x00: name offset in name section + * 0x04: name hash(?) + * 0x08: ? (2x int16) + * 0x0C: section index (int16) + */ if (read_u8(h->table4_offset, sf)) h->bank_name_offset = h->table4_offset; @@ -722,7 +737,7 @@ static bool process_names(STREAMFILE* sf, bnk_header_t* h) { break; } - //;VGM_LOG("BNK: stream_offset=%lx, stream_size=%x, stream_name_offset=%lx\n", h->stream_offset, h->stream_size, h->stream_name_offset); + //;VGM_LOG("BNK: stream_offset=%x, stream_size=%x, stream_name_offset=%x\n", h->stream_offset, h->stream_size, h->stream_name_offset); return true; fail: @@ -785,19 +800,19 @@ static bool process_data(STREAMFILE* sf, bnk_header_t* h) { h->interleave = h->stream_size / h->channels; /* PS Home Arcade has other flags? supposedly: - * 01 = reverb - * 02 = vol scale 20 - * 04 = vol scale 50 - * 06 = vol scale 100 - * 08 = noise - * 10 = no dry - * 20 = no steal - * 40 = loop VAG - * 80 = PCM - * 100 = has advanced packets - * 200 = send LFE - * 400 = send center - */ + * 01 = reverb + * 02 = vol scale 20 + * 04 = vol scale 50 + * 06 = vol scale 100 + * 08 = noise + * 10 = no dry + * 20 = no steal + * 40 = loop VAG + * 80 = PCM + * 100 = has advanced packets + * 200 = send LFE + * 400 = send center + */ if ((h->stream_flags & 0x80) && h->sblk_version <= 3) { h->codec = PCM16; /* rare [Wipeout HD (PS3)]-v3 */ } @@ -813,8 +828,8 @@ static bool process_data(STREAMFILE* sf, bnk_header_t* h) { case 0x08: case 0x09: - subtype = read_u16(h->start_offset+0x00,sf); - extradata_size = 0x08 + read_u32(h->start_offset+0x04,sf); /* 0x14 for AT9 */ + subtype = read_u32(h->start_offset+0x00,sf); + extradata_size = 0x08 + read_u32(h->start_offset+0x04,sf); /* 0x14 for AT9, 0x10 for PCM, 0x90 for MPEG */ switch(subtype) { case 0x00: @@ -829,22 +844,49 @@ static bool process_data(STREAMFILE* sf, bnk_header_t* h) { h->interleave = 0x01; break; - - case 0x02: /* ATRAC9 mono */ - case 0x05: /* ATRAC9 stereo */ - if (read_u32(h->start_offset+0x08,sf) + 0x08 != extradata_size) { /* repeat? */ - VGM_LOG("BNK: unknown subtype\n"); - goto fail; - } + case 0x02: /* ATRAC9 / MPEG mono */ + case 0x05: /* ATRAC9 / MPEG stereo */ h->channels = (subtype == 0x02) ? 1 : 2; - h->atrac9_info = read_u32be(h->start_offset+0x0c,sf); - /* 0x10: null? */ - loop_length = read_u32(h->start_offset+0x14,sf); - h->loop_start = read_u32(h->start_offset+0x18,sf); - h->loop_end = h->loop_start + loop_length; /* loop_start is -1 if not set */ + if (h->big_endian) { + /* The Last of Us demo (PS3) */ - h->codec = ATRAC9; + /* 0x08: mpeg version? (1) */ + /* 0x0C: mpeg layer? (3) */ + /* 0x10: ? (related to frame size, 0xC0 > 0x40, 0x120 > 0x60) */ + /* 0x14: sample rate */ + /* 0x18: mpeg layer? (3) */ + /* 0x1c: mpeg version? (1) */ + /* 0x20: channels? */ + /* 0x24: frame size */ + /* 0x28: encoder delay */ + /* 0x2c: num samples */ + /* 0x30: ? */ + /* 0x34: ? */ + /* 0x38: 0? */ + /* 0x3c: data size */ + /* padding up to 0x90 */ + + h->encoder_delay = read_s32(h->start_offset+0x28,sf); + h->num_samples = read_s32(h->start_offset+0x2c,sf); + + h->codec = MPEG; + } + else { + /* Puyo Puyo Tetris (PS4) */ + if (read_u32(h->start_offset+0x08,sf) + 0x08 != extradata_size) { /* repeat? */ + VGM_LOG("BNK: unknown subtype\n"); + goto fail; + } + + h->atrac9_info = read_u32be(h->start_offset+0x0c,sf); + /* 0x10: null? */ + loop_length = read_u32(h->start_offset+0x14,sf); + h->loop_start = read_u32(h->start_offset+0x18,sf); + h->loop_end = h->loop_start + loop_length; /* loop_start is -1 if not set */ + + h->codec = ATRAC9; + } break; default: diff --git a/Frameworks/vgmstream/vgmstream/src/meta/cpk.c b/Frameworks/vgmstream/vgmstream/src/meta/cpk.c index 3d7487345..ef528dc18 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/cpk.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/cpk.c @@ -282,11 +282,11 @@ static void load_cpk_name(STREAMFILE* sf, STREAMFILE* sf_acb, VGMSTREAM* vgmstre return; /* companion .acb probably loaded */ - load_acb_wave_name(sf_acb, vgmstream, waveid, port, is_memory); + load_acb_wave_info(sf_acb, vgmstream, waveid, port, is_memory, 0); close_streamfile(sf_acb); } else { - load_acb_wave_name(sf_acb, vgmstream, waveid, port, is_memory); + load_acb_wave_info(sf_acb, vgmstream, waveid, port, is_memory, 0); } } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/encrypted.c b/Frameworks/vgmstream/vgmstream/src/meta/encrypted.c index 120fcb1c3..a9e18ccc1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/encrypted.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/encrypted.c @@ -154,8 +154,8 @@ static VGMSTREAM* init_vgmstream_encrypted_rpgmvo_riff(STREAMFILE* sf) { e.key_size = 0x10; load_key(&cfg, e.keybuf, e.key_size); cfg.start = 0x10; - cfg.max_offset = 0x10; - + cfg.max_offset = 0x20; + e.temp_sf = setup_ogg_vorbis_streamfile(sf, &cfg); if (!e.temp_sf) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h index 1e57ca345..e7bfeb6b7 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/fsb_keys.h @@ -57,8 +57,6 @@ static const fsbkey_info fsbkey_list[] = { { MODE_FSB4_STD, FSBKEY_ADD("ghfxhslrghfxhslr") }, // Cookie Run: Ovenbreak { MODE_FSB4_ALT, FSBKEY_ADD("truck/impact/carbody") },// Monster Jam (PS2) [FSB3] { MODE_FSB4_ALT, FSBKEY_ADD("\xFC\xF9\xE4\xB3\xF5\x57\x5C\xA5\xAC\x13\xEC\x4A\x43\x19\x58\xEB\x4E\xF3\x84\x0B\x8B\x78\xFA\xFD\xBB\x18\x46\x7E\x31\xFB\xD0") }, // Guitar Hero 5 (X360) - { MODE_FSB4_ALT, FSBKEY_ADD("\x8C\xFA\xF3\x14\xB1\x53\xDA\xAB\x2B\x82\x6B\xD5\x55\x16\xCF\x01\x90\x20\x28\x14\xB1\x53\xD8") }, // Guitar Hero: Metallica (X360) - { MODE_FSB4_STD, FSBKEY_ADD("\xd2\x37\x70\x39\xa9\x86\xc5\xaf\x5b\x7f\xa2\x23\x98\x7e\xb6\xc2\x7e\x18\x7b\x2d\xd9\x31\x4b\x20\xb0\xc1\x8d\x06\xf2\xa7\xcd") }, // Guitar Hero: Metallica (PS3) [FSB4] { MODE_FSB5_STD, FSBKEY_ADD("G0KTrWjS9syqF7vVD6RaVXlFD91gMgkC") }, // Sekiro: Shadows Die Twice (PC) { MODE_FSB5_STD, FSBKEY_ADD("BasicEncryptionKey") }, // SCP: Unity (PC) { MODE_FSB5_STD, FSBKEY_ADD("FXnTffGJ9LS855Gc") }, // Worms Rumble Beta (PC) @@ -73,6 +71,13 @@ static const fsbkey_info fsbkey_list[] = { { MODE_FSB5_STD, FSBKEY_ADD("Aurogon666") }, // Afterimage demo (PC) { MODE_FSB5_STD, FSBKEY_ADD("IfYouLikeThosesSoundsWhyNotRenumerateTheir2Authors?") }, // Blanc (PC/Switch) { MODE_FSB5_STD, FSBKEY_ADD("L36nshM520") }, // Nishuihan Mobile (Android) + { MODE_FSB5_STD, FSBKEY_ADD("Forza2!") }, // Forza Motorsport (PC) + { MODE_FSB5_STD, FSBKEY_ADD("cbfjZTlUPaZI") }, // JDM: Japanese Drift Master (PC) + + /* these games use a key per file, generated from the filename; could be possible to add them but there is a lot of songs, + so external .fsbkey may be better (use guessfsb 3.1 with --write-key-file or ) */ + //{ MODE_FSB4_STD, FSBKEY_ADD("...") }, // Guitar Hero: Metallica (PC/PS3/X360) [FSB4] + //{ MODE_FSB4_STD, FSBKEY_ADD("...") }, // Guitar Hero: World Tour (PC/PS3/X360) [FSB4] }; static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index a0ac49740..0caca4154 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -1254,6 +1254,24 @@ static const hcakey_info hcakey_list[] = { // BlazBlue Entropy Effect (Early Access) (PC) {29814655674508831}, // 0069EC457894661F + // Star Ocean: The Second Story R (PC, Switch) + {533948357975462459}, // 0768F733DD87D23B + + // Girls' Frontline 2: Exilium (Android) + {8930254087621254}, // 001FBA04CEA58A86 + + // Sonic Superstars (Switch) + {1991062320230623}, // 000712DC5252B0DF + + // Persona 5 Tactica (Switch) + {48319776512953016}, // 00ABAA94AAAE4AB8 + + // THE IDOLM@STER Shiny Colors Song For Prism (PC) + {156967709847897761}, // 022DA94CEAB0C6A1 + + // Dokapon Kingdom Connect (PC, Switch) + {104863924750642073}, // 01748d2f1883eb99 + }; #endif/*_HCA_KEYS_H_*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/idtech.c b/Frameworks/vgmstream/vgmstream/src/meta/idtech.c index 191515ae5..99641c23d 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/idtech.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/idtech.c @@ -389,7 +389,7 @@ VGMSTREAM* init_vgmstream_bsnf(STREAMFILE* sf) { if (stream_size != get_streamfile_size(sb)) goto fail; - loop_flag = (loop_start > 0); + loop_flag = (loop_start > 0); /* loops from 0 on some codecs aren't detectable though */ start_offset = 0x00; /* build the VGMSTREAM */ @@ -404,13 +404,18 @@ VGMSTREAM* init_vgmstream_bsnf(STREAMFILE* sf) { vgmstream->num_streams = num_languages; strncpy(vgmstream->stream_name, language, STREAM_NAME_SIZE); + /* for codecs with explicit encoder delay (mp3/at9/etc) num_samples includes it + * ex. mus_c05_dream_doorloop_* does full loops; with some codecs loop start is encoder delay and num_samples + * has extra delay samples compared to codecs with implicit delay (ex. mp3 1152 to 101152 vs ogg 0 to 100000), + * but there is no header value for encoder delay, maybe engine hardcodes it? */ + switch (codec) { #ifdef VGM_USE_MPEG case 0x0055: { mpeg_custom_config cfg = { 0 }; - cfg.skip_samples = 1152; /* seems ok */ + //cfg.skip_samples = 1152; /* observed default */ vgmstream->codec_data = init_mpeg_custom(sb, start_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg); if (!vgmstream->codec_data) goto fail; @@ -451,14 +456,20 @@ VGMSTREAM* init_vgmstream_bsnf(STREAMFILE* sf) { case 0x42D2: { atrac9_config cfg = { 0 }; + /* extra offset: RIFF fmt extra data (extra size, frame size, GUID, etc), no fact samples/delay */ + cfg.channels = vgmstream->channels; - cfg.encoder_delay = read_u16le(extra_offset + 0x02, sf); + //cfg.encoder_delay = read_u16le(extra_offset + 0x02, sf) / 4; /* seemingly one subframe = 256 */ cfg.config_data = read_u32be(extra_offset + 0x1c, sf); vgmstream->codec_data = init_atrac9(&cfg); if (!vgmstream->codec_data) goto fail; vgmstream->coding_type = coding_ATRAC9; vgmstream->layout_type = layout_none; + + vgmstream->num_samples -= cfg.encoder_delay; + vgmstream->loop_start_sample -= cfg.encoder_delay; + vgmstream->loop_end_sample -= cfg.encoder_delay; break; } #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktsc.c b/Frameworks/vgmstream/vgmstream/src/meta/ktsc.c index d7cc3de54..cd6d2446a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ktsc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktsc.c @@ -14,8 +14,7 @@ VGMSTREAM* init_vgmstream_ktsc(STREAMFILE* sf) { /* checks */ if (!is_id32be(0x00, sf, "KTSC")) goto fail; - if (read_u32be(0x04, sf) != 0x01000001) /* version? */ - goto fail; + /* 0x04: version? (0x01000001: common, 0x01000004: FE Three Houses) */ /* .ktsl2asbin: common [Atelier Ryza (PC)] * .asbin: Warriors Orochi 4 (PC) (assumed) */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktsr.c b/Frameworks/vgmstream/vgmstream/src/meta/ktsr.c index 0ef57293d..94cd74059 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ktsr.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktsr.c @@ -1,18 +1,26 @@ #include "meta.h" #include "../coding/coding.h" #include "../layout/layout.h" +#include "../util/companion_files.h" +#include "ktsr_streamfile.h" typedef enum { NONE, MSADPCM, DSP, GCADPCM, ATRAC9, RIFF_ATRAC9, KOVS, KTSS, } ktsr_codec; #define MAX_CHANNELS 8 typedef struct { + uint32_t base_offset; + bool is_srsa; int total_subsongs; int target_subsong; ktsr_codec codec; + uint32_t audio_id; int platform; int format; + uint32_t sound_id; + uint32_t sound_flags; + uint32_t config_flags; int channels; int sample_rate; @@ -31,32 +39,73 @@ typedef struct { char name[255+1]; } ktsr_header; +static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa); static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf); static layered_layout_data* build_layered_atrac9(ktsr_header* ktsr, STREAMFILE *sf, uint32_t config_data); static VGMSTREAM* init_vgmstream_ktsr_sub(STREAMFILE* sf_b, ktsr_header* ktsr, VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf), const char* ext); /* KTSR - Koei Tecmo sound resource container */ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) { + + /* checks */ + if (!is_id32be(0x00, sf, "KTSR")) + return NULL; + /* others: see internal */ + + /* .ktsl2asbin: common [Atelier Ryza (PC/Switch), Nioh (PC)] + * .asbin: Warriors Orochi 4 (PC) */ + if (!check_extensions(sf, "ktsl2asbin,asbin")) + return NULL; + + return init_vgmstream_ktsr_internal(sf, false) ; +} + +/* ASRS - container of KTSR found in newer games */ +VGMSTREAM* init_vgmstream_asrs(STREAMFILE* sf) { + + /* checks */ + if (!is_id32be(0x00, sf, "ASRS")) + return NULL; + /* 0x04: null */ + /* 0x08: file size */ + /* 0x0c: null */ + + /* .srsa: header id (as generated by common tools, probably "(something)asbin") */ + if (!check_extensions(sf, "srsa")) + return NULL; + + /* mini-container of memory-KTSR (streams also have a "TSRS" for stream-KTSR). + * .srsa/srst usually have hashed filenames, so it isn't easy to match them, so this + * is mainly useful for .srsa with internal streams. */ + + return init_vgmstream_ktsr_internal(sf, true); +} + + +static VGMSTREAM* init_vgmstream_ktsr_internal(STREAMFILE* sf, bool is_srsa) { VGMSTREAM* vgmstream = NULL; STREAMFILE* sf_b = NULL; ktsr_header ktsr = {0}; int target_subsong = sf->stream_index; int separate_offsets = 0; + ktsr.is_srsa = is_srsa; + if (ktsr.is_srsa) { + ktsr.base_offset = 0x10; + } + /* checks */ - if (!is_id32be(0x00, sf, "KTSR")) - goto fail; - if (read_u32be(0x04, sf) != 0x777B481A) /* hash(?) id: 0x777B481A=as, 0x0294DDFC=st, 0xC638E69E=gc */ - goto fail; - /* .ktsl2asbin: common [Atelier Ryza (PC/Switch), Nioh (PC)] - * .asbin: Warriors Orochi 4 (PC) */ - if (!check_extensions(sf, "ktsl2asbin,asbin")) - goto fail; + if (!is_id32be(ktsr.base_offset + 0x00, sf, "KTSR")) + return NULL; + if (read_u32be(ktsr.base_offset + 0x04, sf) != 0x777B481A) /* hash id: 0x777B481A=as, 0x0294DDFC=st, 0xC638E69E=gc */ + return NULL; + /* KTSR can be a memory file (ktsl2asbin), streams (ktsl2stbin) and global config (ktsl2gcbin) * This accepts .ktsl2asbin with internal data or external streams as subsongs. - * Some info from KTSR.bt */ + * Hashes are meant to be read LE, but here are BE for easier comparison (they probably correspond + * to some text but are pre-hashed in exes). Some info from KTSR.bt */ if (target_subsong == 0) target_subsong = 1; ktsr.target_subsong = target_subsong; @@ -66,11 +115,22 @@ VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf) { /* open companion body */ if (ktsr.is_external) { - const char* companion_ext = check_extensions(sf, "asbin") ? "stbin" : "ktsl2stbin"; - sf_b = open_streamfile_by_ext(sf, companion_ext); + if (ktsr.is_srsa) { + /* try parsing TXTM if present, since .srsa+srst have hashed names and don't match unless renamed */ + sf_b = read_filemap_file(sf, 0); + } + if (!sf_b) { - vgm_logi("KTSR: companion file '*.%s' not found\n", companion_ext); - goto fail; + /* try (name).(ext), as seen in older games */ + const char* companion_ext = check_extensions(sf, "asbin") ? "stbin" : "ktsl2stbin"; + if (ktsr.is_srsa) + companion_ext = "srst"; + + sf_b = open_streamfile_by_ext(sf, companion_ext); + if (!sf_b) { + vgm_logi("KTSR: companion file '*.%s' not found\n", companion_ext); + goto fail; + } } } else { @@ -172,15 +232,20 @@ fail: return NULL; } -// TODO improve, unity with other metas that do similar stuff +// TODO improve, unify with other metas that do similar stuff static VGMSTREAM* init_vgmstream_ktsr_sub(STREAMFILE* sf_b, ktsr_header* ktsr, VGMSTREAM* (*init_vgmstream)(STREAMFILE* sf), const char* ext) { VGMSTREAM* sub_vgmstream = NULL; - STREAMFILE* temp_sf = setup_subfile_streamfile(sf_b, ktsr->stream_offsets[0], ktsr->stream_sizes[0], ext); + STREAMFILE* temp_sf = NULL; + + temp_sf = setup_ktsr_streamfile(sf_b, ktsr->is_external, ktsr->stream_offsets[0], ktsr->stream_sizes[0], ext); if (!temp_sf) return NULL; sub_vgmstream = init_vgmstream(temp_sf); close_streamfile(temp_sf); - if (!sub_vgmstream) return NULL; + if (!sub_vgmstream) { + VGM_LOG("ktsr: can't open subfile at %x (size %x)\n", ktsr->stream_offsets[0], ktsr->stream_sizes[0]); + return NULL; + } sub_vgmstream->stream_size = ktsr->stream_sizes[0]; sub_vgmstream->num_streams = ktsr->total_subsongs; @@ -253,6 +318,7 @@ static int parse_codec(ktsr_header* ktsr) { /* platform + format to codec, simplified until more codec combos are found */ switch(ktsr->platform) { case 0x01: /* PC */ + case 0x05: /* PC/Steam [Fate/Samurai Remnant (PC)] */ if (ktsr->is_external) { if (ktsr->format == 0x0005) ktsr->codec = KOVS; // Atelier Ryza (PC) @@ -284,11 +350,13 @@ static int parse_codec(ktsr_header* ktsr) { if (ktsr->is_external) { if (ktsr->format == 0x0005) ktsr->codec = KTSS; // [Ultra Kaiju Monster Rancher (Switch)] + else if (ktsr->format == 0x1000) + ktsr->codec = KTSS; // [Fire Emblem: Three Houses (Switch)-some DSP voices] else goto fail; } else if (ktsr->format == 0x0000) - ktsr->codec = DSP; // Fire Emblem: Three Houses (Switch) + ktsr->codec = DSP; // [Fire Emblem: Three Houses (Switch)] else goto fail; break; @@ -318,9 +386,11 @@ static int parse_ktsr_subfile(ktsr_header* ktsr, STREAMFILE* sf, uint32_t offset case 0x3DEA478D: /* external [Nioh (PC)] (smaller) */ case 0xDF92529F: /* external [Atelier Ryza (PC)] */ case 0x6422007C: /* external [Atelier Ryza (PC)] */ + case 0x793A1FD7: /* external [Stranger of Paradise (PS4)]-encrypted */ + case 0xA0F4FC6C: /* external [Stranger of Paradise (PS4)]-encrypted */ /* 08 subtype? (ex. 0x522B86B9) * 0c channels - * 10 ? (always 0x002706B8) + * 10 ? (always 0x002706B8 / 7864523D in SoP) * 14 external codec * 18 sample rate * 1c num samples @@ -420,16 +490,53 @@ fail: return 0; } +/* ktsr engine reads+decrypts in the same func based on passed flag tho (reversed from exe) + * Strings are usually ASCII but Shift-JIS is used in rare cases (0x0c3e2edf.srsa) */ +static void decrypt_string_ktsr(char* buf, size_t buf_size, uint32_t seed) { + for (int i = 0; i < buf_size - 1; i++) { + if (!buf[i]) /* just in case */ + break; + + seed = 0x343FD * seed + 0x269EC3; /* classic rand */ + buf[i] ^= (seed >> 16) & 0xFF; /* 3rd byte */ + if (!buf[i]) /* end null is also encrypted (but there are extra nulls after it anyway) */ + break; + } +} + +/* like read_string but allow any value since it can be encrypted */ +static size_t read_string_ktsr(char* buf, size_t buf_size, off_t offset, STREAMFILE* sf) { + int pos; + + for (pos = 0; pos < buf_size; pos++) { + uint8_t byte = read_u8(offset + pos, sf); + char c = (char)byte; + if (buf) buf[pos] = c; + if (c == '\0') + return pos; + if (pos+1 == buf_size) { + if (buf) buf[pos] = '\0'; + return buf_size; + } + } + + return 0; +} + static void build_name(ktsr_header* ktsr, STREAMFILE* sf) { char sound_name[255] = {0}; char config_name[255] = {0}; /* names can be different or same but usually config is better */ if (ktsr->sound_name_offset) { - read_string(sound_name, sizeof(sound_name), ktsr->sound_name_offset, sf); + read_string_ktsr(sound_name, sizeof(sound_name), ktsr->sound_name_offset, sf); + if (ktsr->sound_flags & 0x0008) + decrypt_string_ktsr(sound_name, sizeof(sound_name), ktsr->audio_id); } if (ktsr->config_name_offset) { - read_string(config_name, sizeof(config_name), ktsr->config_name_offset, sf); + read_string_ktsr(config_name, sizeof(config_name), ktsr->config_name_offset, sf); + if (ktsr->config_flags & 0x0200) + decrypt_string_ktsr(config_name, sizeof(config_name), ktsr->audio_id); } //if (longname[0] && shortname[0]) { @@ -445,22 +552,24 @@ static void build_name(ktsr_header* ktsr, STREAMFILE* sf) { } -static void parse_longname(ktsr_header* ktsr, STREAMFILE* sf, uint32_t target_id) { +static void parse_longname(ktsr_header* ktsr, STREAMFILE* sf) { /* more configs than sounds is possible so we need target_id first */ uint32_t offset, end, name_offset; uint32_t stream_id; - offset = 0x40; - end = get_streamfile_size(sf); + offset = 0x40 + ktsr->base_offset; + end = get_streamfile_size(sf) - ktsr->base_offset; while (offset < end) { uint32_t type = read_u32be(offset + 0x00, sf); /* hash-id? */ uint32_t size = read_u32le(offset + 0x04, sf); switch(type) { case 0xBD888C36: /* config */ - stream_id = read_u32be(offset + 0x08, sf); - if (stream_id != target_id) + stream_id = read_u32le(offset + 0x08, sf); + if (stream_id != ktsr->sound_id) break; + ktsr->config_flags = read_u32le(offset + 0x0c, sf); + name_offset = read_u32le(offset + 0x28, sf); if (name_offset > 0) ktsr->config_name_offset = offset + name_offset; @@ -476,14 +585,14 @@ static void parse_longname(ktsr_header* ktsr, STREAMFILE* sf, uint32_t target_id static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { uint32_t offset, end, header_offset, name_offset; - uint32_t stream_id = 0, stream_count; + uint32_t stream_count; /* 00: KTSR * 04: type * 08: version? * 0a: unknown (usually 00, 02/03 seen in Vita) * 0b: platform (01=PC, 03=Vita, 04=Switch) - * 0c: game id? + * 0c: audio id? (seen in multiple files/games and used as Ogg stream IDs) * 10: null * 14: null * 18: file size @@ -491,25 +600,28 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { * up to 40: reserved * until end: entries (totals not defined) */ - ktsr->platform = read_u8(0x0b,sf); + ktsr->platform = read_u8(ktsr->base_offset + 0x0b,sf); + ktsr->audio_id = read_u32le(ktsr->base_offset + 0x0c,sf); - if (read_u32le(0x18, sf) != read_u32le(0x1c, sf)) + if (read_u32le(ktsr->base_offset + 0x18, sf) != read_u32le(ktsr->base_offset + 0x1c, sf)) goto fail; - if (read_u32le(0x1c, sf) != get_streamfile_size(sf)) + if (read_u32le(ktsr->base_offset + 0x1c, sf) != get_streamfile_size(sf) - ktsr->base_offset) { + vgm_logi("KTSR: incorrect file size (bad rip?)\n"); goto fail; + } - offset = 0x40; - end = get_streamfile_size(sf); + offset = 0x40 + ktsr->base_offset; + end = get_streamfile_size(sf) - ktsr->base_offset; while (offset < end) { uint32_t type = read_u32be(offset + 0x00, sf); /* hash-id? */ uint32_t size = read_u32le(offset + 0x04, sf); /* parse chunk-like subfiles, usually N configs then N songs */ switch(type) { - case 0x6172DBA8: /* padding (empty) */ - case 0xBD888C36: /* config (floats, stream id, etc, may have extended name) */ + case 0x6172DBA8: /* ? (mostly empty) */ + case 0xBD888C36: /* cue? (floats, stream id, etc, may have extended name; can have sub-chunks)-appears N times */ case 0xC9C48EC1: /* unknown (has some string inside like "boss") */ - case 0xA9D23BF1: /* "state container", some kind of config/floats, witgh names like 'State_bgm01'..N */ + case 0xA9D23BF1: /* "state container", some kind of config/floats, with names like 'State_bgm01'..N */ case 0x836FBECA: /* unknown (~0x300, encrypted? table + data) */ break; @@ -517,9 +629,10 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { ktsr->total_subsongs++; /* sound table: - * 08: stream id (used in several places) - * 0c: unknown (low number but not version?) - * 0e: external flag + * 08: current/stream id (used in several places) + * 0c: flags (sounds only; other types are similar but different bits) + * 0x08: encrypted names (only used after ASRS was introduced?) + * 0x10000: external flag * 10: sub-streams? * 14: offset to header offset * 18: offset to name @@ -528,11 +641,10 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { * --: header * --: subheader (varies) */ - if (ktsr->total_subsongs == ktsr->target_subsong) { - stream_id = read_u32be(offset + 0x08,sf); - //ktsr->is_external = read_u16le(offset + 0x0e,sf); + ktsr->sound_id = read_u32le(offset + 0x08,sf); + ktsr->sound_flags = read_u32le(offset + 0x0c,sf); stream_count = read_u32le(offset + 0x10,sf); if (stream_count != 1) { VGM_LOG("ktsr: unknown stream count\n"); @@ -563,9 +675,18 @@ static int parse_ktsr(ktsr_header* ktsr, STREAMFILE* sf) { if (ktsr->target_subsong > ktsr->total_subsongs) goto fail; - parse_longname(ktsr, sf, stream_id); + parse_longname(ktsr, sf); build_name(ktsr, sf); + /* skip TSRS header (internals are pre-adjusted) */ + if (ktsr->is_external && ktsr->base_offset) { + for (int i = 0; i < ktsr->channels; i++) { + ktsr->stream_offsets[i] += ktsr->base_offset; + } + + ktsr->extra_offset += ktsr->base_offset; /* ? */ + } + return 1; fail: vgm_logi("KTSR: unknown variation (report)\n"); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ktsr_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/ktsr_streamfile.h new file mode 100644 index 000000000..b0cc027dd --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/ktsr_streamfile.h @@ -0,0 +1,152 @@ +#ifndef _KTSR_STREAMFILE_H_ +#define _KTSR_STREAMFILE_H_ + +#include "../streamfile.h" +#include "../util/log.h" +#include "../util/cipher_blowfish.h" + +/* decrypts blowfish in realtime (as done by games) */ +typedef struct { + uint8_t key[0x20]; + uint8_t block[0x08]; + blowfish_ctx* ctx; +} ktsr_io_data; + +static int ktsr_io_init(STREAMFILE* sf, ktsr_io_data* data) { + /* ktsr keys start with size then random bytes (usually 7), assumed max 0x20 */ + if (data->key[0] >= sizeof(data->key) - 1) + return -1; + + data->ctx = blowfish_init_ecb(data->key + 1, data->key[0]); + if (!data->ctx) + return -1; + return 0; +} + +static void ktsr_io_close(STREAMFILE* sf, ktsr_io_data* data) { + blowfish_free(data->ctx); +} + + +static int read_part(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t length, ktsr_io_data* data) { + + off_t offset_rem = offset % 0x08; + offset -= offset_rem; + + if (offset_rem == 0 && length >= 0x08) /* handled in main */ + return 0; + + /* read one full block, regardless of requested length */ + int bytes = read_streamfile(data->block, offset, 0x08, sf); + + /* useless since KTSR data is padded and blocks don't work otherwise but for determinability */ + if (bytes < 0x08) + memset(data->block + bytes, 0, 0x08 - bytes); + + blowfish_decrypt_ecb(data->ctx, data->block); + + int max_copy = bytes - offset_rem; + if (max_copy > length) + max_copy = length; + + memcpy(dest, data->block + offset_rem, max_copy); + + return max_copy; +} + +static int read_main(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t length, ktsr_io_data* data) { + int read = 0; + + off_t offset_rem = offset % 0x08; + size_t length_rem = length % 0x08; + length -= length_rem; + + if (offset_rem != 0 || length == 0) /* handled in part */ + return 0; + + int bytes = read_streamfile(dest, offset, length, sf); + + while (read < bytes) { + blowfish_decrypt_ecb(data->ctx, dest); + dest += 0x08; + read += 0x08; + } + + return bytes; +} + +/* blowfish is a 64-bit block cipher, so arbitrary reads will need to handle partial cases. ex + * - reading 0x00 to 0x20: direct decrypt (4 blocks of 0x08) + * - reading 0x03 to 0x07: decrypt 0x00 to 0x08 but copy 4 bytes at 0x03 + * - reading 0x03 to 0x22: handle as 0x00 to 0x08 (head, copy 5 at 0x3), 0x08 to 0x20 (body, direct), and 0x20 to 0x28 (tail, copy 2 at 0x0). */ +static size_t ktsr_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t length, ktsr_io_data* data) { + int bytes = 0; + + + /* head */ + if (length) { + int done = read_part(sf, dest, offset, length, data); + dest += done; + offset += done; + length -= done; + + bytes += done; + } + + /* body */ + if (length) { + int done = read_main(sf, dest, offset, length, data); + dest += done; + offset += done; + length -= done; + + bytes += done; + } + + /* tail */ + if (length) { + int done = read_part(sf, dest, offset, length, data); + dest += done; + offset += done; + length -= done; + + bytes += done; + } + + return bytes; +} + + + +/* Decrypts blowfish KTSR streams */ +static STREAMFILE* setup_ktsr_streamfile(STREAMFILE* sf, bool is_external, uint32_t subfile_offset, uint32_t subfile_size, const char* extension) { + STREAMFILE* new_sf = NULL; + ktsr_io_data io_data = {0}; + + if (is_external) { + uint32_t offset = 0x00; + if (is_id32be(0x00, sf, "TSRS")) + offset += 0x10; + if (!is_id32be(offset + 0x00, sf, "KTSR")) + goto fail; + + read_streamfile(io_data.key, offset + 0x20, sizeof(io_data.key), sf); + } + + /* setup subfile */ + new_sf = open_wrap_streamfile(sf); + + /* no apparent flag other than key at offset. Only data at subfile is encrypted, so this reader assumes it will be clamped */ + if (io_data.key[0] != 0) + new_sf = open_io_streamfile_ex_f(new_sf, &io_data, sizeof(ktsr_io_data), ktsr_io_read, NULL, ktsr_io_init, ktsr_io_close); + + new_sf = open_clamp_streamfile_f(new_sf, subfile_offset, subfile_size); + if (extension) + new_sf = open_fakename_streamfile_f(new_sf, NULL, extension); + + return new_sf; +fail: + return NULL; +} + +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index f11b389c8..ba6e6ce06 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -618,7 +618,6 @@ VGMSTREAM* init_vgmstream_opus_nop(STREAMFILE* sf); VGMSTREAM* init_vgmstream_opus_shinen(STREAMFILE* sf); VGMSTREAM* init_vgmstream_opus_nus3(STREAMFILE* sf); VGMSTREAM* init_vgmstream_opus_sps_n1(STREAMFILE* sf); -VGMSTREAM* init_vgmstream_opus_nxa(STREAMFILE* sf); VGMSTREAM* init_vgmstream_opus_opusx(STREAMFILE* sf); VGMSTREAM* init_vgmstream_opus_prototype(STREAMFILE* sf); VGMSTREAM* init_vgmstream_opus_opusnx(STREAMFILE* sf); @@ -758,6 +757,8 @@ VGMSTREAM * init_vgmstream_derf(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_utk(STREAMFILE *streamFile); +VGMSTREAM* init_vgmstream_nxa1(STREAMFILE* sf); + VGMSTREAM * init_vgmstream_adpcm_capcom(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_ue4opus(STREAMFILE *streamFile); @@ -818,8 +819,8 @@ VGMSTREAM * init_vgmstream_bwav(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_awb(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_awb_memory(STREAMFILE * streamFile, STREAMFILE *acbFile); -VGMSTREAM * init_vgmstream_acb(STREAMFILE * streamFile); -void load_acb_wave_name(STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid, int port, int is_memory); +VGMSTREAM* init_vgmstream_acb(STREAMFILE* sf); +void load_acb_wave_info(STREAMFILE *acbFile, VGMSTREAM* vgmstream, int waveid, int port, int is_memory, int load_loops); VGMSTREAM * init_vgmstream_rad(STREAMFILE * streamFile); @@ -845,6 +846,7 @@ VGMSTREAM * init_vgmstream_nub_xma(STREAMFILE *streamFile); VGMSTREAM * init_vgmstream_nub_dsp(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_nub_idsp(STREAMFILE * streamFile); VGMSTREAM * init_vgmstream_nub_is14(STREAMFILE * streamFile); +VGMSTREAM* init_vgmstream_nub_caf(STREAMFILE* sf); VGMSTREAM* init_vgmstream_xwv_valve(STREAMFILE* sf); @@ -870,6 +872,7 @@ VGMSTREAM * init_vgmstream_tgc(STREAMFILE *streamFile); VGMSTREAM* init_vgmstream_kwb(STREAMFILE* sf); VGMSTREAM* init_vgmstream_xws(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_snd_koei(STREAMFILE* sf); VGMSTREAM * init_vgmstream_lrmd(STREAMFILE* sf); @@ -883,6 +886,7 @@ VGMSTREAM* init_vgmstream_diva(STREAMFILE* sf); VGMSTREAM* init_vgmstream_imuse(STREAMFILE* sf); VGMSTREAM* init_vgmstream_ktsr(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_asrs(STREAMFILE* sf); VGMSTREAM* init_vgmstream_mups(STREAMFILE* sf); @@ -982,4 +986,6 @@ VGMSTREAM* init_vgmstream_squeaksample(STREAMFILE* sf); VGMSTREAM* init_vgmstream_snds(STREAMFILE* sf); +VGMSTREAM* init_vgmstream_nxof(STREAMFILE* sf); + #endif /*_META_H*/ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nub.c b/Frameworks/vgmstream/vgmstream/src/meta/nub.c index cec97abcd..a309c8cba 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nub.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nub.c @@ -30,7 +30,7 @@ VGMSTREAM* init_vgmstream_nub(STREAMFILE* sf) { goto fail; /* .nub: standard - * .nub2: rare [iDOLM@STER - Gravure For You (PS3)] */ + * .nub2: rare [iDOLM@STER: Gravure For You (PS3), Noby Noby Boy (iOS)] */ if (!check_extensions(sf, "nub,nub2")) goto fail; @@ -97,47 +97,52 @@ VGMSTREAM* init_vgmstream_nub(STREAMFILE* sf) { header_size = align_size_to_block(subheader_start + subheader_size, 0x10); switch(codec) { - case 0x00: /* (none) (xma1) */ + case 0x00: /* xma1 */ fake_ext = "xma"; init_vgmstream_function = init_vgmstream_nub_xma; break; - case 0x01: /* "wav\0" */ + case 0x01: fake_ext = "wav"; init_vgmstream_function = init_vgmstream_nub_wav; break; - case 0x02: /* "vag\0" */ + case 0x02: fake_ext = "vag"; init_vgmstream_function = init_vgmstream_nub_vag; break; - case 0x03: /* "at3\0" */ + case 0x03: fake_ext = "at3"; init_vgmstream_function = init_vgmstream_nub_at3; break; - case 0x04: /* "xma\0" (xma2 old) */ - case 0x08: /* "xma\0" (xma2 new) */ + case 0x04: /* xma2 old */ + case 0x08: /* xma2 new */ fake_ext = "xma"; init_vgmstream_function = init_vgmstream_nub_xma; break; - case 0x05: /* "dsp\0" */ + case 0x05: fake_ext = "dsp"; init_vgmstream_function = init_vgmstream_nub_dsp; break; - case 0x06: /* "idsp" */ + case 0x06: fake_ext = "idsp"; init_vgmstream_function = init_vgmstream_nub_idsp; break; - case 0x07: /* "is14" */ + case 0x07: fake_ext = "is14"; init_vgmstream_function = init_vgmstream_nub_is14; break; + case 0x09: + fake_ext = "caf"; + init_vgmstream_function = init_vgmstream_nub_caf; + break; + default: VGM_LOG("NUB: unknown codec %x\n", codec); goto fail; @@ -235,9 +240,10 @@ 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)] */ +/* .nub wav - from Namco NUB archives [Ridge Racer 7 (PS3), Noby Noby Boy (iOS)] */ VGMSTREAM* init_vgmstream_nub_wav(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; off_t start_offset; int loop_flag, channel_count, sample_rate; size_t data_size, loop_start, loop_length; @@ -246,10 +252,10 @@ VGMSTREAM* init_vgmstream_nub_wav(STREAMFILE* sf) { /* checks */ + if (!is_id32be(0x00,sf, "wav\0")) + return NULL; if (!check_extensions(sf, "wav,lwav")) - goto fail; - if (read_32bitBE(0x00,sf) != 0x77617600) /* "wav\0" "*/ - goto fail; + return NULL; if (guess_endian32(0x1c, sf)) { read_32bit = read_32bitBE; @@ -276,6 +282,21 @@ VGMSTREAM* init_vgmstream_nub_wav(STREAMFILE* sf) { start_offset = 0xD0; + /* seen in Noby Noby Boy (iOS), not sure about flags */ + if (is_id32be(start_offset, sf, "RIFF")) { + uint32_t subfile_offset = start_offset; + uint32_t subfile_size = read_32bitLE(subfile_offset + 0x04, sf) + 0x08; /* RIFF size */ + + temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, NULL); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_riff(temp_sf); + if (!vgmstream) goto fail; + + close_streamfile(temp_sf); + return vgmstream; + } + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channel_count,loop_flag); @@ -296,6 +317,7 @@ VGMSTREAM* init_vgmstream_nub_wav(STREAMFILE* sf) { return vgmstream; fail: + close_streamfile(temp_sf); close_vgmstream(vgmstream); return NULL; } @@ -310,10 +332,10 @@ VGMSTREAM* init_vgmstream_nub_vag(STREAMFILE* sf) { /* checks */ + if (!is_id32be(0x00,sf, "vag\0")) + return NULL; if ( !check_extensions(sf, "vag")) - goto fail; - if (read_32bitBE(0x00,sf) != 0x76616700) /* "vag\0" */ - goto fail; + return NULL; if (guess_endian32(0x1c, sf)) { read_32bit = read_32bitBE; @@ -362,15 +384,13 @@ fail: 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 (!is_id32be(0x00,sf, "at3\0")) + return NULL; if (!check_extensions(sf,"at3")) - goto fail; - if (read_32bitBE(0x00,sf) != 0x61743300) /* "at3\0" */ - goto fail; + return NULL; /* info header */ /* 0x20: loop start (in samples) */ @@ -381,8 +401,8 @@ VGMSTREAM* init_vgmstream_nub_at3(STREAMFILE* sf) { /* format header: mini fmt (WAVEFORMATEX) + fact chunks LE (clone of RIFF's) */ /* 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, sf) + 0x08; /* RIFF size */ + uint32_t subfile_offset = 0x100; + uint32_t subfile_size = read_32bitLE(subfile_offset + 0x04, sf) + 0x08; /* RIFF size */ temp_sf = setup_subfile_streamfile(sf, subfile_offset,subfile_size, NULL); if (!temp_sf) goto fail; @@ -410,9 +430,9 @@ VGMSTREAM* init_vgmstream_nub_xma(STREAMFILE* sf) { /* checks */ if (!check_extensions(sf,"xma")) - goto fail; + return NULL; - if (read_32bitBE(0x00,sf) == 0x786D6100) { /* "xma\0" */ + if (is_id32be(0x00,sf, "xma\0")) { /* nub v2.1 */ nus_codec = read_32bitBE(0x0C,sf); data_size = read_32bitBE(0x14,sf); @@ -435,7 +455,7 @@ VGMSTREAM* init_vgmstream_nub_xma(STREAMFILE* sf) { chunk_size = header_size; } else { - goto fail; + return NULL; } start_offset = align_size_to_block(chunk_offset + header_size, 0x10); @@ -520,10 +540,10 @@ VGMSTREAM* init_vgmstream_nub_dsp(STREAMFILE* sf) { /* checks */ + if (!is_id32be(0x00,sf, "dsp\0")) + return NULL; if (!check_extensions(sf,"dsp")) - goto fail; - if (read_32bitBE(0x00,sf) != 0x64737000) /* "dsp\0" */ - goto fail; + return NULL; /* paste header+data together and pass to meta, which has loop info too */ header_offset = 0xBC; @@ -554,10 +574,10 @@ VGMSTREAM* init_vgmstream_nub_idsp(STREAMFILE* sf) { /* checks */ + if (!is_id32be(0x00,sf, "idsp")) + return NULL; if (!check_extensions(sf,"idsp")) - goto fail; - if (read_32bitBE(0x00,sf) != 0x69647370) /* "idsp" */ - goto fail; + return NULL; /* info header */ /* 0x20: loop start (in samples) */ @@ -595,10 +615,10 @@ VGMSTREAM* init_vgmstream_nub_is14(STREAMFILE* sf) { /* checks */ + if (!is_id32be(0x00,sf, "is14")) + return NULL; if (!check_extensions(sf,"is14")) - goto fail; - if (read_32bitBE(0x00,sf) != 0x69733134) /* "is14" */ - goto fail; + return NULL; if (guess_endian32(0x1c, sf)) { read_32bit = read_32bitBE; @@ -632,3 +652,41 @@ fail: close_vgmstream(vgmstream); return NULL; } + +/* .nub is14 - from Namco NUB archives [Noby Noby Boy (iOS)] */ +VGMSTREAM* init_vgmstream_nub_caf(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + STREAMFILE* temp_sf = NULL; + + + /* checks */ + if (!is_id32be(0x00,sf, "caf\0")) + return NULL; + if (!check_extensions(sf,"caf")) + return NULL; + + /* info header */ + /* 0x20: loop start (in samples) */ + /* 0x24: loop length (in samples) */ + /* 0x28: loop flag */ + /* 0x2c: null */ + + /* format header: simplified caff with some chunks */ + /* we can just ignore and use caff at data start since it has the same info */ + + uint32_t subfile_offset = 0x110; + uint32_t subfile_size = read_u32le(0x14, sf); /* padded but not fully validated */ + + temp_sf = setup_subfile_streamfile(sf, subfile_offset, subfile_size, NULL); + if (!temp_sf) goto fail; + + vgmstream = init_vgmstream_apple_caff(temp_sf); + if (!vgmstream) goto fail; + + close_streamfile(temp_sf); + return vgmstream; +fail: + close_streamfile(temp_sf); + close_vgmstream(vgmstream); + return NULL; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nxa.c b/Frameworks/vgmstream/vgmstream/src/meta/nxa1.c similarity index 92% rename from Frameworks/vgmstream/vgmstream/src/meta/nxa.c rename to Frameworks/vgmstream/vgmstream/src/meta/nxa1.c index d1f1ba201..3ea0c9be1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nxa.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nxa1.c @@ -2,8 +2,8 @@ #include "../coding/coding.h" -/* Entergram NXA Opus [Higurashi no Naku Koro ni Hou (Switch), Gensou Rougoku no Kaleidoscope (Switch)] */ -VGMSTREAM* init_vgmstream_opus_nxa(STREAMFILE* sf) { +/* Entergram NXA1 Opus [Higurashi no Naku Koro ni Hou (Switch), Gensou Rougoku no Kaleidoscope (Switch)] */ +VGMSTREAM* init_vgmstream_nxa1(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; off_t start_offset; int loop_flag, channels, type, sample_rate; @@ -11,10 +11,10 @@ VGMSTREAM* init_vgmstream_opus_nxa(STREAMFILE* sf) { size_t data_size, frame_size; /* checks */ - if (!check_extensions(sf, "nxa")) - goto fail; if (!is_id32be(0x00, sf, "NXA1")) goto fail; + if (!check_extensions(sf, "nxa")) + goto fail; start_offset = 0x30; type = read_u32le(0x04, sf); @@ -36,7 +36,7 @@ VGMSTREAM* init_vgmstream_opus_nxa(STREAMFILE* sf) { vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; - vgmstream->meta_type = meta_NXA; + vgmstream->meta_type = meta_NXA1; vgmstream->sample_rate = sample_rate; vgmstream->num_samples = num_samples; vgmstream->loop_start_sample = loop_start; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nxof.c b/Frameworks/vgmstream/vgmstream/src/meta/nxof.c new file mode 100644 index 000000000..73409b346 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/nxof.c @@ -0,0 +1,53 @@ +#include "meta.h" +#include "../coding/coding.h" + +/* Nihon Falcom FDK NXOpus [Ys X -NORDICS- (Switch)] */ +VGMSTREAM* init_vgmstream_nxof(STREAMFILE* sf) { + VGMSTREAM* vgmstream = NULL; + off_t start_offset; + int loop_flag, channels, sample_rate; + size_t data_size, skip = 0; + int32_t num_samples, loop_start, loop_end; + + /* checks */ + if (!is_id32le(0x00, sf, "nxof")) + goto fail; + if (!check_extensions(sf,"nxopus")) + goto fail; + + channels = read_u8(0x05, sf); + sample_rate = read_u32le(0x08, sf); + start_offset = read_u32le(0x18, sf); + data_size = read_u32le(0x1C, sf); + num_samples = read_u32le(0x20, sf); + loop_start = read_u32le(0x30, sf); + loop_end = read_u32le(0x34, sf); + + loop_flag = (loop_end > 0); + + vgmstream = allocate_vgmstream(channels, loop_flag); + if (!vgmstream) goto fail; + + vgmstream->meta_type = meta_NXOF; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = num_samples; + vgmstream->loop_start_sample = loop_start; + vgmstream->loop_end_sample = loop_end; + +#ifdef VGM_USE_FFMPEG + skip = switch_opus_get_encoder_delay(start_offset, sf); + 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; +#else + goto fail; +#endif + + 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/psb.c b/Frameworks/vgmstream/vgmstream/src/meta/psb.c index f674627f8..6ced033f1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/psb.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/psb.c @@ -4,7 +4,7 @@ #include "../layout/layout.h" -#define PSB_MAX_LAYERS 2 +#define PSB_MAX_LAYERS 6 /* MGS Master Collection Vo.1 (Switch) */ typedef enum { PCM, RIFF_AT3, XMA2, MSADPCM, XWMA, DSP, OPUSNX, RIFF_AT9, VAG } psb_codec_t; typedef struct { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/riff.c b/Frameworks/vgmstream/vgmstream/src/meta/riff.c index 1a91faea5..c3d844cb1 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/riff.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/riff.c @@ -169,6 +169,10 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk case 0x0002: /* MSADPCM */ if (fmt->bps == 4) { + /* ADPCMWAVEFORMAT extra data: + * - samples per frame (16b) + * - num coefs (16b), always 7 + * - N x2 coefs (configurable but in practice fixed) */ fmt->coding_type = coding_MSADPCM; if (!msadpcm_check_coefs(sf, fmt->offset + 0x08 + 0x14)) goto fail; @@ -180,7 +184,7 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk goto fail; } break; - case 0x003: /* floating point PCM */ + case 0x0003: /* floating point PCM */ if (fmt->bps == 32) { fmt->coding_type = coding_PCMFLOAT; } else { @@ -190,6 +194,8 @@ static int read_fmt(int big_endian, STREAMFILE* sf, off_t offset, riff_fmt_chunk break; case 0x0011: /* MS-IMA ADPCM [Layton Brothers: Mystery Room (iOS/Android)] */ + /* IMAADPCMWAVEFORMAT extra data: + * - samples per frame (16b) */ if (fmt->bps != 4) goto fail; fmt->coding_type = coding_MS_IMA; break; @@ -391,8 +397,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) { * .xms: Ty the Tasmanian Tiger (Xbox) * .mus: Burnout Legends/Dominator (PSP) * .dat/ldat: RollerCoaster Tycoon 1/2 (PC) + * .wma/lwma: SRS: Street Racing Syndicate (Xbox), Fast and the Furious (Xbox) */ - if (!check_extensions(sf, "wav,lwav,xwav,mwv,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,ckd,saf,ima,nsa,pcm,xvag,ogg,logg,p1d,xms,mus,dat,ldat")) { + if (!check_extensions(sf, "wav,lwav,xwav,mwv,da,dax,cd,med,snd,adx,adp,xss,xsew,adpcm,adw,wd,,sbv,wvx,str,at3,rws,aud,at9,ckd,saf,ima,nsa,pcm,xvag,ogg,logg,p1d,xms,mus,dat,ldat,wma,lwma")) { goto fail; } @@ -409,6 +416,9 @@ VGMSTREAM* init_vgmstream_riff(STREAMFILE* sf) { else if (codec == 0x0069 && riff_size + 0x04 == file_size) riff_size -= 0x04; /* [Halo 2 (PC)] (possibly bad extractor? 'Gravemind Tool') */ + else if (codec == 0x0069 && riff_size + 0x10 == file_size) + riff_size += 0x08; /* [Fast and the Furious (Xbox)] ("HASH" chunk + 4 byte hash) */ + else if (codec == 0x0000 && riff_size + 0x04 == file_size) riff_size -= 0x04; /* [Headhunter (DC), Bomber hehhe (DC)] */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rstm_rockstar.c b/Frameworks/vgmstream/vgmstream/src/meta/rstm_rockstar.c index abb77086d..3ffc2aa7f 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rstm_rockstar.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rstm_rockstar.c @@ -4,8 +4,9 @@ /* RSTM - from Rockstar games [Midnight Club 3, Bully - Canis Canim Edit (PS2)] */ VGMSTREAM* init_vgmstream_rstm_rockstar(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; - uint32_t start_offset; - int channels, loop_flag; + off_t stream_offset, loop_start, loop_end; + size_t stream_size; + int sample_rate, channels, loop_flag; /* checks */ @@ -14,30 +15,38 @@ VGMSTREAM* init_vgmstream_rstm_rockstar(STREAMFILE* sf) { /* .rsm: in filelist * .rstm: header id */ - if (!check_extensions(sf,"rsm,rstm")) + if (!check_extensions(sf, "rsm,rstm")) return NULL; - loop_flag = (read_s32le(0x24,sf) > 0); - channels = read_s32le(0x0C,sf); - start_offset = 0x800; + sample_rate = read_s32le(0x08, sf); + channels = read_s32le(0x0C, sf); + /* 0x10-0x18 - empty padding(?) */ + stream_size = read_s32le(0x18, sf); + loop_start = read_s32le(0x1C, sf); + loop_end = read_s32le(0x20, sf); + /* other loop start/ends after here? (uncommon) */ + stream_offset = 0x800; + + //loop_flag = (read_s32le(0x24,sf) > 0); + loop_flag = loop_end != stream_size; /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channels,loop_flag); + vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; vgmstream->meta_type = meta_RSTM_ROCKSTAR; - vgmstream->sample_rate = read_s32le(0x08,sf); - vgmstream->num_samples = ps_bytes_to_samples(read_u32le(0x20,sf),channels); - vgmstream->loop_start_sample = ps_bytes_to_samples(read_u32le(0x24,sf),channels); - vgmstream->loop_end_sample = vgmstream->num_samples; + vgmstream->sample_rate = sample_rate; + vgmstream->num_samples = ps_bytes_to_samples(stream_size, channels); + vgmstream->loop_start_sample = ps_bytes_to_samples(loop_start, channels); + vgmstream->loop_end_sample = ps_bytes_to_samples(loop_end, channels); vgmstream->coding_type = coding_PSX; vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = 0x10; /* open the file for reading */ - if ( !vgmstream_open_stream(vgmstream, sf, start_offset) ) + if ( !vgmstream_open_stream(vgmstream, sf, stream_offset) ) goto fail; return vgmstream; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c b/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c index 0637cac61..35a8f41ce 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sps_n1.c @@ -74,27 +74,30 @@ fail: return NULL; } +#define SEGMENT_MAX 3 + /* Nippon Ichi SPS wrapper (segmented) [Penny-Punching Princess (Switch), Disgaea 4 Complete (PC)] */ VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; - off_t segment_offset; size_t data_size, max_size; int loop_flag, type, sample_rate; - int i, segment; init_vgmstream_t init_vgmstream = NULL; const char* extension; segmented_layout_data* data = NULL; - int segment_count, loop_start_segment, loop_end_segment; + int loop_start_segment, loop_end_segment; /* checks */ - /* .at9: Penny-Punching Princess (Switch) + type = read_u32le(0x00,sf); + if (type > 10) + return NULL; + + /* .at9: Penny-Punching Princess (Switch), Labyrinth of Galleria (PC) * .nlsd: Disgaea 4 Complete (PC) */ if (!check_extensions(sf, "at9,nlsd")) - goto fail; + return NULL; - type = read_u32le(0x00,sf); data_size = read_u32le(0x04,sf); sample_rate = read_u16le(0x08,sf); /* 0x0a: flag? (stereo?) */ @@ -113,23 +116,58 @@ VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) { break; default: - goto fail; + return NULL; } - segment_offset = 0x1c; - if (data_size + segment_offset != get_streamfile_size(sf)) - goto fail; - /* segmented using 3 files (intro/loop/outro). non-segmented wrapper is the same * but with loop samples instead of sub-sizes */ + + uint32_t segment_offsets[SEGMENT_MAX]; + uint32_t segment_sizes[SEGMENT_MAX]; + uint32_t segment_start, offset; + int segment_count, segment; + + if (data_size + 0x1c == get_streamfile_size(sf)) { + /* common */ + segment_start = 0x1c; + offset = segment_start; + for (int i = 0; i < SEGMENT_MAX; i++) { + uint32_t segment_size = read_u32le(0x10 + 0x04*i,sf); + + segment_sizes[i] = segment_size; + segment_offsets[i] = offset; + offset += segment_sizes[i]; + } + } + else if (data_size + 0x18 == get_streamfile_size(sf)) { + /* Labyrinth of Galleria (PC) */ + segment_start = 0x18; + offset = segment_start; + for (int i = 0; i < SEGMENT_MAX; i++) { + uint32_t next_offset; + if (i >= 2) { + next_offset = get_streamfile_size(sf) - segment_start; + } + else { + next_offset = read_u32le(0x10 + 0x04*i,sf); /* only 2 (not sure if it can be 0) */ + } + + segment_sizes[i] = next_offset - offset + segment_start; + segment_offsets[i] = offset; + offset += segment_sizes[i]; + } + } + else { + goto fail; + } + max_size = 0; segment_count = 0; - for (i = 0; i < 3; i++) { - size_t segment_size = read_u32le(0x10 + 0x04*i,sf); - max_size += segment_size; - /* may only set 1 segment (Disgaea4's bgm_185) */ - if (segment_size) + for (int i = 0; i < SEGMENT_MAX; i++) { + /* may only set 1 segment, with empty intro/outro (Disgaea4's bgm_185) */ + if (segment_sizes[i]) segment_count++; + max_size += segment_sizes[i]; } if (data_size != max_size) goto fail; @@ -144,25 +182,21 @@ VGMSTREAM* init_vgmstream_sps_n1_segmented(STREAMFILE* sf) { /* open each segment subfile */ segment = 0; - for (i = 0; i < 3; i++) { - STREAMFILE* temp_sf; - size_t segment_size = read_u32le(0x10 + 0x04*i,sf); - - if (!segment_size) + for (int i = 0; i < SEGMENT_MAX; i++) { + if (!segment_sizes[i]) continue; - temp_sf = setup_subfile_streamfile(sf, segment_offset,segment_size, extension); + STREAMFILE* temp_sf = setup_subfile_streamfile(sf, segment_offsets[i],segment_sizes[i], extension); if (!temp_sf) goto fail; data->segments[segment] = init_vgmstream(temp_sf); close_streamfile(temp_sf); if (!data->segments[segment]) goto fail; - segment_offset += segment_size; segment++; if (type == 9) { - //todo there are some trailing samples that must be removed for smooth loops, start skip seems ok + //TODO there are some trailing samples that must be removed for smooth loops, start skip seems ok //not correct for all files, no idea how to calculate data->segments[segment]->num_samples -= 374; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sthd.c b/Frameworks/vgmstream/vgmstream/src/meta/sthd.c index e7be64c16..86ab12be9 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sthd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sthd.c @@ -16,7 +16,6 @@ VGMSTREAM * init_vgmstream_sthd(STREAMFILE *sf) { goto fail; /* first block has special values */ if (read_u16le(0x04,sf) != 0x0800 || - read_u32le(0x0c,sf) != 0x0001 || read_u32le(0x14,sf) != 0x0000) goto fail; diff --git a/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c b/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c index 3fa77f3eb..c020b0920 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/str_wav.c @@ -1,8 +1,10 @@ #include "meta.h" #include "../coding/coding.h" +#include "../util/layout_utils.h" +#include "str_wav_streamfile.h" -typedef enum { PSX, DSP, XBOX, WMA, IMA, XMA2 } strwav_codec; +typedef enum { PSX, PSX_chunked, DSP, XBOX, WMA, IMA, XMA2, MPEG } strwav_codec; typedef struct { int tracks; int channels; @@ -103,6 +105,29 @@ VGMSTREAM* init_vgmstream_str_wav(STREAMFILE* sf) { vgmstream->interleave_block_size = strwav.interleave; break; + case PSX_chunked: { /* hack */ + //tracks are stereo blocks of size 0x20000 * tracks, containing 4 interleaves of 0x8000: + // | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | ... + + vgmstream->coding_type = coding_PSX; + vgmstream->interleave_block_size = strwav.interleave; + for (int i = 0; i < strwav.tracks; i++) { + uint32_t chunk_size = 0x20000; + int layer_channels = 2; + + STREAMFILE* temp_sf = setup_str_wav_streamfile(sf, 0x00, strwav.tracks, i, chunk_size); + if (!temp_sf) goto fail; + + bool res = layered_add_sf(vgmstream, strwav.tracks, layer_channels, temp_sf); + close_streamfile(temp_sf); + if (!res) + goto fail; + } + if (!layered_add_done(vgmstream)) + goto fail; + break; + } + case DSP: vgmstream->coding_type = coding_NGC_DSP; vgmstream->layout_type = layout_interleave; @@ -173,6 +198,26 @@ VGMSTREAM* init_vgmstream_str_wav(STREAMFILE* sf) { } #endif +#ifdef VGM_USE_MPEG + case MPEG: { + /* regular MP3 starting with ID2, stereo tracks xN (bgm + vocals) but assuming last (or only one) could be mono */ + int layers = (strwav.channels + 1) / 2; + + for (int i = 0; i < layers; i++) { + uint32_t size = strwav.interleave; + uint32_t offset = i * size; + const char* ext = "mp3"; + int layer_channels = ((layers % 2) && i + 1 == layers) ? 1 : 2; + + layered_add_subfile(vgmstream, layers, layer_channels, sf, offset, size, ext, init_vgmstream_mpeg); + } + + if (!layered_add_done(vgmstream)) + goto fail; + break; + } +#endif + default: goto fail; } @@ -286,7 +331,7 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa /* 0x10c: header size */ strwav->codec = IMA; - strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000; + strwav->interleave = strwav->tracks > 1 ? 0x10000 : 0x10000; ;VGM_LOG("STR+WAV: header TAZd (PC)\n"); return 1; } @@ -435,8 +480,8 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa strwav->codec = PSX; strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x8000; - //todo: tracks are stereo blocks of size 0x20000*tracks, containing 4 interleaves of 0x8000: - // | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | 1 2 1 2 | 3 4 3 4 | 5 6 5 6 | ... + if (strwav->tracks > 1) /* hack */ + strwav->codec = PSX_chunked; ;VGM_LOG("STR+WAV: header ZPb (PS2)\n"); return 1; } @@ -498,6 +543,32 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa return 1; } + /* Taz Wanted (beta) (PC)[2002] */ + if ( read_u32be(0x04,sf_h) == 0x00000900 && + read_u32le(0x0c,sf_h) != header_size && + read_u32le(0x24,sf_h) != 0 && + read_u32le(0xd4,sf_h) != 0 && + read_u32le(0xdc,sf_h) == header_size + ) { + /* 0x08: null */ + /* 0x0c: hashname */ + strwav->num_samples = read_s32le(0x20,sf_h); + strwav->sample_rate = read_s32le(0x24,sf_h); + /* 0x28: 16 bps */ + strwav->flags = read_u32le(0x2c,sf_h); + strwav->loop_start = read_s32le(0x38,sf_h); + /* 0x54: number of chunks */ + strwav->tracks = read_s32le(0xd4,sf_h); + /* 0xdc: header size */ + + strwav->loop_end = strwav->num_samples; + + strwav->codec = IMA; + strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000; + ;VGM_LOG("STR+WAV: header TAZb (PC)\n"); + return 1; + } + /* Taz Wanted (PC)[2002] */ /* Zapper: One Wicked Cricket! Beta (Xbox)[2002] */ if ( read_u32be(0x04,sf_h) == 0x00000900 && @@ -669,7 +740,7 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa /* 0x4c: ? (some low number) */ strwav->tracks = read_u8 (0x4e,sf_h); /* 0x4f: 16 bps */ - /* 0x54: channels per each track? (ex. 2 stereo track: 0x02,0x02) */ + /* 0x54: channels per each track (ex. 2 stereo track: 0x02,0x02) */ /* 0x64: channels */ /* 0x70+: tables */ @@ -712,6 +783,7 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa /* Tak and the Guardians of Gross (Wii)[2008] */ /* The House of the Dead: Overkill (Wii)[2009] (not Blitz but still the same format) */ /* All Star Karate (Wii)[2010] */ + /* Karaoke Revolution (Wii)[2010] */ if ((read_u32be(0x04,sf_h) == 0x00000800 || read_u32be(0x04,sf_h) == 0x00000700) && /* rare? */ read_u32be(0x08,sf_h) != 0x00000000 && @@ -730,11 +802,12 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa strwav->codec = DSP; strwav->coefs_table = 0x7c; strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; - ;VGM_LOG("STR+WAV: header TKGG/HOTDO/ASK (Wii)\n"); + ;VGM_LOG("STR+WAV: header TKGG/HOTDO/ASK/KR (Wii)\n"); return 1; } /* The House of the Dead: Overkill (PS3)[2009] (not Blitz but still the same format) */ + /* Karaoke Revolution (PS3)[2010] */ if ((read_u32be(0x04,sf_h) == 0x00000800 || read_u32be(0x04,sf_h) == 0x00000700) && /* rare? */ read_u32be(0x08,sf_h) != 0x00000000 && @@ -747,10 +820,30 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa strwav->sample_rate = read_s32be(0x38,sf_h); strwav->flags = read_u32be(0x3c,sf_h); - strwav->channels = read_s32be(0x70,sf_h); /* tracks of 1ch */ - strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; + strwav->channels = read_s32be(0x70,sf_h); + + /* other possibly-useful flags (see Karaoke Wii too): + * - 0x4b: number of tracks + * - 0x60: channels per track, ex. 020202 = 3 tracks of 2ch (max 0x08 = 8) + * - 0xa0: sizes per track (max 0x20 = 8) + * - 0xc0: samples per track (max 0x20 = 8) + * - rest: info/seek table? */ + if (read_s32be(0x78,sf_h) != 0) { /* KRev */ + + strwav->tracks = strwav->channels / 2; + strwav->num_samples = strwav->loop_end; /* num_samples here seems to be data size */ + strwav->interleave = read_s32be(0xA0,sf_h); /* one size per file, but CBR = same for all */ + //C0: stream samples (same as num_samples) + + strwav->codec = MPEG; /* full CBR MP3 one after other */ + } + else { /* HOTD */ + strwav->channels = read_s32be(0x70,sf_h); /* tracks of 1ch */ + strwav->interleave = strwav->channels > 4 ? 0x4000 : 0x8000; + + strwav->codec = PSX; + } - strwav->codec = PSX; ;VGM_LOG("STR+WAV: header HOTDO (PS3)\n"); return 1; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/str_wav_streamfile.h b/Frameworks/vgmstream/vgmstream/src/meta/str_wav_streamfile.h new file mode 100644 index 000000000..f05b103e6 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/meta/str_wav_streamfile.h @@ -0,0 +1,21 @@ +#ifndef _STR_WAV_STREAMFILE_H_ +#define _STR_WAV_STREAMFILE_H_ +#include "deblock_streamfile.h" + +/* Deblocks streams */ +static STREAMFILE* setup_str_wav_streamfile(STREAMFILE* sf, off_t stream_start, int stream_count, int stream_number, size_t interleave) { + STREAMFILE *new_sf = NULL; + deblock_config_t cfg = {0}; + + cfg.stream_start = stream_start; + cfg.chunk_size = interleave; + cfg.step_start = stream_number; + cfg.step_count = stream_count; + + /* setup sf */ + new_sf = open_wrap_streamfile(sf); + new_sf = open_io_deblock_streamfile_f(new_sf, &cfg); + return new_sf; +} + +#endif \ No newline at end of file diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index c7b3052c3..3e0016044 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -841,7 +841,7 @@ static void set_body_chunk(txth_header* txth) { if (!txth->sf_body) return; - /* treat chunks as subsongs */ + /* treat chunks as subsongs (less subsongs than chunks could be allowed to ignore some chunks but it's kinda odd) */ if (txth->subsong_count > 1 && txth->subsong_count == txth->chunk_count) txth->chunk_number = txth->target_subsong; if (txth->chunk_number == 0) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c index e05370e20..5d5ab4dd5 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/ubi_bao.c @@ -1012,7 +1012,7 @@ static int parse_values(ubi_bao_header* bao) { } /* set codec */ - if (bao->stream_type > 0x10) { + if (bao->stream_type >= 0x10) { VGM_LOG("UBI BAO: unknown stream_type at %x\n", (uint32_t)bao->header_offset); goto fail; goto fail; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c index 6c78aeec8..b92582078 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/wwise.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/wwise.c @@ -575,6 +575,7 @@ VGMSTREAM* init_vgmstream_wwise_bnk(STREAMFILE* sf, int* p_prefetch) { switch(ww.channel_layout) { case mapping_7POINT1_surround: cfg.coupled_count = 3; break; /* 2ch+2ch+2ch+1ch+1ch, 5 streams */ case mapping_5POINT1_surround: /* 2ch+2ch+1ch+1ch, 4 streams */ + case mapping_5POINT0_surround: /* 2ch+2ch+1ch, 3 streams [Bayonetta 3 (Switch)] */ case mapping_QUAD_side: cfg.coupled_count = 2; break; /* 2ch+2ch, 2 streams */ case mapping_2POINT1_xiph: /* 2ch+1ch, 2 streams */ case mapping_STEREO: cfg.coupled_count = 1; break; /* 2ch, 1 stream */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xa_xa30.c b/Frameworks/vgmstream/vgmstream/src/meta/xa_xa30.c index 9b49c0460..8c24b8dd6 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xa_xa30.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/xa_xa30.c @@ -5,7 +5,7 @@ VGMSTREAM * init_vgmstream_xa_xa30(STREAMFILE *streamFile) { VGMSTREAM * vgmstream = NULL; off_t start_offset; - int loop_flag, channel_count, codec, interleave_block_size; + int loop_flag, channel_count, codec, interleave_block_size; size_t stream_size; int total_subsongs, target_subsong = streamFile->stream_index; @@ -31,7 +31,7 @@ VGMSTREAM * init_vgmstream_xa_xa30(STREAMFILE *streamFile) { codec = read_32bitLE(0x0c,streamFile); start_offset = read_32bitLE(0x10 + 0x04*(target_subsong-1),streamFile); stream_size = read_32bitLE(0x18 + 0x04*(target_subsong-1),streamFile); - interleave_block_size = read_32bitLE(0x24, streamFile); + interleave_block_size = read_32bitLE(0x24, streamFile); /* build the VGMSTREAM */ @@ -48,14 +48,14 @@ VGMSTREAM * init_vgmstream_xa_xa30(STREAMFILE *streamFile) { case 0x00: /* PCM (rare, seen in Driver 3) */ vgmstream->coding_type = coding_PCM16LE; vgmstream->layout_type = layout_interleave; - vgmstream->interleave_block_size = interleave_block_size / 2; + vgmstream->interleave_block_size = interleave_block_size / 2; vgmstream->num_samples = pcm_bytes_to_samples(stream_size, channel_count, 16); break; case 0x01: /* MS-IMA variation */ vgmstream->coding_type = coding_REF_IMA; vgmstream->layout_type = layout_none; - vgmstream->interleave_block_size = interleave_block_size; + vgmstream->interleave_block_size = interleave_block_size; vgmstream->num_samples = ms_ima_bytes_to_samples(stream_size, vgmstream->interleave_block_size, channel_count); break; diff --git a/Frameworks/vgmstream/vgmstream/src/util/bitstream_lsb.h b/Frameworks/vgmstream/vgmstream/src/util/bitstream_lsb.h index 275cfba13..c6404a5f8 100644 --- a/Frameworks/vgmstream/vgmstream/src/util/bitstream_lsb.h +++ b/Frameworks/vgmstream/vgmstream/src/util/bitstream_lsb.h @@ -1,10 +1,11 @@ #ifndef _BITSTREAM_LSB_H #define _BITSTREAM_LSB_H -#include "../streamtypes.h" +#include /* Simple bitreader for Vorbis' bit style, in 'least significant byte' (LSB) format. - * Example: 0x12345678 is read as 12,34,56,78 (continuous). + * Example: with 0x1234 = 00010010 00110100, reading 5b + 6b = 10010 100000 + * (first lower 5b, then next upper 3b and next lower 3b = 6b) * Kept in .h since it's slightly faster (compiler can optimize statics better using default compile flags). */ @@ -23,7 +24,7 @@ static inline void bl_setup(bitstream_t* b, uint8_t* buf, size_t bufsize) { /* same as (1 << bits) - 1, but that seems to trigger some nasty UB when bits = 32 * (though in theory (1 << 32) = 0, 0 - 1 = UINT_MAX, but gives 0 compiling in some cases, but not always) */ -static const uint32_t MASK_TABLE[33] = { +static const uint32_t MASK_TABLE_LSB[33] = { 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff, @@ -40,7 +41,7 @@ static inline int bl_get(bitstream_t* ib, uint32_t bits, uint32_t* value) { pos = ib->b_off / 8; /* byte offset */ shift = ib->b_off % 8; /* bit sub-offset */ - mask = MASK_TABLE[bits]; /* to remove upper in highest byte */ + mask = MASK_TABLE_LSB[bits]; /* to remove upper in highest byte */ val = ib->buf[pos+0] >> shift; if (bits + shift > 8) { diff --git a/Frameworks/vgmstream/vgmstream/src/util/bitstream_msb.h b/Frameworks/vgmstream/vgmstream/src/util/bitstream_msb.h index 42bd9b295..e93166b9a 100644 --- a/Frameworks/vgmstream/vgmstream/src/util/bitstream_msb.h +++ b/Frameworks/vgmstream/vgmstream/src/util/bitstream_msb.h @@ -1,10 +1,11 @@ #ifndef _BITSTREAM_MSB_H #define _BITSTREAM_MSB_H -#include "../streamtypes.h" +#include /* Simple bitreader for MPEG/standard bit style, in 'most significant byte' (MSB) format. - * Example: 0x12345678 is read as 78,56,34,12 then each byte's bits. + * Example: with 0x1234 = 00010010 00110100, reading 5b + 6b = 00010 010001 + * (first upper 5b, then next lower 3b and next upper 3b = 6b) * Kept in .h since it's slightly faster (compiler can optimize statics better using default compile flags). */ typedef struct { @@ -14,7 +15,7 @@ typedef struct { uint32_t b_off; /* current offset in bits inside buffer */ } bitstream_t; - +/* convenience util */ static inline void bm_setup(bitstream_t* bs, uint8_t* buf, size_t bufsize) { bs->buf = buf; bs->bufsize = bufsize; @@ -60,10 +61,20 @@ static inline int bm_pos(bitstream_t* bs) { return bs->b_off; } +/* same as (1 << bits) - 1, but that seems to trigger some nasty UB when bits = 32 + * (though in theory (1 << 32) = 0, 0 - 1 = UINT_MAX, but gives 0 compiling in some cases, but not always) */ +static const uint32_t MASK_TABLE_MSB[33] = { + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff, + 0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff, 0x0001ffff, + 0x0003ffff, 0x0007ffff, 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff, 0x00ffffff, 0x01ffffff, 0x03ffffff, + 0x07ffffff, 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff +}; + /* Read bits (max 32) from buf and update the bit offset. Order is BE (MSB). */ static inline int bm_get(bitstream_t* ib, uint32_t bits, uint32_t* value) { - uint32_t shift, pos, val; - int i, bit_buf, bit_val; + uint32_t shift, pos, mask; + uint64_t val; //TODO: could use u32 with some shift fiddling + int i, bit_buf, bit_val, left; if (bits > 32 || ib->b_off + bits > ib->b_max) goto fail; @@ -71,7 +82,7 @@ static inline int bm_get(bitstream_t* ib, uint32_t bits, uint32_t* value) { pos = ib->b_off / 8; /* byte offset */ shift = ib->b_off % 8; /* bit sub-offset */ -#if 1 //naive approach +#if 0 //naive approach val = 0; for (i = 0; i < bits; i++) { bit_buf = (1U << (8-1-shift)) & 0xFF; /* bit check for buf */ @@ -86,12 +97,10 @@ static inline int bm_get(bitstream_t* ib, uint32_t bits, uint32_t* value) { pos++; } } -#else //has bugs - pos = ib->b_off / 8; /* byte offset */ - shift = ib->b_off % 8; /* bit sub-offset */ - uint32_t mask = MASK_TABLE[bits]; /* to remove upper in highest byte */ +#else + mask = MASK_TABLE_MSB[bits]; /* to remove upper in highest byte */ - int left = 0; + left = 0; if (bits == 0) val = 0; else @@ -102,12 +111,12 @@ static inline int bm_get(bitstream_t* ib, uint32_t bits, uint32_t* value) { left = 16 - (bits + shift); if (bits + shift > 16) { val = (val << 8u) | ib->buf[pos+2]; - left = 32 - (bits + shift); + left = 24 - (bits + shift); if (bits + shift > 24) { val = (val << 8u) | ib->buf[pos+3]; left = 32 - (bits + shift); if (bits + shift > 32) { - val = (val << 8u) | ib->buf[pos+4]; /* upper bits are lost (shifting over 32) */ TO-DO + val = (val << 8u) | ib->buf[pos+4]; left = 40 - (bits + shift); } } diff --git a/Frameworks/vgmstream/vgmstream/src/util/cipher_blowfish.c b/Frameworks/vgmstream/vgmstream/src/util/cipher_blowfish.c new file mode 100644 index 000000000..bbbef2548 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/util/cipher_blowfish.c @@ -0,0 +1,303 @@ +#include +#include "cipher_blowfish.h" +#include "reader_get.h" +#include "reader_put.h" + + +/* Simple blowfish implementation, mainly for learning purposes and not really optimized. + * Original algorithm: + * - https://www.schneier.com/academic/archives/1994/09/description_of_a_new.html + */ + +/* ~0x48 + 0x1008, could be done in stack */ +struct blowfish_ctx { + uint32_t P[18]; /* P-array */ + uint32_t S[4][256]; /* S-boxes */ +}; + + +/* hex calculation of the fractional part of pi, first P then S-boxes + * (not a base change but calc'd in hex directly, unsure about the algorithm). + * Pi was chosen for being a "nothing up my sleeves" number. */ +static const uint32_t INIT_P[18] = { /* 16 rounds + 2 extra positions */ + 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89, + 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917, + 0x9216D5D9, 0x8979FB1B +}; + +static const uint32_t INIT_S[4][256] = { + { + 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99, + 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16, 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, + 0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013, + 0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF, 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E, + 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60, 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, + 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE, 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A, + 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677, + 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193, 0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032, + 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239, + 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0, + 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3, 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, + 0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE, + 0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6, 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D, + 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B, 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, + 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA, 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463, + 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, 0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09, + 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3, 0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB, + 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279, 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8, + 0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82, + 0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB, 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, + 0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0, 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B, + 0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790, 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8, + 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4, 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, + 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7, 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C, + 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, 0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1, + 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299, 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9, + 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477, 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF, + 0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF, + 0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA, 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, + 0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41, 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915, + 0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400, 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915, + 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664, 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A, + }, + { + 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266, + 0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1, 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, + 0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6, 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1, + 0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E, 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1, + 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737, 0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8, + 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF, 0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD, + 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701, 0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7, + 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41, 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331, + 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF, 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF, + 0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E, 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87, + 0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C, 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2, + 0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16, 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD, + 0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B, 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509, + 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E, 0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3, + 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F, 0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A, + 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4, 0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960, + 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66, 0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28, + 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802, 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84, + 0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510, 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF, + 0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14, 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E, + 0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50, 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7, + 0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8, 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281, + 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99, 0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696, + 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128, 0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73, + 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0, 0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0, + 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105, 0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250, + 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3, 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285, + 0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00, 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061, + 0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB, 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E, + 0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735, 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC, + 0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9, 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340, + 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20, 0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7, + }, + { + 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934, 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068, + 0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF, 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840, + 0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45, 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504, + 0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A, 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB, + 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE, 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6, + 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42, 0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B, + 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2, 0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB, + 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527, 0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B, + 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33, 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C, + 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3, 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC, + 0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17, 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564, + 0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B, 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115, + 0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922, 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728, + 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0, 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E, + 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37, 0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D, + 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804, 0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B, + 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3, 0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB, + 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D, 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C, + 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350, 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9, + 0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A, 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE, + 0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D, 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC, + 0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F, 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61, + 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2, 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9, + 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2, 0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C, + 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E, 0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633, + 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10, 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169, + 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52, 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027, + 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5, 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62, + 0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634, 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76, + 0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24, 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC, + 0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4, 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C, + 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837, 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0, + }, + { 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE, + 0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B, 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, + 0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8, 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6, + 0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304, 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22, + 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4, 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, + 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9, 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59, + 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, 0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51, + 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28, 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C, + 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B, 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28, + 0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD, + 0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A, 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, + 0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB, 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F, + 0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991, 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32, + 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680, 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, + 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE, 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB, + 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, 0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47, + 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370, 0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D, + 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84, 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048, + 0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD, + 0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9, 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, + 0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38, 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F, + 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C, 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525, + 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1, 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, + 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964, 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E, + 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, 0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D, + 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F, 0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299, + 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02, 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC, + 0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A, + 0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6, 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, + 0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0, 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060, + 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E, 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9, + 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F, 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6, + } +}; + + +static uint32_t blowfish_F(blowfish_ctx* ctx, uint32_t x) { + uint8_t a, b, c, d; + + /* divide xl into four eight-bit quarters */ + a = (x >> 24) & 0xFF; + b = (x >> 16) & 0xFF; + c = (x >> 8) & 0xFF; + d = (x >> 0) & 0xFF; + + return ((ctx->S[0][a] + ctx->S[1][b]) ^ ctx->S[2][c]) + ctx->S[3][d]; +} + + +void blowfish_encrypt(blowfish_ctx* ctx, uint32_t* p_xl, uint32_t* p_xr){ + uint32_t xl, xr, tmp; + + /* divide x into two 32-bit halves (external) */ + xl = *p_xl; + xr = *p_xr; + + for (int i = 0; i < 16; ++i) { + xl = xl ^ ctx->P[i]; + xr = blowfish_F(ctx, xl) ^ xr; + + /* swap */ + tmp = xl; + xl = xr; + xr = tmp; + } + + /* swap (undo last swap) */ + tmp = xl; + xl = xr; + xr = tmp; + + xr = xr ^ ctx->P[16 + 0]; + xl = xl ^ ctx->P[16 + 1]; + + /* recombine (external) */ + *p_xl = xl; + *p_xr = xr; +} + + +/* same as encryption but in reverse order */ +void blowfish_decrypt(blowfish_ctx* ctx, uint32_t* p_xl, uint32_t* p_xr) { + uint32_t xl, xr, tmp; + + /* divide x into two 32-bit halves (external) */ + xl = *p_xl; + xr = *p_xr; + + for (int i = 16 + 1; i > 1; --i) { + xl = xl ^ ctx->P[i]; + xr = blowfish_F(ctx, xl) ^ xr; + + /* swap */ + tmp = xl; + xl = xr; + xr = tmp; + } + + /* swap */ + tmp = xl; + xl = xr; + xr = tmp; + + xr = xr ^ ctx->P[1]; + xl = xl ^ ctx->P[0]; + + /* recombine (external) */ + *p_xl = xl; + *p_xr = xr; +} + + +blowfish_ctx* blowfish_init_ecb(uint8_t* key, int32_t key_len) { + uint32_t xl, xr; + uint8_t tmpkey[18*4]; + + blowfish_ctx* ctx = malloc(sizeof(blowfish_ctx)); + if (!ctx) return NULL; + + + /* preload key to simplify usage below; repeat if key is short */ + for (int i = 0; i < sizeof(tmpkey); i++) { + tmpkey[i] = key[i % key_len]; + } + + /* initialize p-array by XORing Pn by 32-bits of key */ + for (int i = 0; i < 18; i++) { + uint32_t tmp = get_u32be(tmpkey + i * 4); + ctx->P[i] = INIT_P[i] ^ tmp; + } + + /* initialize four S-boxes for use below */ + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 256; j++) { + ctx->S[i][j] = INIT_S[i][j]; + } + } + + /* encrypt 0s with blowfish, using the calculated P subkeys, and replace Pn with each step */ + xl = 0x00000000; + xr = 0x00000000; + for (int i = 0; i < 18; i += 2) { + blowfish_encrypt(ctx, &xl, &xr); + /* Replace P1 and P2 with the output of step */ + ctx->P[i + 0] = xl; + ctx->P[i + 1] = xr; + } + + /* continue with four S-boxes in order with the continuous output */ + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 256; j += 2) { + blowfish_encrypt(ctx, &xl, &xr); + ctx->S[i][j + 0] = xl; + ctx->S[i][j + 1] = xr; + } + } + + return ctx; +} + + +void blowfish_free(blowfish_ctx* ctx) { + free(ctx); +} + + +void blowfish_decrypt_ecb(blowfish_ctx* ctx, uint8_t* block) { + uint32_t xl = get_u32be(block + 0x00); + uint32_t xr = get_u32be(block + 0x04); + blowfish_decrypt(ctx, &xl, &xr); + put_u32be(block + 0x00, xl); + put_u32be(block + 0x04, xr); +} diff --git a/Frameworks/vgmstream/vgmstream/src/util/cipher_blowfish.h b/Frameworks/vgmstream/vgmstream/src/util/cipher_blowfish.h new file mode 100644 index 000000000..a31d43754 --- /dev/null +++ b/Frameworks/vgmstream/vgmstream/src/util/cipher_blowfish.h @@ -0,0 +1,14 @@ +#ifndef _CIPHER_BLOWFISH_H_ +#define _CIPHER_BLOWFISH_H_ +#include + +typedef struct blowfish_ctx blowfish_ctx; + +blowfish_ctx* blowfish_init_ecb(uint8_t* key, int key_len); +void blowfish_encrypt(blowfish_ctx *ctx, uint32_t* xl, uint32_t* xr); +void blowfish_decrypt(blowfish_ctx *ctx, uint32_t* xl, uint32_t* xr); +void blowfish_free(blowfish_ctx* ctx); + +/* assumed block size is at least 0x08 */ +void blowfish_decrypt_ecb(blowfish_ctx* ctx, uint8_t* block); +#endif diff --git a/Frameworks/vgmstream/vgmstream/src/util/layout_utils.c b/Frameworks/vgmstream/vgmstream/src/util/layout_utils.c index 70d39358a..ecf5d1a3c 100644 --- a/Frameworks/vgmstream/vgmstream/src/util/layout_utils.c +++ b/Frameworks/vgmstream/vgmstream/src/util/layout_utils.c @@ -2,9 +2,17 @@ #include "../vgmstream.h" #include "../layout/layout.h" +#include "../coding/coding.h" -bool layered_add_codec(VGMSTREAM* vs, int layers, int layer_channels) { - if (!vs || !vs->codec_data) { + +typedef VGMSTREAM* (*init_vgmstream_t)(STREAMFILE*); + +bool layered_add_subfile(VGMSTREAM* vs, int layers, int layer_channels, STREAMFILE* sf, uint32_t offset, uint32_t size, const char* ext, init_vgmstream_t init_vgmstream) { + int i; + layered_layout_data* data; + STREAMFILE* temp_sf; + + if (!vs) { goto fail; } @@ -13,9 +21,54 @@ bool layered_add_codec(VGMSTREAM* vs, int layers, int layer_channels) { if (!layers) layers = vs->channels / layer_channels; +switch(vs->layout_type) { + case layout_segmented: //to be improved + goto fail; + + case layout_layered: + data = vs->layout_data; + + i = data->curr_layer; + break; + + default: + data = init_layout_layered(layers); + if (!data) goto fail; + vs->layout_data = data; + vs->layout_type = layout_layered; + + i = 0; + break; + } + + temp_sf = setup_subfile_streamfile(sf, offset, size, ext); + if (!temp_sf) goto fail; + + data->layers[i] = init_vgmstream(temp_sf); + close_streamfile(temp_sf); + if (!data->layers[i]) goto fail; + + data->curr_layer++; + + return true; +fail: + return false; +} + + +static bool layered_add_internal(VGMSTREAM* vs, int layers, int layer_channels, STREAMFILE* sf) { int i; layered_layout_data* data; +if (!vs) { + goto fail; + } + + if (!layer_channels) + layer_channels = 1; + if (!layers) + layers = vs->channels / layer_channels; + switch(vs->layout_type) { case layout_segmented: //to be improved goto fail; @@ -46,12 +99,20 @@ bool layered_add_codec(VGMSTREAM* vs, int layers, int layer_channels) { data->layers[i]->loop_end_sample = vs->loop_end_sample; data->layers[i]->codec_data = vs->codec_data; - if (!data->layers[i]->codec_data) goto fail; - data->layers[i]->coding_type = vs->coding_type; + data->layers[i]->coding_type = vs->coding_type; + data->layers[i]->layout_type = layout_none; +data->layers[i]->interleave_block_size = vs->interleave_block_size; + if (vs->interleave_block_size) + data->layers[i]->layout_type = layout_interleave; vs->codec_data = NULL; /* moved to layer, don't hold it */ + if (sf) { + if (!vgmstream_open_stream(data->layers[i], sf, 0x00)) + goto fail; + } + data->curr_layer++; return true; @@ -59,12 +120,29 @@ fail: return false; } +bool layered_add_sf(VGMSTREAM* vs, int layers, int layer_channels, STREAMFILE* sf) { + return layered_add_internal(vs, layers, layer_channels, sf); +} + +bool layered_add_codec(VGMSTREAM* vs, int layers, int layer_channels) { + if (!vs->codec_data) + return false; + + return layered_add_internal(vs, layers, layer_channels, NULL); +} + + bool layered_add_done(VGMSTREAM* vs) { //TODO: some extra checks/setup? if (!setup_layout_layered(vs->layout_data)) goto fail; +if (!vs->coding_type) { + layered_layout_data* data = vs->layout_data; + vs->coding_type = data->layers[0]->coding_type; + } + return true; fail: return false; diff --git a/Frameworks/vgmstream/vgmstream/src/util/layout_utils.h b/Frameworks/vgmstream/vgmstream/src/util/layout_utils.h index d52472195..fca8df652 100644 --- a/Frameworks/vgmstream/vgmstream/src/util/layout_utils.h +++ b/Frameworks/vgmstream/vgmstream/src/util/layout_utils.h @@ -3,6 +3,15 @@ #include "../vgmstream.h" +typedef VGMSTREAM* (*init_vgmstream_t)(STREAMFILE*); + +/* add a new layer from subfile (setups layout if needed) */ +bool layered_add_subfile(VGMSTREAM* vs, int layers, int layer_channels, STREAMFILE* sf, uint32_t offset, uint32_t size, const char* ext, init_vgmstream_t init_vgmstream); + + +/* add a new layer from base vgmstream (setups layout if needed) */ +bool layered_add_sf(VGMSTREAM* vs, int layers, int layer_channels, STREAMFILE* sf); + /* add a new layer from codec data (setups layout if needed) * codec is passed in the vs for easier free/etc control */ bool layered_add_codec(VGMSTREAM* vs, int layers, int layer_channels); diff --git a/Frameworks/vgmstream/vgmstream/src/util/log.h b/Frameworks/vgmstream/vgmstream/src/util/log.h index b94641c32..2396b9aac 100644 --- a/Frameworks/vgmstream/vgmstream/src/util/log.h +++ b/Frameworks/vgmstream/vgmstream/src/util/log.h @@ -67,7 +67,7 @@ void vgm_log_set_callback(void* ctx_p, int level, int type, void* callback); do { \ int i; \ for (i=0; i < buf_size; i++) { \ - printf("%02x",buf[i]); \ + printf("%02x",buf[i] & 0xFF); \ if (bytes_per_line && (i+1) % bytes_per_line == 0) printf("\n"); \ } \ printf("\n"); \ diff --git a/Frameworks/vgmstream/vgmstream/src/util/reader_put.h b/Frameworks/vgmstream/vgmstream/src/util/reader_put.h index 091368410..96624e5ed 100644 --- a/Frameworks/vgmstream/vgmstream/src/util/reader_put.h +++ b/Frameworks/vgmstream/vgmstream/src/util/reader_put.h @@ -1,11 +1,7 @@ #ifndef _READER_PUT_H #define _READER_PUT_H -#ifdef BUILD_VGMSTREAM #include "../streamtypes.h" -#else -#include -#endif void put_8bit(uint8_t* buf, int8_t i); diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 1dcf641c6..c92ec7c81 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -308,7 +308,6 @@ init_vgmstream_t init_vgmstream_functions[] = { init_vgmstream_opus_shinen, init_vgmstream_opus_nus3, init_vgmstream_opus_sps_n1, - init_vgmstream_opus_nxa, init_vgmstream_pc_ast, init_vgmstream_naac, init_vgmstream_ubi_sb, @@ -386,6 +385,7 @@ init_vgmstream_t init_vgmstream_functions[] = { init_vgmstream_xau_konami, init_vgmstream_derf, init_vgmstream_utk, + init_vgmstream_nxa1, init_vgmstream_adpcm_capcom, init_vgmstream_ue4opus, init_vgmstream_xwma, @@ -458,6 +458,7 @@ init_vgmstream_t init_vgmstream_functions[] = { init_vgmstream_diva, init_vgmstream_imuse, init_vgmstream_ktsr, + init_vgmstream_asrs, init_vgmstream_mups, init_vgmstream_kat, init_vgmstream_pcm_success, @@ -521,6 +522,7 @@ init_vgmstream_t init_vgmstream_functions[] = { init_vgmstream_squeaksample, init_vgmstream_snds, init_vgmstream_adm2, + init_vgmstream_nxof, /* lower priority metas (no clean header identity, somewhat ambiguous, or need extension/companion file to identify) */ init_vgmstream_scd_pcm, @@ -614,13 +616,7 @@ static VGMSTREAM* init_vgmstream_internal(STREAMFILE* sf) { try_dual_file_stereo(vgmstream, sf, init_vgmstream_function); } - /* clean as loops are readable metadata but loop fields may contain garbage - * (done *after* dual stereo as it needs loop fields to match) */ - if (!vgmstream->loop_flag) { - vgmstream->loop_start_sample = 0; - vgmstream->loop_end_sample = 0; - } - + #ifdef VGM_USE_FFMPEG /* check FFmpeg streams here, for lack of a better place */ if (vgmstream->coding_type == coding_FFmpeg) { @@ -676,6 +672,28 @@ static VGMSTREAM* init_vgmstream_internal(STREAMFILE* sf) { void setup_vgmstream(VGMSTREAM* vgmstream) { + //TODO improve cleanup (done here to handle manually added layers) + + /* sanify loops and remove bad metadata (some layouts will behave incorrectly) */ + if (vgmstream->loop_flag) { + if (vgmstream->loop_end_sample <= vgmstream->loop_start_sample + || vgmstream->loop_end_sample > vgmstream->num_samples + || vgmstream->loop_start_sample < 0) { + VGM_LOG("VGMSTREAM: wrong loops ignored (lss=%i, lse=%i, ns=%i)\n", + vgmstream->loop_start_sample, vgmstream->loop_end_sample, vgmstream->num_samples); + vgmstream->loop_flag = 0; + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = 0; + } + } + + /* clean as loops are readable metadata but loop fields may contain garbage + * (done *after* dual stereo as it needs loop fields to match) */ + if (!vgmstream->loop_flag) { + vgmstream->loop_start_sample = 0; + vgmstream->loop_end_sample = 0; + } + /* save start things so we can restart when seeking */ memcpy(vgmstream->start_ch, vgmstream->ch, sizeof(VGMSTREAMCHANNEL)*vgmstream->channels); memcpy(vgmstream->start_vgmstream, vgmstream, sizeof(VGMSTREAM)); diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream_types.h b/Frameworks/vgmstream/vgmstream/src/vgmstream_types.h index 945681872..d9736d36d 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream_types.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream_types.h @@ -90,6 +90,7 @@ typedef enum { coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */ coding_MTF_IMA, /* Capcom MT Framework IMA ADPCM */ coding_CD_IMA, /* Crystal Dynamics IMA ADPCM */ +coding_CRANKCASE_IMA, /* CrankcaseAudio REV IMA ADPCM */ coding_MSADPCM, /* Microsoft ADPCM (stereo/mono) */ coding_MSADPCM_int, /* Microsoft ADPCM (mono) */ @@ -611,7 +612,7 @@ typedef enum { meta_DERF, /* Stupid Invaders (PC) */ meta_SADF, meta_UTK, - meta_NXA, + meta_NXA1, meta_ADPCM_CAPCOM, meta_UE4OPUS, meta_XWMA, @@ -701,6 +702,7 @@ typedef enum { meta_SQUEAKSTREAM, meta_SQUEAKSAMPLE, meta_SNDS, +meta_NXOF, } meta_t;