mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-28 08:20:54 +01:00
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:
commit
58482731b5
@ -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`
|
||||
|
@ -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++) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/* ************************************************************* */
|
||||
|
||||
|
@ -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)"},
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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]);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) */
|
||||
|
Loading…
Reference in New Issue
Block a user