Merge pull request #1424 from bnnm/crank

- Fix some ADM3 .wem [The Crew Motorfest (PC)]
- Fix some str+wav [Taz: Wanted (PC)]
- Add ADM3 in .bnk (first subsong only)
This commit is contained in:
bnnm 2023-09-24 15:21:26 +02:00 committed by GitHub
commit 58482731b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 107 additions and 34 deletions

View File

@ -1587,7 +1587,7 @@ different internally (encrypted, different versions, etc) and not always can be
- **bkhd.c**
- Audiokinetic Wwise FX header [*WWISE_FX*]
- *bkhd*: `.bnk`
- Subfiles: *wwise_bnk bkhd_fx*
- Subfiles: *wwise_bnk adm3 bkhd_fx*
- *bkhd_fx*: `.wem .bnk`
- Codecs: PCMFLOAT
- **diva.c**
@ -1739,8 +1739,8 @@ different internally (encrypted, different versions, etc) and not always can be
- **adm.c**
- Crankcase ADMx header [*ADM*]
- *adm2*: `.wem`
- *adm3*: `.wem`
- Codecs: APPLE_IMA4
- *adm3*: `.wem .bnk`
- Codecs: CRANKCASE_IMA APPLE_IMA4
- **tt_ad.c**
- Traveller's Tales AUDIO_DATA header [*TT_AD*]
- *tt_ad*: `.audio_data`

View File

@ -418,7 +418,8 @@ int decode_get_samples_per_frame(VGMSTREAM* vgmstream) {
case coding_XBOX_IMA_int:
case coding_FSB_IMA:
case coding_WWISE_IMA:
case coding_CD_IMA:
case coding_CD_IMA: /* (0x24 - 0x04) * 2 */
case coding_CRANKCASE_IMA: /* (0x23 - 0x3) * 2 */
return 64;
case coding_APPLE_IMA4:
return 64;
@ -654,6 +655,8 @@ int decode_get_frame_size(VGMSTREAM* vgmstream) {
case coding_WWISE_IMA:
case coding_CD_IMA:
return 0x24;
case coding_CRANKCASE_IMA:
return 0x23;
case coding_XBOX_IMA_mch:
case coding_FSB_IMA:
return 0x24 * vgmstream->channels;
@ -1264,6 +1267,12 @@ void decode_vgmstream(VGMSTREAM* vgmstream, int samples_written, int samples_to_
vgmstream->channels, vgmstream->samples_into_block, samples_to_do);
}
break;
case coding_CRANKCASE_IMA:
for (ch = 0; ch < vgmstream->channels; ch++) {
decode_crankcase_ima(&vgmstream->ch[ch], buffer+ch,
vgmstream->channels, vgmstream->samples_into_block, samples_to_do);
}
break;
case coding_WS:
for (ch = 0; ch < vgmstream->channels; ch++) {

View File

@ -45,6 +45,7 @@ void decode_ubi_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspaci
void decode_ubi_sce_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_h4m_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel, uint16_t frame_format);
void decode_cd_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
void decode_crankcase_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
size_t ima_bytes_to_samples(size_t bytes, int channels);
size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels);
size_t xbox_ima_bytes_to_samples(size_t bytes, int channels);

View File

@ -35,8 +35,8 @@ static const int IMA_IndexTable[16] = {
/* Original IMA expansion, using shift+ADDs to avoid MULs (slow back then) */
static void std_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;
static void std_ima_expand_nibble_data(uint8_t byte, int shift, int32_t* hist1, int32_t* index) {
int code, sample, step, delta;
/* simplified through math from:
* - diff = (code + 1/2) * (step / 4)
@ -44,21 +44,26 @@ static void std_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset,
* > diff = (step * nibble / 4) + (step / 8)
* final diff = [signed] (step / 8) + (step / 4) + (step / 2) + (step) [when code = 4+2+1] */
sample_nibble = (read_8bit(byte_offset,stream->streamfile) >> nibble_shift)&0xf; /* ADPCM code */
sample_decoded = *hist1; /* predictor value */
step = ADPCMTable[*step_index]; /* current step */
code = (byte >> shift) & 0xf;
sample = *hist1; /* predictor value */
step = ADPCMTable[*index]; /* current step */
delta = step >> 3;
if (sample_nibble & 1) delta += step >> 2;
if (sample_nibble & 2) delta += step >> 1;
if (sample_nibble & 4) delta += step;
if (sample_nibble & 8) delta = -delta;
sample_decoded += delta;
if (code & 1) delta += step >> 2;
if (code & 2) delta += step >> 1;
if (code & 4) delta += step;
if (code & 8) delta = -delta;
sample += delta;
*hist1 = clamp16(sample_decoded);
*step_index += IMA_IndexTable[sample_nibble];
if (*step_index < 0) *step_index=0;
if (*step_index > 88) *step_index=88;
*hist1 = clamp16(sample);
*index += IMA_IndexTable[code];
if (*index < 0) *index = 0;
if (*index > 88) *index = 88;
}
static void std_ima_expand_nibble(VGMSTREAMCHANNEL * stream, off_t byte_offset, int nibble_shift, int32_t * hist1, int32_t * step_index) {
uint8_t byte = read_u8(byte_offset,stream->streamfile);
std_ima_expand_nibble_data(byte, nibble_shift, hist1, step_index);
}
/* Apple's IMA variation. Exactly the same except it uses 16b history (probably more sensitive to overflow/sign extend?) */
@ -1287,6 +1292,43 @@ void decode_cd_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacin
stream->adpcm_step_index = step_index;
}
/* Crankcase Audio IMA, from libs (internally CrankcaseAudio::ADPCM and revadpcm) */
void decode_crankcase_ima(VGMSTREAMCHANNEL* stream, sample_t* outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
uint8_t frame[0x23] = {0};
int i, frames_in, sample_pos = 0, block_samples, frame_size;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
uint32_t frame_offset;
/* external interleave (fixed size), mono */
frame_size = 0x23;
block_samples = (frame_size - 0x3) * 2;
frames_in = first_sample / block_samples;
first_sample = first_sample % block_samples;
frame_offset = stream->offset + frame_size * frames_in;
read_streamfile(frame, frame_offset, frame_size, stream->streamfile); /* ignore EOF errors */
/* normal header (hist+step), mono */
if (first_sample == 0) {
hist1 = get_s16be(frame + 0x00);
step_index = get_u8(frame + 0x02); /* no reserved value at 0x03 unlike other IMAs (misaligned reads?) */
step_index = _clamp_s32(step_index, 0, 88);
}
/* decode nibbles (layout: straight in mono) */
for (i = first_sample; i < first_sample + samples_to_do; i++) {
int pos = 0x03 + (i/2);
int shift = (i & 1 ? 4:0); /* low first */
std_ima_expand_nibble_data(frame[pos], shift, &hist1, &step_index);
outbuf[sample_pos] = (short)(hist1); /* internally output to float using "sample / 32767.0" */
sample_pos += channelspacing;
}
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
/* ************************************************************* */

View File

@ -831,6 +831,7 @@ static const coding_info coding_info_list[] = {
{coding_UBI_SCE_IMA, "Ubisoft 4-bit SCE IMA ADPCM"},
{coding_H4M_IMA, "Hudson HVQM4 4-bit IMA ADPCM"},
{coding_CD_IMA, "Crystal Dynamics 4-bit IMA ADPCM"},
{coding_CRANKCASE_IMA, "CrankcaseAudio REV 4-bit IMA ADPCM"},
{coding_MSADPCM, "Microsoft 4-bit ADPCM"},
{coding_MSADPCM_int, "Microsoft 4-bit ADPCM (mono/interleave)"},

View File

@ -5,7 +5,8 @@
typedef struct {
int total_subsongs;
int target_subsong;
int version;
int file_version;
int header_version; /* major.minor in hex */
uint32_t stream_offset;
uint32_t stream_size;
@ -18,7 +19,7 @@ typedef struct {
static int parse_adm(adm_header_t* adm, STREAMFILE* sf);
static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int version);
static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int file_version);
/* ADM2 - Crankcase Audio REV plugin file [The Grand Tour Game (PC)] */
VGMSTREAM* init_vgmstream_adm2(STREAMFILE* sf) {
@ -38,26 +39,24 @@ VGMSTREAM* init_vgmstream_adm3(STREAMFILE* sf) {
/* checks */
if (!is_id32be(0x00,sf, "ADM3"))
return NULL;
if (!check_extensions(sf, "wem"))
if (!check_extensions(sf, "wem,bnk"))
return NULL;
return init_vgmstream_adm(sf, 3);
}
static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int version) {
static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int file_version) {
VGMSTREAM* vgmstream = NULL;
adm_header_t adm = {0};
/* ADMx are files used with the Wwise Crankaudio plugin, that simulate engine noises with
* base internal samples and some internal RPM config (probably). Actual file seems to
* define some combo of samples, this only plays those separate samples.
* Decoder is basically Apple's IMA (internally just "ADPCMDecoder") but transforms to float
* each sample during decode by multiplying by 0.000030518509 */
* define some combo of samples, this only plays those separate samples. */
adm.target_subsong = sf->stream_index;
if (adm.target_subsong == 0) adm.target_subsong = 1;
adm.version = version;
adm.file_version = file_version;
if (!parse_adm(&adm, sf))
goto fail;
@ -73,9 +72,21 @@ static VGMSTREAM* init_vgmstream_adm(STREAMFILE* sf, int version) {
vgmstream->num_streams = adm.total_subsongs;
vgmstream->stream_size = adm.stream_size;
vgmstream->coding_type = coding_APPLE_IMA4;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x22;
switch(adm.header_version) {
case 0x00070000: /* The Crew Motorfest (PC) */
vgmstream->coding_type = coding_CRANKCASE_IMA;
//vgmstream->layout_type = layout_interleave;
//vgmstream->interleave_block_size = 0x23;
break;
default: /* The Grand Tour Game (PC) [0x00050000], MotoGP 21 (PC) [0x00060000] */
/* Basically Apple's IMA (internally just "ADPCMDecoder") but transforms to float
* each sample during decode by multiplying by 0.000030518509 */
vgmstream->coding_type = coding_APPLE_IMA4;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x22;
break;
}
if (!vgmstream_open_stream(vgmstream, sf, adm.stream_offset))
goto fail;
@ -169,12 +180,12 @@ static int parse_adm(adm_header_t* adm, STREAMFILE* sf) {
uint32_t offset;
/* 0x04: null */
/* 0x08: version? (ADM2: 0x00050000, ADM3: 0x00060000) */
adm->header_version = read_u32le(0x08, sf); /* ADM2: 0x00050000, ADM3: 0x00060000 (older) / 0x00070000 (2023) */
/* 0x0c: header size */
/* 0x10: data start */
/* rest unknown, looks mostly the same between files (some floats and stuff) */
switch(adm->version) {
switch(adm->file_version) {
case 2:
/* low to high */
offset = read_u32le(0x104, sf);

View File

@ -143,6 +143,11 @@ VGMSTREAM* init_vgmstream_bkhd(STREAMFILE* sf) {
vgmstream = init_vgmstream_wwise_bnk(temp_sf, &prefetch);
if (!vgmstream) goto fail;
}
else if (is_id32be(0x00, temp_sf, "ADM3")) {
// TODO: these may have multiple subsongs
vgmstream = init_vgmstream_adm3(temp_sf);
if (!vgmstream) goto fail;
}
else if (read_f32(subfile_offset + 0x02, temp_sf) >= 30.0 &&
read_f32(subfile_offset + 0x02, temp_sf) <= 250.0) {
is_wmid = 1;

View File

@ -57,8 +57,6 @@ static const fsbkey_info fsbkey_list[] = {
{ MODE_FSB4_STD, FSBKEY_ADD("ghfxhslrghfxhslr") }, // Cookie Run: Ovenbreak
{ MODE_FSB4_ALT, FSBKEY_ADD("truck/impact/carbody") },// Monster Jam (PS2) [FSB3]
{ MODE_FSB4_ALT, FSBKEY_ADD("\xFC\xF9\xE4\xB3\xF5\x57\x5C\xA5\xAC\x13\xEC\x4A\x43\x19\x58\xEB\x4E\xF3\x84\x0B\x8B\x78\xFA\xFD\xBB\x18\x46\x7E\x31\xFB\xD0") }, // Guitar Hero 5 (X360)
{ MODE_FSB4_ALT, FSBKEY_ADD("\x8C\xFA\xF3\x14\xB1\x53\xDA\xAB\x2B\x82\x6B\xD5\x55\x16\xCF\x01\x90\x20\x28\x14\xB1\x53\xD8") }, // Guitar Hero: Metallica (X360)
{ MODE_FSB4_STD, FSBKEY_ADD("\xd2\x37\x70\x39\xa9\x86\xc5\xaf\x5b\x7f\xa2\x23\x98\x7e\xb6\xc2\x7e\x18\x7b\x2d\xd9\x31\x4b\x20\xb0\xc1\x8d\x06\xf2\xa7\xcd") }, // Guitar Hero: Metallica (PS3) [FSB4]
{ MODE_FSB5_STD, FSBKEY_ADD("G0KTrWjS9syqF7vVD6RaVXlFD91gMgkC") }, // Sekiro: Shadows Die Twice (PC)
{ MODE_FSB5_STD, FSBKEY_ADD("BasicEncryptionKey") }, // SCP: Unity (PC)
{ MODE_FSB5_STD, FSBKEY_ADD("FXnTffGJ9LS855Gc") }, // Worms Rumble Beta (PC)
@ -73,6 +71,11 @@ static const fsbkey_info fsbkey_list[] = {
{ MODE_FSB5_STD, FSBKEY_ADD("Aurogon666") }, // Afterimage demo (PC)
{ MODE_FSB5_STD, FSBKEY_ADD("IfYouLikeThosesSoundsWhyNotRenumerateTheir2Authors?") }, // Blanc (PC/Switch)
{ MODE_FSB5_STD, FSBKEY_ADD("L36nshM520") }, // Nishuihan Mobile (Android)
/* these games use a key per file, seemingly generated from the filename; could be possible to add them but there is a lot of songs,
so external .fsbkey may be better (use guessfsb 3.1 with --write-key-file) */
//{ MODE_FSB4_STD, FSBKEY_ADD("...") }, // Guitar Hero: Metallica (PC/PS3/X360) [FSB4]
//{ MODE_FSB4_STD, FSBKEY_ADD("...") }, // Guitar Hero: World Tour (PC/PS3/X360) [FSB4]
};
static const int fsbkey_list_count = sizeof(fsbkey_list) / sizeof(fsbkey_list[0]);

View File

@ -286,7 +286,7 @@ static int parse_header(STREAMFILE* sf_h, STREAMFILE* sf_b, strwav_header* strwa
/* 0x10c: header size */
strwav->codec = IMA;
strwav->interleave = strwav->tracks > 1 ? 0x8000 : 0x10000;
strwav->interleave = strwav->tracks > 1 ? 0x10000 : 0x10000;
;VGM_LOG("STR+WAV: header TAZd (PC)\n");
return 1;
}

View File

@ -90,6 +90,7 @@ typedef enum {
coding_H4M_IMA, /* H4M IMA ADPCM (stereo or mono, high nibble first) */
coding_MTF_IMA, /* Capcom MT Framework IMA ADPCM */
coding_CD_IMA, /* Crystal Dynamics IMA ADPCM */
coding_CRANKCASE_IMA, /* CrankcaseAudio REV IMA ADPCM */
coding_MSADPCM, /* Microsoft ADPCM (stereo/mono) */
coding_MSADPCM_int, /* Microsoft ADPCM (mono) */