From 42977ce5bb5bf0ab125699e1d2375317a0438e64 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sat, 5 Jul 2025 02:43:55 -0700 Subject: [PATCH] VGMStream: Updated libvgmstream code base Updated VGMStream to r2023-35-ge417e033 Signed-off-by: Christopher Snowhill --- .../libvgmstream.xcodeproj/project.pbxproj | 4 -- .../vgmstream/vgmstream/src/base/decode.c | 2 +- .../vgmstream/src/coding/ima_decoder.c | 46 ++++++++++------- .../vgmstream/src/coding/tantalus_decoder.c | 32 +++++------- Frameworks/vgmstream/vgmstream/src/formats.c | 2 +- .../vgmstream/vgmstream/src/meta/hca_keys.h | 6 +++ .../vgmstream/vgmstream/src/meta/meta.h | 2 - .../vgmstream/vgmstream/src/meta/piff_tpcm.c | 22 +++++--- .../vgmstream/vgmstream/src/meta/raw_snds.c | 51 ------------------- .../vgmstream/vgmstream/src/meta/txth.c | 37 ++++++++++++++ .../vgmstream/vgmstream/src/vgmstream_init.c | 1 - 11 files changed, 100 insertions(+), 105 deletions(-) delete mode 100644 Frameworks/vgmstream/vgmstream/src/meta/raw_snds.c diff --git a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj index 8499b2c5b..e32ea229d 100644 --- a/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj +++ b/Frameworks/vgmstream/libvgmstream.xcodeproj/project.pbxproj @@ -658,7 +658,6 @@ 837CEAFC23487F2C00E62A4A /* raw_wavm.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE723487F2B00E62A4A /* raw_wavm.c */; }; 837CEAFD23487F2C00E62A4A /* psf.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAE823487F2B00E62A4A /* psf.c */; }; 837CEAFE23487F2C00E62A4A /* jstm_streamfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 837CEAE923487F2B00E62A4A /* jstm_streamfile.h */; }; - 837CEAFF23487F2C00E62A4A /* raw_snds.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAEA23487F2B00E62A4A /* raw_snds.c */; }; 837CEB0023487F2C00E62A4A /* smk.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAEB23487F2B00E62A4A /* smk.c */; }; 837CEB0123487F2C00E62A4A /* xmu.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAEC23487F2C00E62A4A /* xmu.c */; }; 837CEB0223487F2C00E62A4A /* raw_int.c in Sources */ = {isa = PBXBuildFile; fileRef = 837CEAED23487F2C00E62A4A /* raw_int.c */; }; @@ -1618,7 +1617,6 @@ 837CEAE723487F2B00E62A4A /* raw_wavm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw_wavm.c; sourceTree = ""; }; 837CEAE823487F2B00E62A4A /* psf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = psf.c; sourceTree = ""; }; 837CEAE923487F2B00E62A4A /* jstm_streamfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jstm_streamfile.h; sourceTree = ""; }; - 837CEAEA23487F2B00E62A4A /* raw_snds.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw_snds.c; sourceTree = ""; }; 837CEAEB23487F2B00E62A4A /* smk.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = smk.c; sourceTree = ""; }; 837CEAEC23487F2C00E62A4A /* xmu.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xmu.c; sourceTree = ""; }; 837CEAED23487F2C00E62A4A /* raw_int.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = raw_int.c; sourceTree = ""; }; @@ -2617,7 +2615,6 @@ 837CEADD23487F2A00E62A4A /* raw_pcm.c */, 836F6EE718BDC2190095E648 /* raw_rsf.c */, 836F6EEA18BDC2190095E648 /* raw_s14_sss.c */, - 837CEAEA23487F2B00E62A4A /* raw_snds.c */, 837CEAE723487F2B00E62A4A /* raw_wavm.c */, 836F6EE218BDC2190095E648 /* redspark.c */, 834FE0D9215C79EA000A5D3D /* rfrm.c */, @@ -3609,7 +3606,6 @@ 834F7E842C709F5B003AC386 /* apa3.c in Sources */, 834FE106215C79ED000A5D3D /* utk.c in Sources */, 8373342B23F60CDC00DE14DC /* fwse.c in Sources */, - 837CEAFF23487F2C00E62A4A /* raw_snds.c in Sources */, 83031EDC243C510500C3F3E0 /* vid1.c in Sources */, 834F7DD02C7093EA003AC386 /* icelib.c in Sources */, 83AA5D271F6E2F9C0020821C /* stma.c in Sources */, diff --git a/Frameworks/vgmstream/vgmstream/src/base/decode.c b/Frameworks/vgmstream/vgmstream/src/base/decode.c index a9f80e5f6..313e3add3 100644 --- a/Frameworks/vgmstream/vgmstream/src/base/decode.c +++ b/Frameworks/vgmstream/vgmstream/src/base/decode.c @@ -474,6 +474,7 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) { case coding_OKI16: case coding_OKI4S: case coding_MTF_IMA: + case coding_SNDS_IMA: return 0x01; case coding_RAD_IMA: case coding_NDS_IMA: @@ -487,7 +488,6 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) { return 0x800; case coding_RAD_IMA_mono: return 0x14; - case coding_SNDS_IMA: case coding_QD_IMA: return 0; //todo: 0x01? case coding_UBI_IMA: /* variable (PCM then IMA) */ diff --git a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c index a4f158a22..a8b114ebc 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/ima_decoder.c @@ -145,24 +145,27 @@ static void camelot_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offs if (*step_index > 88) *step_index=88; } -/* The Incredibles PC, updates step_index before doing current sample */ -static void snds_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; +/* The Incredibles PC, updates step_index before doing current sample, reverse engineered from the .exe + * (has no apparent name, files are raw data with .WAV extension but are inside a 'SNDS' folder). + * A few voices show slight drifting but tables and algo look fine, encoder issue? */ +static void snds_ima_expand_nibble(VGMSTREAMCHANNEL* stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) { + int sample, step, delta; - sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; - sample_decoded = *hist1; + uint8_t code = (read_u8(byte_offset,stream->streamfile) >> nibble_shift)&0xf; + sample = *hist1; - *step_index += ima_index_table[sample_nibble]; - if (*step_index < 0) *step_index=0; - if (*step_index > 88) *step_index=88; + int code_pos = code & 7; + *step_index += ima_index_table[code_pos]; //OG table doesn't have negative indexes + if (*step_index < 0) *step_index = 0; + if (*step_index > 88) *step_index = 88; step = ima_step_size_table[*step_index]; - delta = (sample_nibble & 7) * step / 4 + step / 8; /* standard IMA */ - if (sample_nibble & 8) delta = -delta; - sample_decoded += delta; + delta = (step >> 3) + ((step * code_pos) >> 2); + if (code & 8) delta = -delta; + sample += delta; - *hist1 = clamp16(sample_decoded); + *hist1 = clamp16(sample); } /* Omikron: The Nomad Soul, algorithm from the .exe */ @@ -440,20 +443,27 @@ void decode_camelot_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channe } void decode_snds_ima(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel) { - int i, sample_count; - int32_t hist1 = stream->adpcm_history1_32; - int step_index = stream->adpcm_step_index; + bool is_stereo = channelspacing > 1; + + int32_t hist1 = stream->adpcm_history1_32; // starts at 0 + int step_index = stream->adpcm_step_index; // starts at 0 //external interleave //no header - for (i=first_sample,sample_count=0; ioffset + i;//one nibble per channel - int nibble_shift = (channel==0?0:4); //high nibble first, based on channel + int sample_count = 0; + for (int i = first_sample; i < first_sample + samples_to_do; i++) { + off_t byte_offset = is_stereo ? + stream->offset + i : // stereo: one nibble per channel + stream->offset + i/2; // mono: consecutive nibbles + int nibble_shift = is_stereo ? + ((channel&1) ? 4:0) : //high nibble first + ((i&1) ? 4:0); snds_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index); outbuf[sample_count] = (short)(hist1); + sample_count += channelspacing; } stream->adpcm_history1_32 = hist1; diff --git a/Frameworks/vgmstream/vgmstream/src/coding/tantalus_decoder.c b/Frameworks/vgmstream/vgmstream/src/coding/tantalus_decoder.c index 109164c1a..deb712ad5 100644 --- a/Frameworks/vgmstream/vgmstream/src/coding/tantalus_decoder.c +++ b/Frameworks/vgmstream/vgmstream/src/coding/tantalus_decoder.c @@ -2,13 +2,13 @@ /* Decodes Tantalus TADC ADPCM codec, used in Saturn games. - * Guessed based on other XA-style codecs values. */ + * Reverse engineered from the exe (@ 0x06086d2 w/ 0x06010000 base address) */ void decode_tantalus(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) { uint8_t frame[0x10] = {0}; off_t frame_offset; - int i, frames_in, sample_count = 0; + int frames_in, sample_count = 0; size_t bytes_per_frame, samples_per_frame; - int shift, filter, coef1, coef2; + int32_t hist1 = stream->adpcm_history1_32; int32_t hist2 = stream->adpcm_history2_32; @@ -22,39 +22,31 @@ void decode_tantalus(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspac /* parse frame header */ frame_offset = stream->offset + bytes_per_frame*frames_in; read_streamfile(frame, frame_offset, bytes_per_frame, stream->streamfile); /* ignore EOF errors */ - filter = (frame[0x00] >> 4) & 0xf; /* 0 in tested files */ - shift = (frame[0x00] >> 0) & 0xf; - if (filter != 0) { - VGM_LOG_ONCE("TANTALUS: unknown filter\n"); - coef1 = 64; - coef2 = 64; /* will sound horrid and hopefully reported */ - } - else { - coef1 = 64; - coef2 = 0; - } + + // there is no XA filter select code but upper 4 bits seem to be always 0 + int shift = (frame[0x00] >> 0); /* decode nibbles */ - for (i = first_sample; i < first_sample + samples_to_do; i++) { + for (int i = first_sample; i < first_sample + samples_to_do; i++) { uint8_t nibbles = frame[0x01 + i/2]; int32_t sample; - sample = i&1 ? /* low nibble first */ + int8_t code = i&1 ? /* low nibble first */ get_high_nibble_signed(nibbles) : get_low_nibble_signed(nibbles); - sample = sample << (shift + 6); - sample = (sample + (hist1 * coef1) + (hist2 * coef2)) >> 6; + + // calc is done via [code][shift] = delta LUT in OG code (perhaps because SH2 can only do 1/2/4 shifts) + // basically equivalent to an XA codec with coef1 = 1.0, coef2 = 0.0 + sample = hist1 + (code << shift); outbuf[sample_count] = clamp16(sample); sample_count += channelspacing; - hist2 = hist1; hist1 = sample; } stream->adpcm_history1_32 = hist1; - stream->adpcm_history2_32 = hist2; } int32_t tantalus_bytes_to_samples(size_t bytes, int channels) { diff --git a/Frameworks/vgmstream/vgmstream/src/formats.c b/Frameworks/vgmstream/vgmstream/src/formats.c index f7f3e9939..fdfd5b332 100644 --- a/Frameworks/vgmstream/vgmstream/src/formats.c +++ b/Frameworks/vgmstream/vgmstream/src/formats.c @@ -543,7 +543,7 @@ static const char* extension_list[] = { "sn0", "snb", "snd", - "snds", + "snds", //fake extension for .wav (renamed, to be removed) "sng", "sngw", "snr", diff --git a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h index 8a769888e..d6961f354 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/hca_keys.h @@ -1566,6 +1566,12 @@ static const hcakey_info hcakey_list[] = { // CHUNITHM Chinese Version (AC) {30194896045700459}, // 006B461914D5756B + // Raidou Remastered: The Mystery of the Soulless Army (PS4) + {954534454324}, // 000000DE3EAFE434 + + // Sand Land (multi) + {910990237314908160}, // 0CA47CCB51010000 + }; #endif diff --git a/Frameworks/vgmstream/vgmstream/src/meta/meta.h b/Frameworks/vgmstream/vgmstream/src/meta/meta.h index 8742ba2ba..f503f1c81 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/meta.h +++ b/Frameworks/vgmstream/vgmstream/src/meta/meta.h @@ -482,8 +482,6 @@ VGMSTREAM * init_vgmstream_lsf_n1nj4n(STREAMFILE* streamFile); VGMSTREAM * init_vgmstream_xwav_new(STREAMFILE* sf); VGMSTREAM * init_vgmstream_xwav_old(STREAMFILE* sf); -VGMSTREAM * init_vgmstream_raw_snds(STREAMFILE* streamFile); - VGMSTREAM * init_vgmstream_hyperscan_kvag(STREAMFILE* streamFile); VGMSTREAM* init_vgmstream_psnd(STREAMFILE* sf); diff --git a/Frameworks/vgmstream/vgmstream/src/meta/piff_tpcm.c b/Frameworks/vgmstream/vgmstream/src/meta/piff_tpcm.c index c1c1b4bef..cf42939b4 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/piff_tpcm.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/piff_tpcm.c @@ -10,19 +10,27 @@ VGMSTREAM* init_vgmstream_piff_tpcm(STREAMFILE* sf) { /* checks */ + if (!is_id32be(0x00,sf, "PIFF")) + return NULL; + /* .tad: from internal filenames */ if (!check_extensions(sf, "tad")) - goto fail; - /* Tantalus also has PIFF without this */ - if (!is_id32be(0x00,sf, "PIFF") || !is_id32be(0x08,sf, "TPCM") || !is_id32be(0x0c,sf, "TADH")) - goto fail; + return NULL; + + uint32_t piff_size = read_s32le(0x04,sf); + if (piff_size + 0x08 != get_streamfile_size(sf)) + return NULL; + + // Tantalus also has PIFF without this + if (!is_id32be(0x08,sf, "TPCM") || !is_id32be(0x0c,sf, "TADH")) + return NULL; header_offset = 0x14; - /* 0x00: 1? */ - /* 0x01: 1? */ + // 0x00: 1? + // 0x01: 1? channels = read_u16le(header_offset + 0x02,sf); sample_rate = read_s32le(header_offset + 0x04,sf); - /* 0x08+: ? (mostly fixed, maybe related to ADPCM?) */ + // 0x08+: ? (mostly fixed, maybe related to ADPCM?) loop_flag = 0; if (!is_id32be(0x38,sf, "BODY")) diff --git a/Frameworks/vgmstream/vgmstream/src/meta/raw_snds.c b/Frameworks/vgmstream/vgmstream/src/meta/raw_snds.c deleted file mode 100644 index 6cf68c0c5..000000000 --- a/Frameworks/vgmstream/vgmstream/src/meta/raw_snds.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "meta.h" - - -/* .snds - from Heavy Iron's The Incredibles (PC) */ -VGMSTREAM * init_vgmstream_raw_snds(STREAMFILE *streamFile) { - VGMSTREAM * vgmstream = NULL; - off_t start_offset; - int loop_flag, channel_count; - size_t file_size; - int i; - - - /* checks */ - if (!check_extensions(streamFile, "snds")) - goto fail; - - loop_flag = 0; - channel_count = 2; - start_offset = 0; - file_size = get_streamfile_size(streamFile); - - - /* build the VGMSTREAM */ - vgmstream = allocate_vgmstream(channel_count, loop_flag); - if (!vgmstream) goto fail; - - vgmstream->meta_type = meta_RAW_SNDS; - vgmstream->sample_rate = 48000; - - /* file seems to be mistakenly 1/8 too long, check for 32 0 bytes where the padding should start */ - vgmstream->num_samples = file_size*8/9; - for (i = 0; i < 8; i++) { - if (read_32bitBE(vgmstream->num_samples+i*4,streamFile) != 0) { - vgmstream->num_samples = file_size; /* no padding? just play the whole file */ - break; - } - } - - vgmstream->coding_type = coding_SNDS_IMA; - vgmstream->layout_type = layout_none; - - - if (!vgmstream_open_stream(vgmstream,streamFile,start_offset)) - goto fail; - return vgmstream; - -fail: - close_vgmstream(vgmstream); - return NULL; -} - diff --git a/Frameworks/vgmstream/vgmstream/src/meta/txth.c b/Frameworks/vgmstream/vgmstream/src/meta/txth.c index b582c04c2..71ca916fe 100644 --- a/Frameworks/vgmstream/vgmstream/src/meta/txth.c +++ b/Frameworks/vgmstream/vgmstream/src/meta/txth.c @@ -58,6 +58,7 @@ typedef enum { ULAW, ALAW, DPCM_KCEJ, + IMA_SNDS, UNKNOWN = 255, } txth_codec_t; @@ -273,6 +274,7 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) { case SDX2: coding = coding_SDX2; break; case DVI_IMA: coding = coding_DVI_IMA; break; case IMA_HV: coding = coding_HV_IMA; break; + case IMA_SNDS: coding = coding_SNDS_IMA; break; #ifdef VGM_USE_MPEG case MPEG: coding = coding_MPEG_layer3; break; /* we later find out exactly which */ #endif @@ -1056,6 +1058,7 @@ static txth_codec_t parse_codec(txth_header* txth, const char* val) { else if (is_string(val,"CP_YM")) return CP_YM; else if (is_string(val,"PCM_FLOAT_LE")) return PCM_FLOAT_LE; else if (is_string(val,"IMA_HV")) return IMA_HV; + else if (is_string(val,"IMA_SNDS")) return IMA_SNDS; else if (is_string(val,"HEVAG")) return HEVAG; else if (is_string(val,"ULAW")) return ULAW; else if (is_string(val,"ALAW")) return ALAW; @@ -2268,6 +2271,7 @@ static int get_bytes_to_samples(txth_header* txth, uint32_t bytes) { case IMA: case DVI_IMA: case IMA_HV: + case IMA_SNDS: return ima_bytes_to_samples(bytes, txth->channels); case AICA: case YMZ: @@ -2293,6 +2297,34 @@ static int get_bytes_to_samples(txth_header* txth, uint32_t bytes) { } } +//TODO move +static uint32_t find_padding(STREAMFILE* sf, uint32_t start_offset, uint32_t data_size) { + uint8_t buf[0x2000]; + uint32_t read_size = sizeof(buf); + + int32_t offset = start_offset + data_size; + while (offset > start_offset) { + int32_t read_offset = offset - read_size; + if (read_offset < 0) + read_offset = 0; + + int bytes = read_streamfile(buf, read_offset, read_size, sf); + while (bytes >= 0) { + bytes--; + + if (buf[bytes] != 0) { + uint32_t last_offset = start_offset + data_size; + uint32_t curr_offset = read_offset + bytes + 1; + return last_offset - curr_offset; + } + } + + offset -= read_size; + } + + return 0; +} + static int get_padding_size(txth_header* txth, int discard_empty) { if (txth->data_size == 0 || txth->channels == 0) return 0; @@ -2300,6 +2332,11 @@ static int get_padding_size(txth_header* txth, int discard_empty) { switch(txth->codec) { case PSX: return ps_find_padding(txth->sf_body, txth->start_offset, txth->data_size, txth->channels, txth->interleave, discard_empty); + + case IMA_SNDS: { + // several files seems to be 1/8 too long + return find_padding(txth->sf_body, txth->start_offset, txth->data_size); + } default: return 0; } diff --git a/Frameworks/vgmstream/vgmstream/src/vgmstream_init.c b/Frameworks/vgmstream/vgmstream/src/vgmstream_init.c index 59d15bd95..fdfe82b40 100644 --- a/Frameworks/vgmstream/vgmstream/src/vgmstream_init.c +++ b/Frameworks/vgmstream/vgmstream/src/vgmstream_init.c @@ -554,7 +554,6 @@ init_vgmstream_t init_vgmstream_functions[] = { init_vgmstream_raw_rsf, /* raw GC streamed files */ init_vgmstream_raw_int, /* .int raw PCM */ init_vgmstream_ps_headerless, /* tries to detect a bunch of PS-ADPCM formats */ - init_vgmstream_raw_snds, /* .snds raw SNDS IMA */ init_vgmstream_raw_wavm, /* .wavm raw xbox */ init_vgmstream_raw_pcm, /* .raw raw PCM */ init_vgmstream_raw_s14_sss, /* .s14/sss raw siren14 */