Add TXTH codec CP_YM [Marvel vs SF (SAT)]

This commit is contained in:
bnnm 2021-10-23 13:16:44 +02:00
parent 79c444c52e
commit 3d88b965d3
8 changed files with 105 additions and 11 deletions

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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) {

View File

@ -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,

View File

@ -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"},

View File

@ -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:

View File

@ -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 */