Add AWC PCM/IMA (non-encrypted only) [GTA5 (PC)]

This commit is contained in:
bnnm 2017-08-28 16:07:09 +02:00
parent 953022b983
commit 41b22c2699
12 changed files with 141 additions and 12 deletions

View File

@ -30,7 +30,8 @@ void decode_ms_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * out
void decode_otns_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_fsb_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_wwise_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do, int channel);
void decode_ref_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel);
void decode_ref_ima(VGMSTREAM * vgmstream, VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do,int channel);
void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do);
size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels);
size_t ima_bytes_to_samples(size_t bytes, int channels);

View File

@ -678,6 +678,43 @@ void decode_ref_ima(VGMSTREAM * vgmstream,VGMSTREAMCHANNEL * stream, sample * ou
stream->adpcm_step_index = step_index;
}
void decode_awc_ima(VGMSTREAMCHANNEL * stream, sample * outbuf, int channelspacing, int32_t first_sample, int32_t samples_to_do) {
int i, sample_count;
int32_t hist1 = stream->adpcm_history1_32;
int step_index = stream->adpcm_step_index;
//internal interleave, mono
int block_samples = (0x800 - 4) * 2;
first_sample = first_sample % block_samples;
//inverted header
if (first_sample == 0) {
off_t header_offset = stream->offset;
step_index = read_16bitLE(header_offset,stream->streamfile);
hist1 = read_16bitLE(header_offset+2,stream->streamfile);
if (step_index < 0) step_index=0;
if (step_index > 88) step_index=88;
}
for (i=first_sample,sample_count=0; i<first_sample+samples_to_do; i++,sample_count+=channelspacing) {
off_t byte_offset = stream->offset + 4 + i/2;
int nibble_shift = (i&1?4:0); //low nibble first
ms_ima_expand_nibble(stream, byte_offset,nibble_shift, &hist1, &step_index);
outbuf[sample_count] = (short)(hist1);
}
//internal interleave: increment offset on complete frame
if (i == block_samples) stream->offset += 0x800;
stream->adpcm_history1_32 = hist1;
stream->adpcm_step_index = step_index;
}
size_t ms_ima_bytes_to_samples(size_t bytes, int block_align, int channels) {
/* MS IMA blocks have a 4 byte header per channel; 2 samples per byte (2 nibbles) */
return (bytes / block_align) * (block_align - 4 * channels) * 2 / channels;

View File

@ -449,6 +449,7 @@ static const coding_info coding_info_list[] = {
{coding_FSB_IMA, "FSB multichannel 4-bit IMA ADPCM"},
{coding_WWISE_IMA, "Audiokinetic Wwise 4-bit IMA ADPCM"},
{coding_REF_IMA, "Reflections 4-bit IMA ADPCM"},
{coding_AWC_IMA, "Rockstar AWC 4-bit IMA ADPCM"},
{coding_WS, "Westwood Studios VBR ADPCM"},
{coding_ACM, "InterPlay ACM"},
{coding_NWA0, "NWA DPCM Level 0"},
@ -533,6 +534,7 @@ static const layout_info layout_info_list[] = {
{layout_aax, "AAX blocked, 18-byte interleaved"},
{layout_scd_int, "SCD multistream interleave"},
{layout_ea_sns_blocked, "Electronic Arts SNS blocked"},
{layout_blocked_awc, "blocked (AWC)"},
#ifdef VGM_USE_VORBIS
{layout_ogg_vorbis, "Ogg"},
#endif

View File

@ -143,6 +143,9 @@ void render_vgmstream_blocked(sample * buffer, int32_t sample_count, VGMSTREAM *
break;
case layout_ea_sns_blocked:
ea_sns_block_update(vgmstream->next_block_offset,vgmstream);
break;
case layout_blocked_awc:
block_update_awc(vgmstream->next_block_offset,vgmstream);
break;
default:
break;

58
src/layout/blocked_awc.c Normal file
View File

@ -0,0 +1,58 @@
#include "layout.h"
#include "../coding/coding.h"
#include "../vgmstream.h"
static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, int channels, int big_endian);
/* AWC music chunks */
void block_update_awc(off_t block_offset, VGMSTREAM * vgmstream) {
STREAMFILE* streamFile = vgmstream->ch[0].streamfile;
int32_t (*read_32bit)(off_t,STREAMFILE*) = vgmstream->codec_endian ? read_32bitBE : read_32bitLE;
size_t header_size, entries, block_size, block_samples;
int i;
/* assumed only AWC_IMA enters here, MPEG/XMA2 need special parsing as blocked layout is too limited */
entries = read_32bit(block_offset + 0x18*0 + 0x04, streamFile); /* assumed same for all channels */
block_samples = entries * (0x800-4)*2;
block_size = vgmstream->full_block_size;
vgmstream->current_block_offset = block_offset;
vgmstream->next_block_offset = block_offset + block_size;
vgmstream->current_block_samples = block_samples;
/* starts with a header block */
/* for each channel
* 0x00: start entry within channel (ie. entries * ch)
* 0x04: entries
* 0x08: samples to discard in the beginning of this block (MPEG only?)
* 0x0c: samples in channel (for MPEG/XMA2 can vary between channels)
* 0x10: MPEG only: close to number of frames but varies a bit?
* 0x14: MPEG only: channel usable data size (not counting padding)
* for each channel
* 32b * entries = global samples per frame in each block (for MPEG probably per full frame)
*/
header_size = get_block_header_size(streamFile, block_offset, vgmstream->channels, vgmstream->codec_endian);
for (i = 0; i < vgmstream->channels; i++) {
vgmstream->ch[i].offset = block_offset + header_size + 0x800*entries*i;
}
}
static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, int channels, int big_endian) {
size_t header_size = 0;
int i;
int entries = channels;
int32_t (*read_32bit)(off_t,STREAMFILE*) = big_endian ? read_32bitBE : read_32bitLE;
for (i = 0; i < entries; i++) {
header_size += 0x18;
header_size += read_32bit(offset + 0x18*i + 0x04, streamFile) * 0x04; /* entries in the table */
}
if (header_size % 0x800) /* padded */
header_size += 0x800 - (header_size % 0x800);
return header_size;
}

View File

@ -65,6 +65,8 @@ void hwas_block_update(off_t block_offset, VGMSTREAM * vgmstream);
void ea_sns_block_update(off_t block_offset, VGMSTREAM * vgmstream);
void block_update_awc(off_t block_ofset, VGMSTREAM * vgmstream);
/* other layouts */
void render_vgmstream_interleave(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstream);

View File

@ -1534,6 +1534,10 @@
RelativePath=".\layout\blocked.c"
>
</File>
<File
RelativePath=".\layout\blocked_awc.c"
>
</File>
<File
RelativePath=".\layout\caf_blocked.c"
>

View File

@ -454,6 +454,7 @@
<ClCompile Include="layout\ast_blocked.c" />
<ClCompile Include="layout\bdsp_blocked.c" />
<ClCompile Include="layout\blocked.c" />
<ClCompile Include="layout\blocked_awc.c" />
<ClCompile Include="layout\caf_blocked.c" />
<ClCompile Include="layout\de2_blocked.c" />
<ClCompile Include="layout\ea_block.c" />

View File

@ -898,6 +898,9 @@
<ClCompile Include="layout\blocked.c">
<Filter>layout\Source Files</Filter>
</ClCompile>
<ClCompile Include="layout\blocked_awc.c">
<Filter>layout\Source Files</Filter>
</ClCompile>
<ClCompile Include="layout\caf_blocked.c">
<Filter>layout\Source Files</Filter>
</ClCompile>

View File

@ -1,5 +1,6 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
typedef struct {
int big_endian;
@ -51,16 +52,19 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
switch(awc.codec) {
//case 0x01: /* PCM (PC/PS3) */
// vgmstream->coding_type = coding_PCM!6;
// vgmstream->layout_type = awc.is_music ? layout_blocked_awc : layout_none;
// break;
//case 0x04: /* IMA (PC) */
// vgmstream->coding_type = coding_AWC_IMA;
// vgmstream->layout_type = awc.is_music ? layout_blocked_awc : layout_none;
// break;
case 0x01: /* PCM (PC/PS3) [sfx, rarely] */
if (awc.is_music) goto fail; /* blocked_awc needs to be prepared */
vgmstream->coding_type = awc.big_endian ? coding_PCM16BE : coding_PCM16LE;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = 0x02;
break;
case 0x04: /* IMA (PC) */
vgmstream->coding_type = coding_AWC_IMA;
vgmstream->layout_type = awc.is_music ? layout_blocked_awc : layout_none;
vgmstream->full_block_size = awc.block_chunk;
vgmstream->codec_endian = awc.big_endian;
break;
#ifdef VGM_USE_MPEG
case 0x07: { /* MPEG (PS3) */
@ -89,8 +93,8 @@ VGMSTREAM * init_vgmstream_awc(STREAMFILE *streamFile) {
if (!vgmstream_open_stream(vgmstream,streamFile,awc.stream_offset))
goto fail;
//if (vgmstream->layout_type == layout_blocked_awc)
// update_
if (vgmstream->layout_type == layout_blocked_awc)
block_update_awc(awc.stream_offset, vgmstream);
return vgmstream;

View File

@ -934,6 +934,7 @@ void render_vgmstream(sample * buffer, int32_t sample_count, VGMSTREAM * vgmstre
case layout_rws_blocked:
case layout_hwas_blocked:
case layout_ea_sns_blocked:
case layout_blocked_awc:
render_vgmstream_blocked(buffer,sample_count,vgmstream);
break;
case layout_interleave_byte:
@ -1053,6 +1054,8 @@ int get_vgmstream_samples_per_frame(VGMSTREAM * vgmstream) {
case coding_WWISE_IMA:
case coding_REF_IMA:
return (vgmstream->interleave_block_size-4*vgmstream->channels)*2/vgmstream->channels;
case coding_AWC_IMA:
return (0x800-4)*2;
case coding_RAD_IMA_mono:
return 32;
case coding_NDS_PROCYON:
@ -1156,6 +1159,8 @@ int get_vgmstream_frame_size(VGMSTREAM * vgmstream) {
case coding_WWISE_IMA:
case coding_REF_IMA:
return vgmstream->interleave_block_size;
case coding_AWC_IMA:
return 0x800;
case coding_RAD_IMA_mono:
return 0x14;
case coding_NGC_DTK:
@ -1653,6 +1658,13 @@ void decode_vgmstream(VGMSTREAM * vgmstream, int samples_written, int samples_to
samples_to_do,chan);
}
break;
case coding_AWC_IMA:
for (chan=0;chan<vgmstream->channels;chan++) {
decode_awc_ima(&vgmstream->ch[chan],buffer+samples_written*vgmstream->channels+chan,
vgmstream->channels,vgmstream->samples_into_block,
samples_to_do);
}
break;
case coding_WS:
for (chan=0;chan<vgmstream->channels;chan++) {

View File

@ -132,6 +132,7 @@ typedef enum {
coding_FSB_IMA, /* FMOD's FSB multichannel IMA ADPCM */
coding_WWISE_IMA, /* Audiokinetic Wwise IMA ADPCM */
coding_REF_IMA, /* Reflections IMA ADPCM */
coding_AWC_IMA, /* Rockstar AWC IMA ADPCM */
coding_MSADPCM, /* Microsoft ADPCM */
coding_WS, /* Westwood Studios VBR ADPCM */
@ -236,6 +237,7 @@ typedef enum {
layout_rws_blocked,
layout_hwas_blocked,
layout_ea_sns_blocked, /* newest Electronic Arts blocks, found in SNS/SNU/SPS/etc formats */
layout_blocked_awc, /* Rockstar AWC */
/* otherwise odd */
layout_acm, /* libacm layout */