From d74af63da0aaecb7decc0ddd3f3eb670560fae33 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sat, 14 Jun 2025 23:14:53 -0700 Subject: [PATCH] VGMStream: Updated libvgmstream code base Updated VGMStream to r2023-27-g7b0c835c Signed-off-by: Christopher Snowhill --- .../vgmstream/src/base/play_config.c | 1 + .../vgmstream/vgmstream/src/base/play_state.c | 7 -- .../vgmstream/vgmstream/src/base/plugins.c | 2 - .../vgmstream/vgmstream/src/meta/hca_keys.h | 3 + Frameworks/vgmstream/vgmstream/src/meta/nub.c | 6 +- .../vgmstream/vgmstream/src/meta/sndx.c | 52 ++++---- .../vgmstream/src/meta/txtp_process.c | 111 +++++++++--------- .../vgmstream/vgmstream/src/meta/xwb_xsb.h | 22 ++-- .../vgmstream/vgmstream/src/vgmstream.h | 3 - 9 files changed, 102 insertions(+), 105 deletions(-) diff --git a/Frameworks/vgmstream/vgmstream/src/base/play_config.c b/Frameworks/vgmstream/vgmstream/src/base/play_config.c index 1f577fa07..337f095df 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/play_config.c +++ b/Frameworks/vgmstream/vgmstream/src/base/play_config.c @@ -106,4 +106,5 @@ void vgmstream_apply_config(VGMSTREAM* vgmstream, vgmstream_cfg_t* vcfg) { vgmstream->config_enabled = def->config_set; setup_vgmstream_play_state(vgmstream); + setup_vgmstream(vgmstream); } diff --git a/Frameworks/vgmstream/vgmstream/src/base/play_state.c b/Frameworks/vgmstream/vgmstream/src/base/play_state.c index 91cfb37ce..bb79338cb 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/play_state.c +++ b/Frameworks/vgmstream/vgmstream/src/base/play_state.c @@ -166,10 +166,6 @@ static void setup_state_processing(VGMSTREAM* vgmstream) { ps->fade_start = ps->pad_begin_duration + ps->body_duration; //ps->pad_end_left = ps->pad_end_duration; ps->pad_end_start = ps->fade_start + ps->fade_duration; - - /* other info (updated once mixing is enabled) */ - ps->input_channels = vgmstream->channels; - ps->output_channels = vgmstream->channels; } /* apply play config to internal state */ @@ -179,7 +175,4 @@ void setup_vgmstream_play_state(VGMSTREAM* vgmstream) { setup_state_modifiers(vgmstream); setup_state_processing(vgmstream); - - /* save new config for resets */ - setup_vgmstream(vgmstream); } diff --git a/Frameworks/vgmstream/vgmstream/src/base/plugins.c b/Frameworks/vgmstream/vgmstream/src/base/plugins.c index 3e51a04e5..217100b12 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/plugins.c +++ b/Frameworks/vgmstream/vgmstream/src/base/plugins.c @@ -159,8 +159,6 @@ void vgmstream_mixing_enable(VGMSTREAM* vgmstream, int32_t max_sample_count, int mixing_setup(vgmstream, max_sample_count); mixing_info(vgmstream, input_channels, output_channels); - /* update internals */ - mixing_info(vgmstream, &vgmstream->pstate.input_channels, &vgmstream->pstate.output_channels); setup_vgmstream(vgmstream); } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index 0ce5e6f0b..c40368700 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -1552,6 +1552,9 @@ static const hcakey_info hcakey_list[] = { // Suikoden I & II HD Remaster (PC) {14510296783270449627u}, // C95EEE0BA85411DB + // CHUNITHM Chinese Version (AC) + {30194896045700459}, // 006B461914D5756B + }; #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/nub.c b/Frameworks/vgmstream/vgmstream/src/meta/nub.c index ab88f1490..4f41b9b41 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/nub.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/nub.c @@ -30,8 +30,10 @@ VGMSTREAM* init_vgmstream_nub(STREAMFILE* sf) { goto fail; /* .nub: standard - * .nub2: rare [iDOLM@STER: Gravure For You (PS3), Noby Noby Boy (iOS)] */ - if (!check_extensions(sf, "nub,nub2")) + * .nub2: rare [iDOLM@STER: Gravure For You (PS3), Noby Noby Boy (iOS)] + * .nps: Ace Combat Joint Assault (PSP) BGM only + */ + if (!check_extensions(sf, "nub,nub2,nps")) goto fail; /* sometimes LE [Soul Calibur: Broken Destiny (PSP), Tales of Vesperia (PS4) */ diff --git a/Frameworks/vgmstream/vgmstream/src/meta/sndx.c b/Frameworks/vgmstream/vgmstream/src/meta/sndx.c index 801e838d0..4cc2f8869 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/sndx.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/sndx.c @@ -2,6 +2,7 @@ #include "../coding/coding.h" #include "../util/chunks.h" +static uint32_t get_name_offset(STREAMFILE* sf_sxd1, uint32_t first_offset, int target_subsong); /* SXDF/SXDS - Sony/SCE's SNDX lib format (cousin of SGXD) [Gravity Rush, Freedom Wars, Soul Sacrifice PSV] */ VGMSTREAM* init_vgmstream_sndx(STREAMFILE* sf) { @@ -147,19 +148,7 @@ VGMSTREAM* init_vgmstream_sndx(STREAMFILE* sf) { } } - /* get stream name (NAME is tied to REQD/cues, and SFX cues repeat WAVEs, but should work ok for streams) */ - if (is_dual && find_chunk_le(sf_sxd1, get_id32be("NAME"),first_offset,0, &chunk_offset,NULL)) { - /* table: relative offset (32b) + hash? (32b) + cue index (32b) */ - int i; - int num_entries = read_s16le(chunk_offset + 0x04, sf_sxd1); /* can be bigger than streams */ - for (i = 0; i < num_entries; i++) { - uint32_t index = read_u32le(chunk_offset + 0x08 + 0x08 + i * 0x0c,sf_sxd1); - if (index+1 == target_subsong) { - name_offset = chunk_offset + 0x08 + 0x00 + i*0x0c + read_u32le(chunk_offset + 0x08 + 0x00 + i * 0x0c, sf_sxd1); - break; - } - } - } + name_offset = get_name_offset(sf_sxd1, first_offset, target_subsong); if (is_external && !is_dual) { VGM_LOG("SXD: found single sxd with external data\n"); @@ -228,14 +217,6 @@ VGMSTREAM* init_vgmstream_sndx(STREAMFILE* sf) { goto fail; } - /* .sxd have names but they are a bit complex: - * - WAVE chunk has N subsongs - * - NAME chunk may define M names but usually doesn't match with subsongs (may be less or more) - * - LVRN may define some state and LVAR its possible values (battle_state=bgm_battle_start/win/intro) - * - TRNS also has names based on possible transition - * - final WAVE seem to depend on NAME (event?) + state=value, similar to Wwise - * - presumably TONE/REQD/SEQD chunks have WAVE<>event matching info since they seem to always appear - */ /* open the file for reading */ if (!vgmstream_open_stream(vgmstream, sf_data, start_offset)) @@ -251,3 +232,32 @@ fail: close_vgmstream(vgmstream); return NULL; } + +/* .sxd have names but they are a bit complex: + * - WAVE chunk has N subsongs + * - NAME chunk may define M names but may not match with subsongs (less or more, ex. randoms) + * - LVRN may define some state and LVAR its possible values (battle_state=bgm_battle_start/win/intro) + * - TRNS also has names based on possible transition + * - final WAVE seem to depend on NAME (event?) + state=value, similar to Wwise + * - presumably TONE/REQD/SEQD chunks have WAVE<>event matching info since they seem to always appear + * For now do simple matching. + */ +static uint32_t get_name_offset(STREAMFILE* sf_sxd1, uint32_t first_offset, int target_subsong) { + off_t chunk_offset = 0; + + if (!find_chunk_le(sf_sxd1, get_id32be("NAME"),first_offset,0, &chunk_offset,NULL)) + return 0; + + // table: relative offset (32b) + hash? (32b) + cue index (32b) + int num_entries = read_s16le(chunk_offset + 0x04, sf_sxd1); + + // TODO: index N to subsong N works ok for streams but not always for SFX + for (int i = 0; i < num_entries; i++) { + uint32_t index = read_u32le(chunk_offset + 0x08 + 0x08 + i * 0x0c,sf_sxd1); + if (index+1 != target_subsong) + continue; + return chunk_offset + 0x08 + 0x00 + i*0x0c + read_u32le(chunk_offset + 0x08 + 0x00 + i * 0x0c, sf_sxd1); + } + + return 0; +} diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txtp_process.c b/Frameworks/vgmstream/vgmstream/src/meta/txtp_process.c index 437e75172..7185278c3 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txtp_process.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txtp_process.c @@ -102,8 +102,7 @@ static void apply_settings(VGMSTREAM* vgmstream, txtp_entry_t* current) { /* add macro to mixing list */ if (current->channel_mask) { - int ch; - for (ch = 0; ch < vgmstream->channels; ch++) { + for (int ch = 0; ch < vgmstream->channels; ch++) { if (!((current->channel_mask >> ch) & 1)) { txtp_mix_data_t mix = {0}; mix.ch_dst = ch + 1; @@ -113,72 +112,70 @@ static void apply_settings(VGMSTREAM* vgmstream, txtp_entry_t* current) { } } - /* copy mixing list (should be done last as some mixes depend on config) */ - if (current->mixing_count > 0) { - int m, position_samples; + /* apply play config (after sample rate/etc mods) */ + txtp_copy_config(&vgmstream->config, ¤t->config); + setup_vgmstream_play_state(vgmstream); + // config is enabled in layouts or externally (for compatibility, since we don't know yet if this + // VGMSTREAM will part of a layout, or is enabled externally to not mess up plugins's calcs) - for (m = 0; m < current->mixing_count; m++) { - txtp_mix_data_t *mix = ¤t->mixing[m]; + /* apply mixing (last as some mixes depend on config like loops/etc, shouldn't matter much) */ + for (int m = 0; m < current->mixing_count; m++) { + txtp_mix_data_t* mix = ¤t->mixing[m]; - switch(mix->command) { - /* base mixes */ - case MIX_SWAP: mixing_push_swap(vgmstream, mix->ch_dst, mix->ch_src); break; - case MIX_ADD: mixing_push_add(vgmstream, mix->ch_dst, mix->ch_src, 1.0); break; - case MIX_ADD_VOLUME: mixing_push_add(vgmstream, mix->ch_dst, mix->ch_src, mix->vol); break; - case MIX_VOLUME: mixing_push_volume(vgmstream, mix->ch_dst, mix->vol); break; - case MIX_LIMIT: mixing_push_limit(vgmstream, mix->ch_dst, mix->vol); break; - case MIX_UPMIX: mixing_push_upmix(vgmstream, mix->ch_dst); break; - case MIX_DOWNMIX: mixing_push_downmix(vgmstream, mix->ch_dst); break; - case MIX_KILLMIX: mixing_push_killmix(vgmstream, mix->ch_dst); break; - case MIX_FADE: - /* Convert from time to samples now that sample rate is final. - * Samples and time values may be mixed though, so it's done for every - * value (if one is 0 the other will be too, though) */ - if (mix->time_pre > 0.0) mix->sample_pre = mix->time_pre * vgmstream->sample_rate; - if (mix->time_start > 0.0) mix->sample_start = mix->time_start * vgmstream->sample_rate; - if (mix->time_end > 0.0) mix->sample_end = mix->time_end * vgmstream->sample_rate; - if (mix->time_post > 0.0) mix->sample_post = mix->time_post * vgmstream->sample_rate; - /* convert special meaning too */ - if (mix->time_pre < 0.0) mix->sample_pre = -1; - if (mix->time_post < 0.0) mix->sample_post = -1; + switch(mix->command) { + // base mixes + case MIX_SWAP: mixing_push_swap(vgmstream, mix->ch_dst, mix->ch_src); break; + case MIX_ADD: mixing_push_add(vgmstream, mix->ch_dst, mix->ch_src, 1.0); break; + case MIX_ADD_VOLUME: mixing_push_add(vgmstream, mix->ch_dst, mix->ch_src, mix->vol); break; + case MIX_VOLUME: mixing_push_volume(vgmstream, mix->ch_dst, mix->vol); break; + case MIX_LIMIT: mixing_push_limit(vgmstream, mix->ch_dst, mix->vol); break; + case MIX_UPMIX: mixing_push_upmix(vgmstream, mix->ch_dst); break; + case MIX_DOWNMIX: mixing_push_downmix(vgmstream, mix->ch_dst); break; + case MIX_KILLMIX: mixing_push_killmix(vgmstream, mix->ch_dst); break; + case MIX_FADE: + // Convert from time to samples now that sample rate is final. + // Samples and time values may be mixed though, so it's done for every + // value (if one is 0 the other will be too, though) + if (mix->time_pre > 0.0) mix->sample_pre = mix->time_pre * vgmstream->sample_rate; + if (mix->time_start > 0.0) mix->sample_start = mix->time_start * vgmstream->sample_rate; + if (mix->time_end > 0.0) mix->sample_end = mix->time_end * vgmstream->sample_rate; + if (mix->time_post > 0.0) mix->sample_post = mix->time_post * vgmstream->sample_rate; + // convert special meaning too + if (mix->time_pre < 0.0) mix->sample_pre = -1; + if (mix->time_post < 0.0) mix->sample_post = -1; - if (mix->position_type == TXTP_POSITION_LOOPS && vgmstream->loop_flag) { - int loop_pre = vgmstream->loop_start_sample; - int loop_samples = (vgmstream->loop_end_sample - vgmstream->loop_start_sample); + if (mix->position_type == TXTP_POSITION_LOOPS && vgmstream->loop_flag) { + int loop_pre = vgmstream->loop_start_sample; + int loop_samples = (vgmstream->loop_end_sample - vgmstream->loop_start_sample); - position_samples = loop_pre + loop_samples * mix->position; + int position_samples = loop_pre + loop_samples * mix->position; - if (mix->sample_pre >= 0) mix->sample_pre += position_samples; - mix->sample_start += position_samples; - mix->sample_end += position_samples; - if (mix->sample_post >= 0) mix->sample_post += position_samples; - } + if (mix->sample_pre >= 0) mix->sample_pre += position_samples; + mix->sample_start += position_samples; + mix->sample_end += position_samples; + if (mix->sample_post >= 0) mix->sample_post += position_samples; + } + mixing_push_fade(vgmstream, + mix->ch_dst, mix->vol_start, mix->vol_end, mix->shape, + mix->sample_pre, mix->sample_start, mix->sample_end, mix->sample_post); + break; - mixing_push_fade(vgmstream, mix->ch_dst, mix->vol_start, mix->vol_end, mix->shape, - mix->sample_pre, mix->sample_start, mix->sample_end, mix->sample_post); - break; + // macro mixes + case MACRO_VOLUME: mixing_macro_volume(vgmstream, mix->vol, mix->mask); break; + case MACRO_TRACK: mixing_macro_track(vgmstream, mix->mask); break; + case MACRO_LAYER: mixing_macro_layer(vgmstream, mix->max, mix->mask, mix->mode); break; + case MACRO_CROSSTRACK: mixing_macro_crosstrack(vgmstream, mix->max); break; + case MACRO_CROSSLAYER: mixing_macro_crosslayer(vgmstream, mix->max, mix->mode); break; + case MACRO_DOWNMIX: mixing_macro_downmix(vgmstream, mix->max); break; - /* macro mixes */ - case MACRO_VOLUME: mixing_macro_volume(vgmstream, mix->vol, mix->mask); break; - case MACRO_TRACK: mixing_macro_track(vgmstream, mix->mask); break; - case MACRO_LAYER: mixing_macro_layer(vgmstream, mix->max, mix->mask, mix->mode); break; - case MACRO_CROSSTRACK: mixing_macro_crosstrack(vgmstream, mix->max); break; - case MACRO_CROSSLAYER: mixing_macro_crosslayer(vgmstream, mix->max, mix->mode); break; - case MACRO_DOWNMIX: mixing_macro_downmix(vgmstream, mix->max); break; - - default: - break; - } + default: + break; } } - - /* default play config (last after sample rate mods/mixing/etc) */ - txtp_copy_config(&vgmstream->config, ¤t->config); - setup_vgmstream_play_state(vgmstream); - /* config is enabled in layouts or externally (for compatibility, since we don't know yet if this - * VGMSTREAM will part of a layout, or is enabled externally to not mess up plugins's calcs) */ + /* save final config */ + setup_vgmstream(vgmstream); } diff --git a/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h b/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h index 0177c04d1..14d2b5a48 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/xwb_xsb.h @@ -711,14 +711,14 @@ static int parse_xsb_cues(xsb_header *xsb, STREAMFILE *sf) { * - https://github.com/MonoGame/MonoGame/blob/master/MonoGame.Framework/Audio/Xact/ * - https://github.com/espes/MacTerrariaWrapper/tree/master/xactxtract */ -static int parse_xsb(xsb_header *xsb, STREAMFILE *sf, char *xwb_wavebank_name) { +static bool parse_xsb(xsb_header *xsb, STREAMFILE *sf, char *xwb_wavebank_name) { /* check header */ - if ((read_u32be(0x00,sf) != 0x5344424B) && /* "SDBK" (LE) */ - (read_u32be(0x00,sf) != 0x4B424453)) /* "KBDS" (BE) */ - goto fail; + if (!is_id32be(0x00,sf, "SDBK") && // LE + !is_id32be(0x00,sf, "KBDS")) // BE + return false; - xsb->big_endian = (read_u32be(0x00,sf) == 0x4B424453); /* "KBDS" */ + xsb->big_endian = (is_id32be(0x00,sf, "KBDS")); read_s32_t read_s32 = xsb->big_endian ? read_s32be : read_s32le; read_s16_t read_s16 = xsb->big_endian ? read_s16be : read_s16le; @@ -843,20 +843,18 @@ static int parse_xsb(xsb_header *xsb, STREAMFILE *sf, char *xwb_wavebank_name) { if (xsb->version > XSB_XACT1_2_MAX && xsb->cue_names_size <= 0) { VGM_LOG("XSB: no names found\n"); - return 1; + return true; } /* find target wavebank */ if (xsb->wavebanks_count) { char xsb_wavebank_name[64+1]; - int i; - off_t offset; xsb->selected_wavebank = -1; - offset = xsb->wavebanks_offset; - for (i = 0; i < xsb->wavebanks_count; i++) { + off_t offset = xsb->wavebanks_offset; + for (int i = 0; i < xsb->wavebanks_count; i++) { read_string(xsb_wavebank_name,xsb->wavebanks_name_size, offset, sf); //;VGM_LOG("XSB wavebanks: bank %i\n", i); //, wavebank_name if (strcasecmp(xsb_wavebank_name, xwb_wavebank_name)==0) { @@ -883,9 +881,7 @@ static int parse_xsb(xsb_header *xsb, STREAMFILE *sf, char *xwb_wavebank_name) { parse_xsb_cues(xsb, sf); } - return 1; -fail: - return 0; + return true; } static STREAMFILE * open_xsb_filename_pair(STREAMFILE *streamXwb) { diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream.h b/Frameworks/vgmstream/vgmstream/src/vgmstream.h index 5a7d8c844..1a1f23620 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream.h +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream.h @@ -81,9 +81,6 @@ typedef struct { typedef struct { - int input_channels; - int output_channels; - int32_t pad_begin_duration; int32_t pad_begin_left; int32_t trim_begin_duration;