From fce851bfffb9a0b19c771730a589a2953cf68656 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Wed, 26 Jan 2022 21:00:13 -0800 Subject: [PATCH] Updated VGMStream to r1702-16-g2db8e56e Signed-off-by: Christopher Snowhill --- .../vgmstream/vgmstream/src/meta/aifc.c | 14 +- .../vgmstream/vgmstream/src/meta/bnk_sony.c | 135 ++++++++++++++---- .../vgmstream/vgmstream/src/meta/mjb_mjh.c | 39 ++++- .../vgmstream/vgmstream/src/meta/rwsd.c | 10 +- .../vgmstream/vgmstream/src/vgmstream.c | 2 +- 5 files changed, 150 insertions(+), 50 deletions(-) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/aifc.c b/Frameworks/vgmstream/vgmstream/src/meta/aifc.c index dcfb02980..678895c0a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/aifc.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/aifc.c @@ -68,7 +68,7 @@ static int is_str(const char* str, int len, off_t offset, STREAMFILE* sf) { VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; off_t start_offset = 0, coef_offset = 0; - size_t file_size; + uint32_t aifx_size, file_size; coding_t coding_type = 0; int channels = 0, sample_count = 0, sample_size = 0, sample_rate = 0; int interleave = 0; @@ -111,8 +111,7 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { } file_size = get_streamfile_size(sf); - if (read_u32be(0x04,sf) + 0x08 != file_size) - goto fail; + aifx_size = read_u32be(0x04,sf); /* AIFF originally allowed only PCM (non-compressed) audio, so newer AIFC was added, * though some AIFF with other codecs exist */ @@ -128,6 +127,15 @@ VGMSTREAM* init_vgmstream_aifc(STREAMFILE* sf) { goto fail; } + /* some games have wonky sizes, selectively fix to catch bad rips and new mutations */ + if (file_size != aifx_size + 0x08) { + if (is_aiff && file_size == aifx_size + 0x08 + 0x08) + aifx_size += 0x08; /* [Psychic Force Puzzle Taisen CD2 (PS1)] */ + } + + if (aifx_size + 0x08 != file_size) + goto fail; + /* read through chunks to verify format and find metadata */ { diff --git a/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c b/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c index 329c74b67..410565749 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/bnk_sony.c @@ -5,7 +5,7 @@ typedef enum { PSX, PCM16, ATRAC9, HEVAG } bnk_codec; -/* .BNK - Sony's Scream Tool bank format [Puyo Puyo Tetris (PS4), NekoBuro: Cats Block (Vita)] */ +/* .BNK - Sony's SCREAM bank format [The Sly Collection (PS3), Puyo Puyo Tetris (PS4), NekoBuro: Cats Block (Vita)] */ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; uint32_t start_offset, stream_offset, name_offset = 0; @@ -13,7 +13,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { uint32_t stream_size, data_size, interleave = 0; int channels = 0, loop_flag, sample_rate, parts, sblk_version, big_endian; int loop_start = 0, loop_end = 0; - uint32_t pitch, flags; + uint32_t center_note, center_fine, flags; uint32_t atrac9_info = 0; int total_subsongs, target_subsong = sf->stream_index; @@ -23,7 +23,7 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { bnk_codec codec; - /* bnk version */ + /* bnk/SCREAM tool version */ if (read_u32be(0x00,sf) == 0x03) { /* PS3 */ read_u32 = read_u32be; read_u16 = read_u16be; @@ -47,9 +47,10 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { parts = read_u32(0x04,sf); if (parts < 2 || parts > 3) goto fail; + /* in theory a bank can contain multiple blocks */ sblk_offset = read_u32(0x08,sf); - /* 0x0c: sklb size */ + /* 0x0c: sblk size */ data_offset = read_u32(0x10,sf); data_size = read_u32(0x14,sf); /* when sblk_offset >= 0x20: */ @@ -62,10 +63,17 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { /* SBlk part: parse header */ - if (read_u32(sblk_offset+0x00,sf) != get_id32be("klBS")) /* SBlk = sample block? */ + if (read_u32(sblk_offset+0x00,sf) != get_id32be("klBS")) /* SBlk = SFX block */ goto fail; sblk_version = read_u32(sblk_offset+0x04,sf); - /* 0x08: possibly when sblk_version=0x0d, 0x03=Vita, 0x06=PS4 */ + /* 0x08: flags? (sblk_version>=0x0d?, 0x03=Vita, 0x06=PS4) + * - 04: non-fixed bank? + * - 100: has names + * - 200: has user data + */ + /* 0x0c: block id */ + /* 0x10: block number */ + /* 0x11: padding */ //;VGM_LOG("BNK: sblk_offset=%lx, data_offset=%lx, sblk_version %x\n", sblk_offset, data_offset, sblk_version); { @@ -99,13 +107,18 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { case 0x05: /* Ratchet & Clank (PS3) */ case 0x08: /* Playstation Home Arcade (Vita) */ case 0x09: /* Puyo Puyo Tetris (PS4) */ - section_entries = read_u16(sblk_offset+0x16,sf); /* entry size: ~0x0c */ - material_entries = read_u16(sblk_offset+0x18,sf); /* entry size: ~0x08 */ - stream_entries = read_u16(sblk_offset+0x1a,sf); /* entry size: ~0x18 + variable */ - table1_offset = sblk_offset + read_u32(sblk_offset+0x1c,sf); - table2_offset = sblk_offset + read_u32(sblk_offset+0x20,sf); - table3_offset = sblk_offset + read_u32(sblk_offset+0x34,sf); - table4_offset = sblk_offset + read_u32(sblk_offset+0x38,sf); + section_entries = read_u16(sblk_offset+0x16,sf); /* entry size: ~0x0c (NumSounds)*/ + material_entries = read_u16(sblk_offset+0x18,sf); /* entry size: ~0x08 (NumGrains) */ + stream_entries = read_u16(sblk_offset+0x1a,sf); /* entry size: ~0x18 + variable (NumWaveforms) */ + table1_offset = sblk_offset + read_u32(sblk_offset+0x1c,sf); /* sound offset */ + table2_offset = sblk_offset + read_u32(sblk_offset+0x20,sf); /* grain offset */ + /* 0x24: VAG address? */ + /* 0x28: data size */ + /* 0x2c: RAM size */ + /* 0x30: next block offset */ + table3_offset = sblk_offset + read_u32(sblk_offset+0x34,sf); /* grain data? */ + table4_offset = sblk_offset + read_u32(sblk_offset+0x38,sf); /* block names */ + /*0x3c: SFXUD? */ table1_entry_size = 0x0c; table1_suboffset = 0x08; @@ -138,8 +151,11 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { /* table defs: * - table1: sections, point to some materials (may be less than streams/materials) + * (a "sound" that has N grains, and is triggered by games like a cue) * - table2: materials, point to all sounds or others subtypes (may be more than sounds) + * (a "grain" that does actions like play or changes volume) * - table3: sounds, point to streams (multiple sounds can repeat stream) + * (a "waveform" being the actual stream) * - table4: names define section names (not all sounds may have a name) * * approximate table parsing @@ -185,8 +201,8 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { table2_value = read_u32(table2_offset+(i*0x08)+table2_suboffset+0x00,sf); table2_subinfo = (table2_value >> 0) & 0xFFFF; table2_subtype = (table2_value >> 16) & 0xFFFF; - if (table2_subtype != 0x100) - continue; /* not sounds */ + if (table2_subtype != 0x0100) + continue; /* not sounds (ex. 1: waveform, 42: silence, 25: random, etc) */ total_subsongs++; if (total_subsongs == target_subsong) { @@ -205,8 +221,8 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { if (target_subsong < 0 || target_subsong > total_subsongs || total_subsongs < 1) goto fail; /* this means some subsongs repeat streams, that can happen in some sfx banks, whatevs */ if (total_subsongs != stream_entries) { - VGM_LOG("BNK: subsongs %i vs table3 %i don't match\n", total_subsongs, stream_entries); - /* find_dupes...? */ + VGM_LOG("BNK: subsongs %i vs table3 %i don't match (repeated streams?)\n", total_subsongs, stream_entries); + /* TODO: find dupes? */ } //;VGM_LOG("BNK: header entry at %lx\n", table3_offset+table3_entry_offset); @@ -219,21 +235,64 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { case 0x05: case 0x08: case 0x09: - pitch = read_u8(table3_offset+table3_entry_offset+0x02,sf); - flags = read_u8(table3_offset+table3_entry_offset+0x0f,sf); + /* "tone" */ + /* 0x00: priority */ + /* 0x01: volume */ + center_note = read_u8 (table3_offset+table3_entry_offset+0x02,sf); + center_fine = read_u8 (table3_offset+table3_entry_offset+0x03,sf); + /* 0x04: pan */ + /* 0x06: map low */ + /* 0x07: map high */ + /* 0x08: pitch bend low */ + /* 0x09: pitch bend high */ + /* 0x0a: ADSR1 */ + /* 0x0c: ADSR2 */ + flags = read_u16(table3_offset+table3_entry_offset+0x0e,sf); stream_offset = read_u32(table3_offset+table3_entry_offset+0x10,sf); stream_size = read_u32(table3_offset+table3_entry_offset+0x14,sf); - switch(pitch) { - case 0xC4: sample_rate = 48000; break; - case 0xC2: sample_rate = 44100; break; - case 0xB6: sample_rate = 22050; break; - case 0xAA: sample_rate = 11025; break; - default: - /* approximate note-to-hz, not sure about real formula (observed from 0x9a to 0xc6) - * using (rate) * 2 ^ ((pitch - note) / 12) but not correct? (may be rounded) */ - sample_rate = 614.0 * pow(2.0, (float)(pitch - 0x70) / 12.0); - break; + /* "base" sample rates, allowed by the tool (for other rates must use base + semitones, but aren't exact) */ + if (center_note == 0xC4 && center_fine == 0x00) + sample_rate = 48000; + else if (center_note == 0xC2 && center_fine == 0x42) + sample_rate = 44100; + else if (center_note == 0xb6 && center_fine == 0x42) + sample_rate = 22050; + else if (center_note == 0xaa && center_fine == 0x42) + sample_rate = 11025; + else if (center_note == 0xa4 && center_fine == 0x7c) + sample_rate = 8000; + else { + /* rough ("center") sample rates using semitone-to-hz formula: (rate) * 2 ^ ((pitch - base) / 12) */ + double curr_rate = 48000 * pow(2.0, (double)((int)center_note - 0xc4) / 12.0); + double prev_rate = 48000 * pow(2.0, (double)(((int)center_note - 1) - 0xc4) / 12.0); + /* partial semitone, from 0x00 = 0.0 to 0x7f = 1.0 of a semitone for current rate */ + float fine_pct = center_fine / 127.0f; + + //TODO improve (fine seems approximate and not sure how to calc current semitone hz value, so needs prev_rate) + sample_rate = curr_rate + (curr_rate - prev_rate) * fine_pct; + + /* some odd "beep" sfx in Sly 2/3 seems to go slightly higher after applying fine_pct, probably should resample */ + if (sample_rate > VGMSTREAM_MAX_SAMPLE_RATE) + sample_rate = VGMSTREAM_MAX_SAMPLE_RATE; + + /* waves can set base sample rate (48/44/22/11/8khz) + pitch in semitones, then converted to center+fine + * 48000 + pitch 0.0 > 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.0 > center=0xd0, fine=0x00 + * 48000 + pitch 24.0 > center=0xdc, fine=0x00 + * 48000 + pitch 56.0 > center=0xfc, fine=0x00 + * 48000 + pitch 68.0 > center=0x08, fine=0x00 > ? + * 48000 + pitch -12.0 > 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; @@ -343,12 +402,26 @@ VGMSTREAM* init_vgmstream_bnk_sony(STREAMFILE* sf) { } interleave = stream_size / channels; - if ((flags & 0x80) && sblk_version <= 3) { /* PS Home Arcade has other flags? */ + /* 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 + */ + if ((flags & 0x80) && sblk_version <= 3) { codec = PCM16; /* rare [Wipeout HD (PS3)]-v3 */ } else { loop_flag = ps_find_loop_offsets(sf, start_offset, stream_size, channels, interleave, &loop_start, &loop_end); - loop_flag = (flags & 0x40); /* no loops values in sight so may only apply to PS-ADPCM flags */ + loop_flag = (flags & 0x40); /* only applies to PS-ADPCM flags */ codec = PSX; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/mjb_mjh.c b/Frameworks/vgmstream/vgmstream/src/meta/mjb_mjh.c index f80b7f7b0..d2d7ca07a 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/mjb_mjh.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/mjb_mjh.c @@ -5,18 +5,33 @@ VGMSTREAM* init_vgmstream_mjb_mjh(STREAMFILE* sf) { VGMSTREAM* vgmstream = NULL; STREAMFILE* sh = NULL; + STREAMFILE* sb = NULL; off_t start_offset = 0, header_offset = 0; size_t data_size, frame_size, frame_count; int channels, loop_flag, sample_rate; int total_subsongs, target_subsong = sf->stream_index; + /* for plugins that start with .mjb */ + if (check_extensions(sf,"mjb")) { + sh = open_streamfile_by_ext(sf, "mjh"); + if (!sh) goto fail; + } + else { + sh = sf; + } + /* checks */ + /* "base" header check only has total subsongs, check expected header size */ + if (read_s32le(0x00,sh) * 0x40 + 0x40 != get_streamfile_size(sh)) + goto fail; + if (read_u32le(0x10,sh) != 0 || read_u32le(0x20,sh) != 0 || read_u32le(0x30,sh) != 0) /* padding until 0x40 */ + goto fail; + + /* only body for now */ if (!check_extensions(sf, "mjb")) goto fail; - sh = open_streamfile_by_ext(sf, "mjh"); - if (!sh) goto fail; /* parse entries */ { @@ -42,8 +57,6 @@ VGMSTREAM* init_vgmstream_mjb_mjh(STREAMFILE* sf) { goto fail; } - VGM_LOG("3: %lx\n", start_offset); - /* also see MIB+MIH (same thing but this excludes padding stuff) */ if (read_u32le(header_offset + 0x00,sh) != 0x40) goto fail; @@ -56,6 +69,16 @@ VGMSTREAM* init_vgmstream_mjb_mjh(STREAMFILE* sf) { loop_flag = 0; + /* for plugins that start with .mjh (and don't check extensions) */ + if (sf == sh) { + sb = open_streamfile_by_ext(sf, "mjb"); + if (!sb) goto fail; + } + else { + sb = sf; + } + + /* build the VGMSTREAM */ vgmstream = allocate_vgmstream(channels, loop_flag); if (!vgmstream) goto fail; @@ -71,13 +94,15 @@ VGMSTREAM* init_vgmstream_mjb_mjh(STREAMFILE* sf) { vgmstream->layout_type = layout_interleave; vgmstream->interleave_block_size = frame_size; - if (!vgmstream_open_stream(vgmstream, sf, start_offset)) + if (!vgmstream_open_stream(vgmstream, sb, start_offset)) goto fail; - close_streamfile(sh); + if (sf != sh) close_streamfile(sh); + if (sf != sb) close_streamfile(sb); return vgmstream; fail: - close_streamfile(sh); + if (sf != sh) close_streamfile(sh); + if (sf != sb) close_streamfile(sb); close_vgmstream(vgmstream); return NULL; } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c b/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c index 669d08aef..42dc35995 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/rwsd.c @@ -128,7 +128,7 @@ VGMSTREAM* init_vgmstream_rwsd(STREAMFILE* sf) { /* get WAVE offset, check */ rwav_data.wave_offset = read_32bit(0x18,sf); - if (!is_id32be(0x00, sf, "WAVE")) + if (!is_id32be(rwav_data.wave_offset + 0x00, sf, "WAVE")) goto fail; /* get WAVE size, check */ @@ -237,11 +237,6 @@ VGMSTREAM* init_vgmstream_rwsd(STREAMFILE* sf) { vgmstream->ch[j].adpcm_coef[i] = read_16bit(codec_info_offset + i*0x2, sf); } } - - if (vgmstream->coding_type == coding_3DS_IMA) { - vgmstream->ch[j].adpcm_history1_16 = read_16bit(codec_info_offset + 0x00,sf); - vgmstream->ch[j].adpcm_step_index = read_16bit(codec_info_offset + 0x02,sf); - } } } @@ -263,8 +258,7 @@ VGMSTREAM* init_vgmstream_rwsd(STREAMFILE* sf) { if (!vgmstream->ch[i].streamfile) goto fail; - if (!(rwar || rwav)) - { + if (!(rwar || rwav)) { vgmstream->ch[i].channel_start_offset= vgmstream->ch[i].offset= rwav_data.start_offset + i*stream_size; diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.c b/Frameworks/vgmstream/vgmstream/src/vgmstream.c index 13cf7641d..f54d7ab99 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.c @@ -499,7 +499,6 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_acx, init_vgmstream_compresswave, init_vgmstream_ktac, - init_vgmstream_mjb_mjh, init_vgmstream_mzrt_v1, init_vgmstream_bsnf, init_vgmstream_tac, @@ -525,6 +524,7 @@ VGMSTREAM* (*init_vgmstream_functions[])(STREAMFILE* sf) = { init_vgmstream_rsf, init_vgmstream_ps2_wmus, init_vgmstream_mib_mih, + init_vgmstream_mjb_mjh, init_vgmstream_mic_koei, init_vgmstream_seb, init_vgmstream_ps2_pnb,