mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-31 04:13:47 +01:00
Add TXTH codec CP_YM [Marvel vs SF (SAT)]
This commit is contained in:
parent
79c444c52e
commit
3d88b965d3
11
doc/TXTH.md
11
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
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
11
src/decode.c
11
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,
|
||||
|
@ -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"},
|
||||
|
@ -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:
|
||||
|
@ -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 */
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user