Implement Tiger Game.com ADPCM

This commit is contained in:
Simon Aarons 2020-01-11 21:52:24 +11:00
parent b2043c0e5b
commit 5106ccba68
10 changed files with 106 additions and 1 deletions

View File

@ -137,6 +137,9 @@ void decode_nxap(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int channelspacin
size_t yamaha_bytes_to_samples(size_t bytes, int channels);
size_t aska_bytes_to_samples(size_t bytes, int channels);
/* tgcadpcm_decoder */
void decode_tgc(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int32_t first_sample, int32_t samples_to_do);
/* nds_procyon_decoder */
void decode_nds_procyon(VGMSTREAMCHANNEL *stream, sample_t *outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);

View File

@ -0,0 +1,42 @@
#include "coding.h"
/* Decodes SunPlus' ADPCM codec used on the Tiger Game.com.
* Reverse engineered from the Game.com's BIOS. */
static uint16_t slopeTable[64] =
{
0x0000, 0x0100, 0x0200, 0x0400, 0x0610, 0x0810, 0x0C18, 0x1020,
0x0100, 0x0300, 0x0508, 0x0908, 0x0D18, 0x1118, 0x1920, 0x2128,
0x0208, 0x0508, 0x0810, 0x0E10, 0x1420, 0x1A20, 0x2628, 0x3230,
0x0310, 0x0710, 0x0B18, 0x1318, 0x1B28, 0x2328, 0x2930, 0x4338,
0x0418, 0x0918, 0x0E20, 0x1820, 0x2230, 0x2C30, 0x4038, 0x5438,
0x0520, 0x0B20, 0x1128, 0x1D28, 0x2938, 0x3538, 0x4D38, 0x6F38,
0x0628, 0x0D28, 0x1430, 0x2230, 0x3038, 0x3E38, 0x5A38, 0x7638,
0x0730, 0x0F30, 0x1738, 0x2738, 0x3738, 0x4738, 0x6738, 0x7D38
};
void decode_tgc(VGMSTREAMCHANNEL * stream, sample_t * outbuf, int32_t first_sample, int32_t samples_to_do)
{
for (int i = first_sample, sample_count = 0; i < first_sample + samples_to_do; i++, sample_count++)
{
uint8_t samp = ((uint8_t)read_8bit(i/2, stream->streamfile) >>
(i & 1 ? 4 : 0)) & 0xf;
uint8_t slopeIndex = stream->adpcm_scale | (samp >> 1);
stream->adpcm_step_index = slopeTable[slopeIndex] >> 8;
stream->adpcm_scale = slopeTable[slopeIndex] & 0xff;
stream->adpcm_history1_16 += (samp & 1) ?
-stream->adpcm_step_index:
stream->adpcm_step_index;
if (stream->adpcm_history1_16 < 0)
stream->adpcm_history1_16 = 0;
if (stream->adpcm_history1_16 > 0xff)
stream->adpcm_history1_16 = 0xff;
outbuf[sample_count] = stream->adpcm_history1_16 * 0x100 - 0x8000;
}
}

View File

@ -25,6 +25,7 @@ static const char* extension_list[] = {
"208",
"2dx9",
"2pfs",
"4",
"8", //txth/reserved [Gungage (PS1)]
"800",
"9tav",
@ -177,6 +178,7 @@ static const char* extension_list[] = {
"gbts",
"gca",
"gcm",
"gcomadpcm",
"gcub",
"gcw",
"genh",
@ -474,6 +476,7 @@ static const char* extension_list[] = {
"sxd3",
"tec",
"tgcadpcm",
"tgq",
"thp",
"tk5",
@ -718,6 +721,7 @@ static const coding_info coding_info_list[] = {
{coding_AICA_int, "Yamaha AICA 4-bit ADPCM (mono/interleave)"},
{coding_ASKA, "tri-Ace Aska 4-bit ADPCM"},
{coding_NXAP, "Nex NXAP 4-bit ADPCM"},
{coding_TGC, "Tiger Game.com 4-bit ADPCM"},
{coding_NDS_PROCYON, "Procyon Studio Digital Sound Elements NDS 4-bit APDCM"},
{coding_L5_555, "Level-5 0x555 4-bit ADPCM"},
{coding_LSF, "lsf 4-bit ADPCM"},
@ -1255,6 +1259,7 @@ static const meta_info meta_info_list[] = {
{meta_XSSB, "Artoon XSSB header"},
{meta_XMA_UE3, "Unreal Engine XMA header"},
{meta_FDA, "Relic FDA header"},
{meta_TGC, "Tiger Game.com .4 header"},
};

View File

@ -196,6 +196,7 @@
<ClCompile Include="meta\mtaf.c" />
<ClCompile Include="meta\ps2_spm.c" />
<ClCompile Include="meta\rad.c" />
<ClCompile Include="meta\tgc.c" />
<ClCompile Include="meta\vs_str.c" />
<ClCompile Include="meta\ps2_wmus.c" />
<ClCompile Include="meta\ivag.c" />
@ -224,6 +225,7 @@
<ClCompile Include="plugins.c" />
<ClCompile Include="meta\ps2_va3.c" />
<ClCompile Include="streamfile.c" />
<ClCompile Include="coding\tgcadpcm_decoder.c" />
<ClCompile Include="util.c" />
<ClCompile Include="vgmstream.c" />
<ClCompile Include="meta\208.c" />

View File

@ -1669,5 +1669,11 @@
<ClCompile Include="meta\xmv_valve.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\tgcadpcm_decoder.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="meta\tgc.c">
<Filter>meta\Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -879,4 +879,6 @@ VGMSTREAM* init_vgmstream_csb(STREAMFILE *sf);
VGMSTREAM* init_vgmstream_fda(STREAMFILE *sf);
VGMSTREAM * init_vgmstream_tgc(STREAMFILE *streamFile);
#endif /*_META_H*/

30
src/meta/tgc.c Normal file
View File

@ -0,0 +1,30 @@
#include "meta.h"
#include "../coding/coding.h"
/* Tiger Game.com ADPCM file */
VGMSTREAM * init_vgmstream_tgc(STREAMFILE *streamFile) {
VGMSTREAM * vgmstream = NULL;
/* checks */
if (!check_extensions(streamFile, "gcomadpcm,tgcadpcm,4"))
goto fail;
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(1, 0);
if (!vgmstream) goto fail;
vgmstream->sample_rate = 8000;
vgmstream->num_samples = (read_16bitBE(1, streamFile) - 3) * 2;
vgmstream->meta_type = meta_TGC;
vgmstream->layout_type = layout_none;
vgmstream->coding_type = coding_TGC;
if (!vgmstream_open_stream(vgmstream, streamFile, 3))
goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
return NULL;
}

View File

@ -36,6 +36,7 @@ typedef enum {
PCM4_U = 26, /* 4-bit unsigned PCM (3rd and 4th gen games) */
OKI16 = 27, /* OKI ADPCM with 16-bit output (unlike OKI/VOX/Dialogic ADPCM's 12-bit) */
AAC = 28, /* Advanced Audio Coding (raw without .mp4) */
TGC = 29 /* Tiger Game.com 4-bit ADPCM */
} txth_type;
typedef struct {
@ -221,6 +222,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
case PCM4: coding = coding_PCM4; break;
case PCM4_U: coding = coding_PCM4_U; break;
case OKI16: coding = coding_OKI16; break;
case TGC: coding = coding_TGC; break;
default:
goto fail;
}
@ -266,6 +268,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
case coding_IMA:
case coding_AICA:
case coding_APPLE_IMA4:
case coding_TGC:
vgmstream->interleave_block_size = txth.interleave;
vgmstream->interleave_last_block_size = txth.interleave_last;
if (vgmstream->channels > 1)
@ -843,6 +846,8 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
else if (is_string(val,"PCM4_U")) txth->codec = PCM4_U;
else if (is_string(val,"OKI16")) txth->codec = OKI16;
else if (is_string(val,"AAC")) txth->codec = AAC;
else if (is_string(val,"TGC")) txth->codec = TGC;
else if (is_string(val,"GCOM_ADPCM")) txth->codec = TGC;
else goto fail;
/* set common interleaves to simplify usage
@ -1698,6 +1703,7 @@ static int get_bytes_to_samples(txth_header * txth, uint32_t bytes) {
return pcm_bytes_to_samples(bytes, txth->channels, 8);
case PCM4:
case PCM4_U:
case TGC:
return pcm_bytes_to_samples(bytes, txth->channels, 4);
case MSADPCM:
return msadpcm_bytes_to_samples(bytes, txth->interleave, txth->channels);

View File

@ -487,6 +487,7 @@ VGMSTREAM * (*init_vgmstream_functions[])(STREAMFILE *streamFile) = {
init_vgmstream_xma_ue3,
init_vgmstream_csb,
init_vgmstream_fda,
init_vgmstream_tgc,
/* lowest priority metas (should go after all metas, and TXTH should go before raw formats) */
init_vgmstream_txth, /* proper parsers should supersede TXTH, once added */
@ -2031,6 +2032,12 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
vgmstream->channels,vgmstream->samples_into_block,samples_to_do);
}
break;
case coding_TGC:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_tgc(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,
vgmstream->samples_into_block,samples_to_do);
}
break;
case coding_NDS_PROCYON:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_nds_procyon(&vgmstream->ch[ch],buffer+samples_written*vgmstream->channels+ch,

View File

@ -154,6 +154,8 @@ typedef enum {
coding_ASKA, /* Aska ADPCM */
coding_NXAP, /* NXAP ADPCM */
coding_TGC, /* Tiger Game.com 4-bit ADPCM */
coding_NDS_PROCYON, /* Procyon Studio ADPCM */
coding_L5_555, /* Level-5 0x555 ADPCM */
coding_LSF, /* lsf ADPCM (Fastlane Street Racing iPhone)*/
@ -720,7 +722,7 @@ typedef enum {
meta_XSSB,
meta_XMA_UE3,
meta_FDA,
meta_TGC,
} meta_t;
/* standard WAVEFORMATEXTENSIBLE speaker positions */