From 3d88b965d3c74125104f5338808cfebf054758e9 Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 23 Oct 2021 13:16:44 +0200 Subject: [PATCH] Add TXTH codec CP_YM [Marvel vs SF (SAT)] --- doc/TXTH.md | 11 +++++++ doc/USAGE.md | 2 +- src/coding/coding.h | 1 + src/coding/yamaha_decoder.c | 60 +++++++++++++++++++++++++++++++++++++ src/decode.c | 11 +++++++ src/formats.c | 1 + src/meta/txth.c | 29 +++++++++++------- src/vgmstream.h | 1 + 8 files changed, 105 insertions(+), 11 deletions(-) diff --git a/doc/TXTH.md b/doc/TXTH.md index c49c889b..ce17cba9 100644 --- a/doc/TXTH.md +++ b/doc/TXTH.md @@ -157,6 +157,8 @@ as explained below, but often will use default values. Accepted codec strings: # * For rare EA games [Harry Potter and the Chamber of Secrets (PC)] # - XA CD-XA ADPCM (ISO 2048 mode1/data streams without subchannels) # * For rare Saturn and PS2 games [Phantasy Star Collection (SAT), Fantavision (PS2), EA SAT videos] +# - CP_YM Capcom's Yamaha ADPCM +# * For rare Saturn games [Marvel Super Heroes vs Street Fighter (SAT)] codec = (codec string) ``` @@ -1052,6 +1054,15 @@ channels = 2 num_samples = data_size ``` +#### Marvel Super Heroes vs Street Fighter (SAT) .ADP.txth +``` +codec = CP_YM +sample_rate = 24000 +channels = 2 + +#loops are in MM.BIN, table at 0x80700 + id*4 - 0x06018B00 +``` + #### Sega Rally 3 (SAT) ALL_SOUND.txth ``` codec = PCM16LE diff --git a/doc/USAGE.md b/doc/USAGE.md index 886d9b83..ce24c077 100644 --- a/doc/USAGE.md +++ b/doc/USAGE.md @@ -183,7 +183,7 @@ like foobar or Winamp don't react well to that, they may be renamed to these - `.aac` to `.laac` (tri-Ace games) - `.ac3` to `.lac3` (standard AC3) - `.aif` to `.laif` (standard Mac AIF, Asobo AIF, Ogg) -- `.aiff/aifc` to `.laiffl/laifc` (standard Mac AIF) +- `.aiff/aifc` to `.laiff/laifc` (standard Mac AIF) - `.asf` to `.lasf` (EA games, Argonaut ASF) - `.bin` to `.lbin` (various formats) - `.flac` to `.lflac` (standard FLAC) diff --git a/src/coding/coding.h b/src/coding/coding.h index 32fea8f3..a531f5bb 100644 --- a/src/coding/coding.h +++ b/src/coding/coding.h @@ -172,6 +172,7 @@ int msadpcm_check_coefs(STREAMFILE* sf, off_t offset); /* yamaha_decoder */ void decode_aica(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); +void decode_cp_ym(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo); void decode_aska(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, size_t frame_size); void decode_nxap(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do); size_t yamaha_bytes_to_samples(size_t bytes, int channels); diff --git a/src/coding/yamaha_decoder.c b/src/coding/yamaha_decoder.c index 4ba8e91c..ea66088b 100644 --- a/src/coding/yamaha_decoder.c +++ b/src/coding/yamaha_decoder.c @@ -12,6 +12,10 @@ static const int scale_step_adpcmb[16] = { 57, 57, 57, 57, 77, 102, 128, 153, }; +static const int scale_step_capcom[8] = { + 58982, 58982, 58982, 58982, 78643, 104858, 131072, 157286, +}; + /* look-up for 'mul' IMA's sign*((code&7) * 2 + 1) for every code */ static const int scale_delta[16] = { 1, 3, 5, 7, 9, 11, 13, 15, @@ -59,6 +63,31 @@ static void yamaha_aica_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offs *hist1 = sample; } +/* Capcom's version of Yamaha expand */ +static void yamaha_capcom_expand_nibble(uint8_t byte, int shift, int32_t* hist1, int32_t* step_size, int16_t *out_sample) { + int code, ucode, delta, sample; + const int scale = 0x200; /* ADPCM state var, but seemingly fixed */ + + code = (byte >> shift) & 0xf; + ucode = code & 0x7; + delta = (ucode * (*step_size)) >> 2; /* custom (SH2 CPU can't do odd shifts in one op, it seems) */ + if (code & 8) + delta = -delta; + sample = *hist1 + delta; + + sample = (short)sample; /* clamp not done, but output is always low-ish */ + + *step_size = ((*step_size) * scale_step_capcom[ucode]) >> 16; + if (*step_size < 0x80) *step_size = 0x80; /* unlike usual 0x7f */ + else if (*step_size > 0x6000) *step_size = 0x6000; + + *hist1 = sample; + + /* OG code adds out sample, but seems to be always 0 (used for mono downmix?) */ + sample = ((scale * sample) >> 8) /*+ *out_sample */; + *out_sample = sample; +} + /* info about Yamaha ADPCM as created by official yadpcm.acm (in 'Yamaha-ADPCM-ACM-Driver-100-j') * - possibly RIFF codec 0x20 @@ -107,6 +136,37 @@ void decode_aica(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin stream->adpcm_step_index = step_size; } +/* Capcom/Saturn Yamaha ADPCM, reverse engineered from the exe (codec has no apparent name so CP_YM = Capcom Yamaha) */ +void decode_cp_ym(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, int is_stereo) { + int i, sample_count = 0; + int16_t out_sample; + int32_t hist1 = stream->adpcm_history1_16; + int step_size = stream->adpcm_step_index; + + /* no header (external setup), pre-clamp for wrong values */ + if (step_size < 0x80) step_size = 0x80; + if (step_size > 0x6000) step_size = 0x6000; + + for (i = first_sample; i < first_sample + samples_to_do; i++) { + uint8_t byte; + uint32_t offset = is_stereo ? + stream->offset + i : /* stereo: one nibble per channel */ + stream->offset + i/2; /* mono: consecutive nibbles */ + int shift = is_stereo ? + (!(channel&1) ? 0:4) : /* even = low/L, odd = high/R */ + (!(i&1) ? 0:4); /* low nibble first */ + + byte = read_u8(offset, stream->streamfile); + yamaha_capcom_expand_nibble(byte, shift, &hist1, &step_size, &out_sample); + outbuf[sample_count] = out_sample; + sample_count += channelspacing; + } + + stream->adpcm_history1_16 = hist1; + stream->adpcm_step_index = step_size; +} + + /* tri-Ace Aska ADPCM, Yamaha ADPCM-B with headered frames (reversed from Android SO's .so) * implements table with if-else/switchs too but that's too goofy */ void decode_aska(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, size_t frame_size) { diff --git a/src/decode.c b/src/decode.c index 70b23b92..e4e37b4e 100644 --- a/src/decode.c +++ b/src/decode.c @@ -471,6 +471,7 @@ int get_vgmstream_samples_per_frame(VGMSTREAM* vgmstream) { case coding_WS: /* only works if output sample size is 8 bit, which always is for WS ADPCM */ return vgmstream->ws_output_size; case coding_AICA: + case coding_CP_YM: return 1; case coding_AICA_int: return 2; @@ -690,6 +691,7 @@ int get_vgmstream_frame_size(VGMSTREAM* vgmstream) { return vgmstream->current_block_size; case coding_AICA: case coding_AICA_int: + case coding_CP_YM: return 0x01; case coding_ASKA: return vgmstream->frame_size; @@ -1331,6 +1333,15 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_ } break; } + case coding_CP_YM: { + int is_stereo = (vgmstream->channels > 1); + for (ch = 0; ch < vgmstream->channels; ch++) { + decode_cp_ym(&vgmstream->ch[ch], buffer+ch, + vgmstream->channels, vgmstream->samples_into_block, samples_to_do, ch, + is_stereo); + } + break; + } case coding_ASKA: for (ch = 0; ch < vgmstream->channels; ch++) { decode_aska(&vgmstream->ch[ch], buffer+ch, diff --git a/src/formats.c b/src/formats.c index 84c5dacc..64c13b33 100644 --- a/src/formats.c +++ b/src/formats.c @@ -788,6 +788,7 @@ static const coding_info coding_info_list[] = { {coding_WS, "Westwood Studios VBR ADPCM"}, {coding_AICA, "Yamaha AICA 4-bit ADPCM"}, {coding_AICA_int, "Yamaha AICA 4-bit ADPCM (mono/interleave)"}, + {coding_CP_YM, "Capcom Yamaha 4-bit ADPCM"}, {coding_ASKA, "tri-Ace Aska 4-bit ADPCM"}, {coding_NXAP, "Nex NXAP 4-bit ADPCM"}, {coding_TGC, "Tiger Game.com 4-bit ADPCM"}, diff --git a/src/meta/txth.c b/src/meta/txth.c index 79976e6a..7fc4e4c9 100644 --- a/src/meta/txth.c +++ b/src/meta/txth.c @@ -40,7 +40,8 @@ typedef enum { ASF = 30, /* Argonaut ASF 4-bit ADPCM */ EAXA = 31, /* Electronic Arts EA-XA 4-bit ADPCM v1 */ OKI4S = 32, /* OKI ADPCM with 16-bit output (unlike OKI/VOX/Dialogic ADPCM's 12-bit) */ - XA = 33, + XA, + CP_YM, UNKNOWN = 99, } txth_codec_t; @@ -261,6 +262,7 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) { case ASF: coding = coding_ASF; break; case EAXA: coding = coding_EA_XA; break; case XA: coding = coding_XA; break; + case CP_YM: coding = coding_CP_YM; break; default: goto fail; } @@ -373,6 +375,7 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) { case coding_OKI16: case coding_OKI4S: case coding_XA: + case coding_CP_YM: vgmstream->layout_type = layout_none; break; @@ -945,6 +948,7 @@ static txth_codec_t parse_codec(txth_header* txth, const char* val) { else if (is_string(val,"ASF")) return ASF; else if (is_string(val,"EAXA")) return EAXA; else if (is_string(val,"XA")) return XA; + else if (is_string(val,"CP_YM")) return CP_YM; /* special handling */ else if (is_string(val,"name_value")) return txth->name_values[0]; else if (is_string(val,"name_value1")) return txth->name_values[0]; @@ -1582,6 +1586,16 @@ fail: return 0; } +static void string_trim(char* str) { + int str_len = strlen(str); + int i; + for (i = str_len - 1; i >= 0; i--) { + if (str[i] != ' ') + break; + str[i] = '\0'; + } +} + static int read_name_table_keyval(txth_header* txth, const char* line, char* key, char* val) { int ok; int subsong; @@ -1594,8 +1608,10 @@ static int read_name_table_keyval(txth_header* txth, const char* line, char* key return 0; /* try "(name): (val))" */ + ok = sscanf(line, " %[^\t#:] : %[^\t#\r\n] ", key, val); if (ok == 2) { + string_trim(key); /* otherwise includes end spaces before : */ //;VGM_LOG("TXTH: name %s get\n", key); return 1; } @@ -1658,15 +1674,7 @@ static int parse_name_table(txth_header* txth, char* name_list) { goto fail; /* trim name_list just in case */ - { - int name_list_len = strlen(name_list); - int i; - for (i = name_list_len - 1; i >= 0; i--) { - if (name_list[i] != ' ') - break; - name_list[i] = '\0'; - } - } + string_trim(name_list); //;VGM_LOG("TXTH: name_list='%s'\n", name_list); @@ -2031,6 +2039,7 @@ static int get_bytes_to_samples(txth_header* txth, uint32_t bytes) { case DVI_IMA: return ima_bytes_to_samples(bytes, txth->channels); case AICA: + case CP_YM: return yamaha_bytes_to_samples(bytes, txth->channels); case PCFX: case OKI16: diff --git a/src/vgmstream.h b/src/vgmstream.h index ac6ac141..ce20b989 100644 --- a/src/vgmstream.h +++ b/src/vgmstream.h @@ -138,6 +138,7 @@ typedef enum { coding_AICA, /* Yamaha AICA ADPCM (stereo) */ coding_AICA_int, /* Yamaha AICA ADPCM (mono/interleave) */ + coding_CP_YM, /* Capcom's Yamaha ADPCM (stereo/mono) */ coding_ASKA, /* Aska ADPCM */ coding_NXAP, /* NXAP ADPCM */