Merge pull request #1522 from bnnm/rage-awc

- Add RAGE aud MPEG [GTA IV (PS3)]
- Fix some MPEG .awc [GTA V (PS3)]
This commit is contained in:
bnnm 2024-05-03 14:54:27 +02:00 committed by GitHub
commit c39d1cad92
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 246 additions and 320 deletions

View File

@ -482,7 +482,6 @@ typedef enum {
MPEG_EAL32P, /* EALayer3 v2 "PCM", custom frames with v2 header + bigger PCM blocks? */ MPEG_EAL32P, /* EALayer3 v2 "PCM", custom frames with v2 header + bigger PCM blocks? */
MPEG_EAL32S, /* EALayer3 v2 "Spike", custom frames with v2 header + smaller PCM blocks? */ MPEG_EAL32S, /* EALayer3 v2 "Spike", custom frames with v2 header + smaller PCM blocks? */
MPEG_LYN, /* N streams of fixed interleave */ MPEG_LYN, /* N streams of fixed interleave */
MPEG_AWC, /* N streams in block layout (music) or absolute offsets (sfx) */
MPEG_EAMP3 /* custom frame header + MPEG frame + PCM blocks */ MPEG_EAMP3 /* custom frame header + MPEG frame + PCM blocks */
} mpeg_custom_t; } mpeg_custom_t;
@ -519,7 +518,8 @@ int mpeg_get_sample_rate(mpeg_codec_data* data);
long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data); long mpeg_bytes_to_samples(long bytes, const mpeg_codec_data* data);
uint32_t mpeg_get_tag_size(STREAMFILE* sf, uint32_t offset, uint32_t header); uint32_t mpeg_get_tag_size(STREAMFILE* sf, uint32_t offset, uint32_t header);
int mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info); bool mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info);
bool mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info* info);
int test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey); int test_ahx_key(STREAMFILE* sf, off_t offset, crikey_t* crikey);
#endif #endif

View File

@ -8,7 +8,7 @@ int mpeg_custom_setup_init_default(STREAMFILE* sf, off_t start_offset, mpeg_code
/* get frame info at offset */ /* get frame info at offset */
if ( !mpeg_get_frame_info(sf, start_offset, &info)) if (!mpeg_get_frame_info(sf, start_offset, &info))
goto fail; goto fail;
switch(info.layer) { switch(info.layer) {
case 1: *coding_type = coding_MPEG_layer1; break; case 1: *coding_type = coding_MPEG_layer1; break;
@ -264,7 +264,7 @@ fail:
* Gets info from a MPEG frame header at offset. Normally you would use mpg123_info but somehow * Gets info from a MPEG frame header at offset. Normally you would use mpg123_info but somehow
* it's wrong at times (maybe because we use an ancient version) so here we do our thing. * it's wrong at times (maybe because we use an ancient version) so here we do our thing.
*/ */
static int mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info* info) { bool mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info* info) {
/* index tables */ /* index tables */
static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 }; static const int versions[4] = { /* MPEG 2.5 */ 3, /* reserved */ -1, /* MPEG 2 */ 2, /* MPEG 1 */ 1 };
static const int layers[4] = { -1,3,2,1 }; static const int layers[4] = { -1,3,2,1 };
@ -330,12 +330,12 @@ static int mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info* info) {
default: goto fail; default: goto fail;
} }
return 1; return true;
fail: fail:
return 0; return false;
} }
int mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info) { bool mpeg_get_frame_info(STREAMFILE* sf, off_t offset, mpeg_frame_info* info) {
uint32_t header = read_u32be(offset, sf); uint32_t header = read_u32be(offset, sf);
return mpeg_get_frame_info_h(header, info); return mpeg_get_frame_info_h(header, info);
} }

View File

@ -1,184 +0,0 @@
#include "mpeg_decoder.h"
#ifdef VGM_USE_MPEG
/**
* AWC music uses blocks (sfx doesn't), the fun part being each channel has different num_samples/frames
* per block, so it's unsuitable for the normal "blocked" layout and parsed here instead.
* Channel data is separate within the block (first all frames of ch0, then ch1, etc), padded, and sometimes
* the last few frames of a channel are repeated in the new block (marked with the "discard samples" field).
*/
/* block header size, algined/padded to 0x800 */
static size_t get_block_header_size(STREAMFILE *streamFile, off_t offset, mpeg_codec_data *data) {
size_t header_size = 0;
int i;
int entries = data->config.channels;
int32_t (*read_32bit)(off_t,STREAMFILE*) = data->config.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;
}
/* find data that repeats in the beginning of a new block at the end of last block */
static size_t get_repeated_data_size(STREAMFILE *streamFile, off_t new_offset, off_t last_offset) {
uint8_t new_frame[0x1000];/* buffer to avoid fseek back and forth */
mpeg_frame_info info;
off_t off;
int i;
/* read block first frame */
if ( !mpeg_get_frame_info(streamFile, new_offset, &info))
goto fail;
if (info.frame_size > 0x1000)
goto fail;
if (read_streamfile(new_frame,new_offset, info.frame_size,streamFile) != info.frame_size)
goto fail;
/* find the frame in last bytes of prev block */
off = last_offset - 0x4000; /* typical max is 5-10 frames of ~0x200, no way to know exact size */
while (off < last_offset) {
/* compare frame vs prev block data */
for (i = 0; i < info.frame_size; i++) {
if ((uint8_t)read_8bit(off+i,streamFile) != new_frame[i])
break;
}
/* frame fully compared? */
if (i == info.frame_size)
return last_offset - off;
else
off += i+1;
}
fail:
VGM_LOG("AWC: can't find repeat size, new=0x%08x, last=0x%08x\n", (uint32_t)new_offset, (uint32_t)last_offset);
return 0; /* keep on truckin' */
}
/* init config and validate */
int mpeg_custom_setup_init_awc(STREAMFILE *streamFile, off_t start_offset, mpeg_codec_data *data, coding_t *coding_type) {
mpeg_frame_info info;
int is_music;
/* start_offset can point to a block header that always starts with 0 (music) or normal data (sfx) */
is_music = read_32bitBE(start_offset, streamFile) == 0x00000000;
if (is_music)
start_offset += get_block_header_size(streamFile, start_offset, data);
/* get frame info at offset */
if ( !mpeg_get_frame_info(streamFile, start_offset, &info))
goto fail;
switch(info.layer) {
case 1: *coding_type = coding_MPEG_layer1; break;
case 2: *coding_type = coding_MPEG_layer2; break;
case 3: *coding_type = coding_MPEG_layer3; break;
default: goto fail;
}
data->channels_per_frame = info.channels;
data->samples_per_frame = info.frame_samples;
/* extra checks */
if (is_music) {
if (data->config.chunk_size <= 0)
goto fail; /* needs block size */
}
/* no big encoder delay added (for sfx they can start in less than ~300 samples) */
return 1;
fail:
return 0;
}
/* writes data to the buffer and moves offsets, parsing AWC blocks */
int mpeg_custom_parse_frame_awc(VGMSTREAMCHANNEL *stream, mpeg_codec_data *data, int num_stream) {
mpeg_custom_stream *ms = data->streams[num_stream];
mpeg_frame_info info;
size_t current_data_size = 0, data_offset;
size_t file_size = get_streamfile_size(stream->streamfile);
int i;
/* blocked layout used for music */
if (data->config.chunk_size) {
off_t last_offset = stream->offset; /* when block end needs to be known */
/* block ended for this channel, move to next block start */
if (ms->current_size_count > 0 && ms->current_size_count == ms->current_size_target) {
//mpg123_open_feed(ms->m); //todo reset maybe needed?
data_offset = stream->offset - stream->channel_start_offset; /* ignoring header */
data_offset -= data_offset % data->config.chunk_size; /* start of current block */
stream->offset = stream->channel_start_offset + data_offset + data->config.chunk_size;
ms->current_size_count = 0;
ms->current_size_target = 0;
}
/* just in case, shouldn't happen */
if (stream->offset >= file_size) {
goto fail;
}
/* block starts for this channel, point to mpeg data */
if (ms->current_size_count == 0) {
int32_t (*read_32bit)(off_t,STREAMFILE*) = data->config.big_endian ? read_32bitBE : read_32bitLE;
off_t channel_offset = 0;
/* block has a header with base info per channel and table per channel (see blocked_awc.c) */
ms->decode_to_discard = read_32bit(stream->offset + 0x18*num_stream + 0x08, stream->streamfile);
ms->current_size_target = read_32bit(stream->offset + 0x18*num_stream + 0x14, stream->streamfile);
for (i = 0; i < num_stream; i++) { /* num_stream serves as channel */
size_t channel_size = read_32bit(stream->offset + 0x18*i + 0x14, stream->streamfile);
if (channel_size % 0x10) /* 32b aligned */
channel_size += 0x10 - channel_size % 0x10;
channel_offset += channel_size;
}
//;VGM_ASSERT(ms->decode_to_discard > 0, "AWC: s%i discard of %x found at chunk %lx\n", num_stream, ms->decode_to_discard, stream->offset);
stream->offset += channel_offset + get_block_header_size(stream->streamfile, stream->offset, data);
/* A new block may repeat frame bytes from prev block, and decode_to_discard has the number of repeated samples.
* However in RDR PS3 (not GTA5?) the value can be off (ie. discards 1152 but the repeat decodes to ~1152*4).
* I can't figure out why, so just find and skip the repeat data manually (probably better for mpg123 too) */
if (ms->decode_to_discard) {
size_t repeat = get_repeated_data_size(stream->streamfile, stream->offset, last_offset);
if (repeat > 0)
ms->decode_to_discard = 0;
stream->offset += repeat;
ms->current_size_target -= repeat;
}
}
}
/* update frame */
if ( !mpeg_get_frame_info(stream->streamfile, stream->offset, &info) )
goto fail;
current_data_size = info.frame_size;
ms->bytes_in_buffer = read_streamfile(ms->buffer,stream->offset, current_data_size, stream->streamfile);
stream->offset += current_data_size;
ms->current_size_count += current_data_size;
return 1;
fail:
return 0;
}
#endif

View File

@ -113,9 +113,9 @@ fail:
/* Init custom MPEG, with given type and config */ /* Init custom MPEG, with given type and config */
mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t* coding_type, int channels, mpeg_custom_t type, mpeg_custom_config* config) { mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t* coding_type, int channels, mpeg_custom_t type, mpeg_custom_config* cfg) {
mpeg_codec_data* data = NULL; mpeg_codec_data* data = NULL;
int i, ok; int ok;
/* init codec */ /* init codec */
data = calloc(1, sizeof(mpeg_codec_data)); data = calloc(1, sizeof(mpeg_codec_data));
@ -124,7 +124,8 @@ mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t*
/* keep around to decode */ /* keep around to decode */
data->custom = 1; data->custom = 1;
data->type = type; data->type = type;
memcpy(&data->config, config, sizeof(mpeg_custom_config)); if (cfg)
memcpy(&data->config, cfg, sizeof(mpeg_custom_config));
data->config.channels = channels; data->config.channels = channels;
data->default_buffer_size = MPEG_DATA_BUFFER_SIZE; data->default_buffer_size = MPEG_DATA_BUFFER_SIZE;
@ -135,7 +136,6 @@ mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t*
case MPEG_EAL31b: case MPEG_EAL31b:
case MPEG_EAL32P: case MPEG_EAL32P:
case MPEG_EAL32S: ok = mpeg_custom_setup_init_ealayer3(sf, start_offset, data, coding_type); break; case MPEG_EAL32S: ok = mpeg_custom_setup_init_ealayer3(sf, start_offset, data, coding_type); break;
case MPEG_AWC: ok = mpeg_custom_setup_init_awc(sf, start_offset, data, coding_type); break;
case MPEG_EAMP3: ok = mpeg_custom_setup_init_eamp3(sf, start_offset, data, coding_type); break; case MPEG_EAMP3: ok = mpeg_custom_setup_init_eamp3(sf, start_offset, data, coding_type); break;
default: ok = mpeg_custom_setup_init_default(sf, start_offset, data, coding_type); break; default: ok = mpeg_custom_setup_init_default(sf, start_offset, data, coding_type); break;
} }
@ -161,7 +161,7 @@ mpeg_codec_data* init_mpeg_custom(STREAMFILE* sf, off_t start_offset, coding_t*
data->streams_size += 1; data->streams_size += 1;
data->streams = calloc(data->streams_size, sizeof(mpeg_custom_stream*)); data->streams = calloc(data->streams_size, sizeof(mpeg_custom_stream*));
for (i = 0; i < data->streams_size; i++) { for (int i = 0; i < data->streams_size; i++) {
data->streams[i] = calloc(1, sizeof(mpeg_custom_stream)); data->streams[i] = calloc(1, sizeof(mpeg_custom_stream));
data->streams[i]->m = init_mpg123_handle(); /* decoder not shared as may need several frames to decode)*/ data->streams[i]->m = init_mpg123_handle(); /* decoder not shared as may need several frames to decode)*/
if (!data->streams[i]->m) goto fail; if (!data->streams[i]->m) goto fail;
@ -409,7 +409,6 @@ static void decode_mpeg_custom_stream(VGMSTREAMCHANNEL* stream, mpeg_codec_data*
case MPEG_EAL32P: case MPEG_EAL32P:
case MPEG_EAL32S: ok = mpeg_custom_parse_frame_ealayer3(stream, data, num_stream); break; case MPEG_EAL32S: ok = mpeg_custom_parse_frame_ealayer3(stream, data, num_stream); break;
case MPEG_AHX: ok = mpeg_custom_parse_frame_ahx(stream, data, num_stream); break; case MPEG_AHX: ok = mpeg_custom_parse_frame_ahx(stream, data, num_stream); break;
case MPEG_AWC: ok = mpeg_custom_parse_frame_awc(stream, data, num_stream); break;
case MPEG_EAMP3: ok = mpeg_custom_parse_frame_eamp3(stream, data, num_stream); break; case MPEG_EAMP3: ok = mpeg_custom_parse_frame_eamp3(stream, data, num_stream); break;
default: ok = mpeg_custom_parse_frame_default(stream, data, num_stream); break; default: ok = mpeg_custom_parse_frame_default(stream, data, num_stream); break;
} }

View File

@ -251,7 +251,6 @@
<ClCompile Include="coding\mp4_aac_decoder.c" /> <ClCompile Include="coding\mp4_aac_decoder.c" />
<ClCompile Include="coding\mpeg_custom_utils.c" /> <ClCompile Include="coding\mpeg_custom_utils.c" />
<ClCompile Include="coding\mpeg_custom_utils_ahx.c" /> <ClCompile Include="coding\mpeg_custom_utils_ahx.c" />
<ClCompile Include="coding\mpeg_custom_utils_awc.c" />
<ClCompile Include="coding\mpeg_custom_utils_ealayer3.c" /> <ClCompile Include="coding\mpeg_custom_utils_ealayer3.c" />
<ClCompile Include="coding\mpeg_custom_utils_eamp3.c" /> <ClCompile Include="coding\mpeg_custom_utils_eamp3.c" />
<ClCompile Include="coding\mpeg_decoder.c" /> <ClCompile Include="coding\mpeg_decoder.c" />

View File

@ -574,9 +574,6 @@
<ClCompile Include="coding\mpeg_custom_utils_ahx.c"> <ClCompile Include="coding\mpeg_custom_utils_ahx.c">
<Filter>coding\Source Files</Filter> <Filter>coding\Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="coding\mpeg_custom_utils_awc.c">
<Filter>coding\Source Files</Filter>
</ClCompile>
<ClCompile Include="coding\mpeg_custom_utils_ealayer3.c"> <ClCompile Include="coding\mpeg_custom_utils_ealayer3.c">
<Filter>coding\Source Files</Filter> <Filter>coding\Source Files</Filter>
</ClCompile> </ClCompile>

View File

@ -6,9 +6,10 @@
#include "awc_decryption_streamfile.h" #include "awc_decryption_streamfile.h"
typedef struct { typedef struct {
int big_endian; bool big_endian;
int is_encrypted; bool is_encrypted;
int is_streamed; /* implicit: streams=music, sfx=memory */ bool is_streamed; /* implicit: streams=music, sfx=memory */
bool is_alt;
int total_subsongs; int total_subsongs;
@ -115,15 +116,17 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
#ifdef VGM_USE_MPEG #ifdef VGM_USE_MPEG
case 0x07: { /* MPEG (PS3) */ case 0x07: { /* MPEG (PS3) */
mpeg_custom_config cfg = {0}; if (awc.is_streamed) {
vgmstream->layout_data = build_layered_awc(sf_body, &awc);
cfg.chunk_size = awc.block_chunk; if (!vgmstream->layout_data) goto fail;
cfg.big_endian = awc.big_endian; vgmstream->layout_type = layout_layered;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->codec_data = init_mpeg_custom(sf_body, awc.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_AWC, &cfg); }
else {
vgmstream->codec_data = init_mpeg_custom(sf_body, awc.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, NULL);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
}
break; break;
} }
#endif #endif
@ -280,7 +283,7 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
/* flags = 8b (always FF) + 8b (actual flags) + 16b (version, 00=rarely, 01=common) */ /* flags = 8b (always FF) + 8b (actual flags) + 16b (version, 00=rarely, 01=common) */
if ((flags & 0xFF00FFFF) != 0xFF000001 || (flags & 0x00F00000)) { if ((flags & 0xFF00FFFF) != 0xFF000001 || (flags & 0x00F00000)) {
VGM_LOG("AWC: unknown flags 0x%08x\n", flags); VGM_LOG("AWC: unknown flags 0x%08x\n", flags);
goto fail; return false;
} }
/* stream tag starts (ex. stream#0 = 0, stream#1 = 4, stream#2 = 7: to read tags from stream#2 skip to 7th tag) */ /* stream tag starts (ex. stream#0 = 0, stream#1 = 4, stream#2 = 7: to read tags from stream#2 skip to 7th tag) */
@ -288,17 +291,16 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
offset += 0x2 * entries; offset += 0x2 * entries;
/* seems to indicate chunks are not ordered (ie. header structures from tags may go after data), usually for non-streams */ /* seems to indicate chunks are not ordered (ie. header structures from tags may go after data), usually for non-streams */
//if (flags % 0x00020000) //if (flags & 0x00020000)
// awc->is_unordered = 1; // awc->is_unordered = true;
/* stream/multichannel flag (rare, GTA5/RDR2) */ /* stream/multichannel flag? (GTA5, some RDR2), can be used to detect some odd behavior in GTA5 vs RDR1 */
//if (flags % 0x00040000) if (flags & 0x00040000)
// awc->is_multichannel = 1; awc->is_alt = true;
/* encrypted data chunk (most of GTA5 PC for licensed audio) */ /* encrypted data chunk (most of GTA5 PC for licensed audio) */
if (flags & 0x00080000) if (flags & 0x00080000)
awc->is_encrypted = 1; awc->is_encrypted = true;
/* When first stream hash/id is 0 AWC it has fake entry with info for all channels = music, sfx pack otherwise. /* When first stream hash/id is 0 AWC it has fake entry with info for all channels = music, sfx pack otherwise.
* sfx = N single streams, music = N interleaved mono channels (even for MP3/XMA/Vorbis/etc). * sfx = N single streams, music = N interleaved mono channels (even for MP3/XMA/Vorbis/etc).
@ -439,12 +441,12 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
} }
awc->num_samples = read_u32(tag_offset + 0x00,sf); awc->num_samples = read_u32(tag_offset + 0x00,sf);
/* 0x04: -1? */ /* 0x04: -1? (loop related?) */
awc->sample_rate = read_u16(tag_offset + 0x08,sf); awc->sample_rate = read_u16(tag_offset + 0x08,sf);
/* 0x0a: headroom */ /* 0x0a: headroom */
/* 0x0c: unknown */ /* 0x0c: unknown (loop related?) */
/* 0x0e: unknown */ /* 0x0e: unknown (loop related?) */
/* 0x10: unknown */ /* 0x10: unknown (loop related?) */
/* 0x12: null? */ /* 0x12: null? */
awc->codec = read_u8(tag_offset + 0x13, sf); awc->codec = read_u8(tag_offset + 0x13, sf);
/* 0x14: ? (PS3 only, for any codec) */ /* 0x14: ? (PS3 only, for any codec) */
@ -561,7 +563,7 @@ static VGMSTREAM* build_blocks_vgmstream(STREAMFILE* sf, awc_header* awc, int ch
/* setup custom IO streamfile that removes AWC's odd blocks (not perfect but serviceable) */ /* setup custom IO streamfile that removes AWC's odd blocks (not perfect but serviceable) */
temp_sf = setup_awc_streamfile(sf, awc->stream_offset, awc->stream_size, awc->block_chunk, awc->channels, channel, awc->codec, awc->big_endian); temp_sf = setup_awc_streamfile(sf, awc->stream_offset, awc->stream_size, awc->block_chunk, awc->channels, channel, awc->codec, awc->big_endian, awc->is_alt);
if (!temp_sf) goto fail; if (!temp_sf) goto fail;
substream_offset = 0x00; substream_offset = 0x00;
@ -591,6 +593,14 @@ static VGMSTREAM* build_blocks_vgmstream(STREAMFILE* sf, awc_header* awc, int ch
break; break;
} }
#endif #endif
#ifdef VGM_USE_MPEG
case 0x07: { /* MPEG (PS3) */
vgmstream->codec_data = init_mpeg_custom(temp_sf, substream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, NULL);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
break;
}
#endif
#ifdef VGM_USE_VORBIS #ifdef VGM_USE_VORBIS
case 0x08: { case 0x08: {
vorbis_custom_config cfg = {0}; vorbis_custom_config cfg = {0};

View File

@ -4,20 +4,22 @@
#include "../util/endianness.h" #include "../util/endianness.h"
#define AWC_MAX_MUSIC_CHANNELS 32 /* seen ~24 */ #define AWC_MAX_MUSIC_CHANNELS 32 /* seen ~24 */
#define AWC_FRAME_SIZE 0x800
/* ************************************************************************* */ /* ************************************************************************* */
typedef struct { typedef struct {
int start_entry; /* innacurate! */ int start_entry; /* inaccurate! */
int entries; int entries;
int32_t channel_skip; int32_t channel_skip;
int32_t channel_samples; int32_t channel_samples;
uint32_t channel_size; /* size of this channel's data (not including padding) */
uint32_t frame_size; uint32_t frame_size;
/* derived */ /* derived */
uint32_t chunk_start; /* relative to block offset */ uint32_t chunk_start; /* relative to block offset */
uint32_t chunk_size; /* size of this channel's data (not including padding) */ uint32_t chunk_size; /* size of this channel's data (may include padding) */
} awc_block_t; } awc_block_t;
typedef struct { typedef struct {
@ -36,24 +38,26 @@ typedef struct {
* - usually there is padding between channels or blocks (usually 0s but seen 0x97 in AT9) * - usually there is padding between channels or blocks (usually 0s but seen 0x97 in AT9)
* *
* Header format: * Header format:
* - per channel (frame start table) * - channel info (per channel)
* 0x00: start entry for that channel? (-1 in vorbis) * 0x00: start entry/frame for that channel? (-1 in vorbis, innacurate in MPEG/AT9, ? in XMA)
* may be off by +1/+2? * Unlike RAGE-aud, this value isn't useful to calculate data offsets and possibly just used for seek table.
* ex. on block 0, ch0/1 have 0x007F frames, a start entry is: ch0=0x0000, ch1=0x007F (MP3) * ex. on block 0, ch0/1 have 0x02A9 frames, start entry is: ch0=0x0000, ch1=0x02AA (AT9) !! (would be 1 frame into ch2)
* ex. on block 0, ch0/1 have 0x02A9 frames, a start entry is: ch0=0x0000, ch1=0x02AA (AT9) !! * 0x04: entries/frames in this channel (may be different between channels)
* (sum of all values from all channels may go beyond all posible frames, no idea) * This refers to 1 logical chunk of N sub-frames
* 0x04: frames in this channel (may be different between channels) * MPEG padding works differently vs RAGE-aud too.
* 'frames' here may be actual single decoder frames or a chunk of frames
* 0x08: samples to discard in the beginning of this block (MPEG/XMA2/Vorbis only?) * 0x08: samples to discard in the beginning of this block (MPEG/XMA2/Vorbis only?)
* When this is set, channel repeats XMA/MPEG super-frames from prev block. However discard doesn't seem to match
* repeated samples, and skip value may be smaller (ex. just 1152 skip samples but repeats make 4608 samples).
* So maybe they just skip data like we do below and don't actually use this value.
* 0x0c: samples in channel (for MPEG/XMA2 can vary between channels) * 0x0c: samples in channel (for MPEG/XMA2 can vary between channels)
* full samples without removing samples to discard * full samples without removing samples to discard
* (next fields only exists for MPEG, Vorbis or some IMA versions) * (next fields only exists for MPEG, Vorbis or some IMA versions)
* 0x10: (MPEG only, empty otherwise) close to number of frames but varies a bit? * 0x10: (MPEG only, empty otherwise) close to number of frames but varies a bit?
* 0x14: (MPEG only, empty otherwise) channel chunk size (not counting padding) * 0x14: (MPEG only, empty otherwise) channel chunk size (not counting padding)
* - for each channel (seek table) * - seek table (entries for all channels, 1 per frame)
* 32b * entries = global samples per frame in each block (for MPEG probably per full frame) * 0x00: global samples per frame in each block (for MPEG probably per full frame)
* (AT9 doesn't have a seek table as it's CBR) * (AT9 doesn't have a seek table as it's CBR)
* - per channel (ATRAC9/DSP extra info): * - extra info (per channel, ATRAC9/DSP ony):
* 0x00: "D11A" * 0x00: "D11A"
* 0x04: frame size * 0x04: frame size
* 0x06: frame samples * 0x06: frame samples
@ -61,11 +65,7 @@ typedef struct {
* 0x0a: sample rate * 0x0a: sample rate
* 0x0c: ATRAC9 config (repeated but same for all blocks) or "D11E" (DSP) * 0x0c: ATRAC9 config (repeated but same for all blocks) or "D11E" (DSP)
* 0x10-0x70: padding with 0x77 (ATRAC3) or standard DSP header for original full file (DSP) * 0x10-0x70: padding with 0x77 (ATRAC3) or standard DSP header for original full file (DSP)
* - padding until channel data start, depending on codec (DSP/ATRAC9: one, others: aligned to 0x800) * - padding up to data start, depending on codec (DSP/ATRAC9: none, others: aligned to 0x800)
* - per channel:
* 0xNN: channel frames
* 0xNN: may have padding between channels depending on codec (mainly MPEG/XMA)
* - padding until this block's end
*/ */
static bool read_awc_block(STREAMFILE* sf, awc_block_info_t* bi) { static bool read_awc_block(STREAMFILE* sf, awc_block_info_t* bi) {
read_s32_t read_s32 = bi->big_endian ? read_s32be : read_s32le; read_s32_t read_s32 = bi->big_endian ? read_s32be : read_s32le;
@ -74,6 +74,7 @@ static bool read_awc_block(STREAMFILE* sf, awc_block_info_t* bi) {
uint32_t channel_entry_size, seek_entry_size, extra_entry_size, header_padding; uint32_t channel_entry_size, seek_entry_size, extra_entry_size, header_padding;
uint32_t offset = bi->block_offset; uint32_t offset = bi->block_offset;
int channels = bi->channels; int channels = bi->channels;
/* read stupid block crap + derived info at once so hopefully it's a bit easier to understand */ /* read stupid block crap + derived info at once so hopefully it's a bit easier to understand */
switch(bi->codec) { switch(bi->codec) {
@ -83,6 +84,7 @@ static bool read_awc_block(STREAMFILE* sf, awc_block_info_t* bi) {
extra_entry_size = 0x00; extra_entry_size = 0x00;
header_padding = 0x800; header_padding = 0x800;
break; break;
case 0x07: /* MPEG */
case 0x08: /* Vorbis */ case 0x08: /* Vorbis */
channel_entry_size = 0x18; channel_entry_size = 0x18;
seek_entry_size = 0x04; seek_entry_size = 0x04;
@ -96,7 +98,7 @@ static bool read_awc_block(STREAMFILE* sf, awc_block_info_t* bi) {
header_padding = 0x00; header_padding = 0x00;
break; break;
default: default:
goto fail; return false;
} }
/* channel info table */ /* channel info table */
@ -105,7 +107,9 @@ static bool read_awc_block(STREAMFILE* sf, awc_block_info_t* bi) {
bi->blk[ch].entries = read_s32(offset + 0x04, sf); bi->blk[ch].entries = read_s32(offset + 0x04, sf);
bi->blk[ch].channel_skip = read_s32(offset + 0x08, sf); bi->blk[ch].channel_skip = read_s32(offset + 0x08, sf);
bi->blk[ch].channel_samples = read_s32(offset + 0x0c, sf); bi->blk[ch].channel_samples = read_s32(offset + 0x0c, sf);
/* others: optional */ if (bi->codec == 0x07) { /* MPEG */
bi->blk[ch].channel_size = read_s32(offset + 0x14, sf);
}
offset += channel_entry_size; offset += channel_entry_size;
} }
@ -123,17 +127,22 @@ static bool read_awc_block(STREAMFILE* sf, awc_block_info_t* bi) {
/* each 'frame'/entry in Vorbis is actually N vorbis frames then padding up to 0x800 /* each 'frame'/entry in Vorbis is actually N vorbis frames then padding up to 0x800
* (more or less like a big Ogg page or XMA 'frame'). Padding is considered part of * (more or less like a big Ogg page or XMA 'frame'). Padding is considered part of
* the data and handled by the decoder, since sfx (non-blocked) algo have it. */ * the data and handled by the decoder, since sfx (non-blocked) algo have it. */
bi->blk[ch].frame_size = 0x800; bi->blk[ch].frame_size = AWC_FRAME_SIZE;
bi->blk[ch].chunk_size = bi->blk[ch].entries * bi->blk[ch].frame_size; bi->blk[ch].chunk_size = bi->blk[ch].entries * bi->blk[ch].frame_size;
bi->blk[ch].channel_size = bi->blk[ch].chunk_size;
break;
case 0x07: /* MPEG */
bi->blk[ch].frame_size = AWC_FRAME_SIZE; /* approximate but not used like RAGE-aud */
bi->blk[ch].chunk_size = align_size_to_block(bi->blk[ch].channel_size, 0x10);
//bi->blk[ch].channel_size = (pre-loaded);
break; break;
case 0x0F: /* ATRAC9 */ case 0x0F: /* ATRAC9 */
bi->blk[ch].frame_size = read_u16(offset + 0x04, sf); bi->blk[ch].frame_size = read_u16(offset + 0x04, sf);
bi->blk[ch].chunk_size = bi->blk[ch].entries * bi->blk[ch].frame_size; bi->blk[ch].chunk_size = bi->blk[ch].entries * bi->blk[ch].frame_size;
bi->blk[ch].channel_size = bi->blk[ch].chunk_size;
break; break;
default: default:
goto fail; return false;
} }
offset += extra_entry_size; offset += extra_entry_size;
} }
@ -154,14 +163,12 @@ static bool read_awc_block(STREAMFILE* sf, awc_block_info_t* bi) {
/* beyond this is padding until chunk_start */ /* beyond this is padding until chunk_start */
return true; return true;
fail:
return false;
} }
/* Find data that repeats in the beginning of a new block at the end of last block. /* Find data that repeats in the beginning of a new block at the end of last block.
* When a new block starts there is some repeated data + channel_skip (for seeking + encoder delay?). * When a new block starts there is some repeated data + channel_skip (for seeking + encoder delay?).
* Detect it so decoder may ignore it. */ * Detect it so decoder may ignore it. */
static uint32_t get_block_repeated_size(STREAMFILE* sf, awc_block_info_t* bi, int channel) { static uint32_t get_block_repeated_size(STREAMFILE* sf, awc_block_info_t* bi, int channel, bool is_alt) {
if (bi->blk[channel].channel_skip == 0) if (bi->blk[channel].channel_skip == 0)
return 0; return 0;
@ -172,6 +179,44 @@ static uint32_t get_block_repeated_size(STREAMFILE* sf, awc_block_info_t* bi, in
/* when data repeats seems to clone the last (super-)frame */ /* when data repeats seems to clone the last (super-)frame */
return bi->blk[channel].frame_size; return bi->blk[channel].frame_size;
case 0x07: { /* MPEG */
/* first super-frame will repeat N VBR old sub-frames, without crossing frame_size.
* In GTA5 repeated sub-frames seems to match exactly repeated samples, while RDR seems to match 1 full frame (like RAGE-aud).
* ex. RDR: repeated frames' size could be set to 0x774 (7 sub-frames) if adding 1 more would take >0x800.
* ex. GTA5: repeated frames' samples could be set to 3456 = 3 * 1152 = size 0x420
* This behavior may be hardcoded but seems detectable by a flag set in every(?) streamed GTA5 file (all platforms though). */
uint8_t frame[AWC_FRAME_SIZE];
uint32_t offset = bi->block_offset + bi->blk[channel].chunk_start;
read_streamfile(frame, offset, sizeof(frame), sf);
int frames = 0;
int max_frames = is_alt ? bi->blk[channel].channel_skip / 1152 : 999;
/* read sub-frames until padding or end */
int skip_size = 0x00;
while (skip_size < sizeof(frame) - 0x04) {
if (frames == max_frames)
return skip_size;
if (frame[skip_size] == 0x00) /* possible? */
return AWC_FRAME_SIZE;
mpeg_frame_info info = {0};
uint32_t header = get_u32be(frame + skip_size);
if (!mpeg_get_frame_info_h(header, &info)) /* ? */
return AWC_FRAME_SIZE;
if (skip_size + info.frame_size > sizeof(frame)) /* not a repeated frame */
return skip_size;
skip_size += info.frame_size;
frames++;
}
return skip_size; /* skip_size fills frame size */
}
case 0x0F: /* ATRAC9 */ case 0x0F: /* ATRAC9 */
default: default:
VGM_LOG("AWC: found channel skip in codec %x\n", bi->codec); /* not seen */ VGM_LOG("AWC: found channel skip in codec %x\n", bi->codec); /* not seen */
@ -190,18 +235,21 @@ static void block_callback(STREAMFILE *sf, deblock_io_data* data) {
bi.channels = data->cfg.track_count; bi.channels = data->cfg.track_count;
bi.codec = data->cfg.track_type; bi.codec = data->cfg.track_type;
if (bi.block_offset >= get_streamfile_size(sf))
return;
if (!read_awc_block(sf, &bi)) if (!read_awc_block(sf, &bi))
return; //??? return; //???
uint32_t repeat_size = get_block_repeated_size(sf, &bi, channel); uint32_t repeat_size = get_block_repeated_size(sf, &bi, channel, data->cfg.config);
data->block_size = data->cfg.chunk_size; data->block_size = data->cfg.chunk_size;
data->skip_size = bi.blk[channel].chunk_start + repeat_size; data->skip_size = bi.blk[channel].chunk_start + repeat_size;
data->data_size = bi.blk[channel].chunk_size - repeat_size; data->data_size = bi.blk[channel].channel_size - repeat_size;
} }
/* deblocks AWC blocks */ /* deblocks AWC blocks */
static STREAMFILE* setup_awc_streamfile(STREAMFILE* sf, uint32_t stream_offset, uint32_t stream_size, uint32_t block_size, int channels, int channel, uint8_t codec, int big_endian) { static STREAMFILE* setup_awc_streamfile(STREAMFILE* sf, uint32_t stream_offset, uint32_t stream_size, uint32_t block_size, int channels, int channel, uint8_t codec, bool big_endian, bool is_alt) {
STREAMFILE* new_sf = NULL; STREAMFILE* new_sf = NULL;
deblock_config_t cfg = {0}; deblock_config_t cfg = {0};
@ -215,6 +263,7 @@ static STREAMFILE* setup_awc_streamfile(STREAMFILE* sf, uint32_t stream_offset,
cfg.chunk_size = block_size; cfg.chunk_size = block_size;
cfg.track_type = codec; cfg.track_type = codec;
cfg.big_endian = big_endian; cfg.big_endian = big_endian;
cfg.config = is_alt;
//cfg.physical_offset = stream_offset; //cfg.physical_offset = stream_offset;
//cfg.logical_size = awc_io_size(sf, &cfg); /* force init */ //cfg.logical_size = awc_io_size(sf, &cfg); /* force init */
cfg.block_callback = block_callback; cfg.block_callback = block_callback;

View File

@ -18,7 +18,7 @@ static size_t deblock_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_
/* re-start when previous offset (can't map logical<>physical offsets) */ /* re-start when previous offset (can't map logical<>physical offsets) */
if (data->logical_offset < 0 || offset < data->logical_offset) { if (data->logical_offset < 0 || offset < data->logical_offset) {
;VGM_LOG("DEBLOCK: restart offset=%lx + %x, po=%lx, lo=%lx\n", offset, length, data->physical_offset, data->logical_offset); //;VGM_LOG("DEBLOCK: restart offset=%lx + %x, po=%lx, lo=%lx\n", offset, length, data->physical_offset, data->logical_offset);
data->physical_offset = data->cfg.stream_start; data->physical_offset = data->cfg.stream_start;
data->logical_offset = 0x00; data->logical_offset = 0x00;
data->block_size = 0; data->block_size = 0;

View File

@ -1,6 +1,7 @@
#include "meta.h" #include "meta.h"
#include "../coding/coding.h" #include "../coding/coding.h"
#include "../layout/layout.h" #include "../layout/layout.h"
#include "../util.h"
#include "../util/endianness.h" #include "../util/endianness.h"
#include "rage_aud_streamfile.h" #include "rage_aud_streamfile.h"
@ -22,7 +23,7 @@ typedef struct {
uint32_t stream_size; uint32_t stream_size;
} aud_header; } aud_header;
static int parse_aud_header(STREAMFILE* sf, aud_header* aud); static bool parse_aud_header(STREAMFILE* sf, aud_header* aud);
static layered_layout_data* build_layered_rage_aud(STREAMFILE* sf, aud_header* aud); static layered_layout_data* build_layered_rage_aud(STREAMFILE* sf, aud_header* aud);
@ -88,15 +89,14 @@ VGMSTREAM* init_vgmstream_rage_aud(STREAMFILE* sf) {
#ifdef VGM_USE_MPEG #ifdef VGM_USE_MPEG
case 0x0100: { /* MPEG (PS3) */ case 0x0100: { /* MPEG (PS3) */
mpeg_custom_config cfg = {0};
if (aud.is_streamed) { if (aud.is_streamed) {
goto fail; vgmstream->layout_data = build_layered_rage_aud(sf, &aud);
if (!vgmstream->layout_data) goto fail;
vgmstream->layout_type = layout_layered;
vgmstream->coding_type = coding_MPEG_custom;
} }
else { else {
cfg.chunk_size = aud.block_chunk; vgmstream->codec_data = init_mpeg_custom(sf, aud.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, NULL);
cfg.big_endian = aud.big_endian;
vgmstream->codec_data = init_mpeg_custom(sf, aud.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
if (!vgmstream->codec_data) goto fail; if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
} }
@ -126,7 +126,7 @@ fail:
} }
/* Parse Rockstar's AUD header (much info from SparkIV). */ /* Parse Rockstar's AUD header (much info from SparkIV). */
static int parse_aud_header(STREAMFILE* sf, aud_header* aud) { static bool parse_aud_header(STREAMFILE* sf, aud_header* aud) {
int target_subsong = sf->stream_index; int target_subsong = sf->stream_index;
read_u64_t read_u64; read_u64_t read_u64;
read_u32_t read_u32; read_u32_t read_u32;
@ -139,8 +139,8 @@ static int parse_aud_header(STREAMFILE* sf, aud_header* aud) {
read_u16 = aud->big_endian ? read_u16be : read_u16le; read_u16 = aud->big_endian ? read_u16be : read_u16le;
uint64_t table_offset = read_u64(0x00, sf); uint64_t table_offset = read_u64(0x00, sf);
if (table_offset > 0x18000) /* typically 0x1c~0x1000, seen 0x17BE8 */ if (table_offset > 0x20000 || table_offset < 0x1c) /* typically 0x1c~0x1000, seen ~0x19000 */
return 0; return false;
/* use bank's stream count to detect */ /* use bank's stream count to detect */
aud->is_streamed = (read_u32(0x10, sf) == 0); aud->is_streamed = (read_u32(0x10, sf) == 0);
@ -160,14 +160,14 @@ static int parse_aud_header(STREAMFILE* sf, aud_header* aud) {
aud->stream_offset = read_u32(0x2c, sf); aud->stream_offset = read_u32(0x2c, sf);
channel_info_offset = channel_table_offset + aud->channels * 0x10; channel_info_offset = channel_table_offset + aud->channels * 0x10;
/* block count is off in rare XMA streams: /* block count is off in rare XMA streams, though we only need it to check the header:
* GTA4 - Header says 2 blocks, actually has 3 - EP1_SFX/RP03_ML * GTA4 - Header says 2 blocks, actually has 3 - EP1_SFX/RP03_ML
* MCLA - Header says 3 blocks, actually has 4 - AUDIO/X360/SFX/AMBIENCE_STREAM/AMB_QUADSHOT_MALL_ADVERT_09 * MCLA - Header says 3 blocks, actually has 4 - AUDIO/X360/SFX/AMBIENCE_STREAM/AMB_QUADSHOT_MALL_ADVERT_09
*/ */
uint32_t expected_size = aud->block_count * aud->block_chunk + aud->stream_offset; uint32_t expected_size = aud->block_count * aud->block_chunk + aud->stream_offset;
if (expected_size != get_streamfile_size(sf) && expected_size + aud->block_chunk != get_streamfile_size(sf)) { if (expected_size != get_streamfile_size(sf) && expected_size + aud->block_chunk != get_streamfile_size(sf)) {
VGM_LOG("RAGE AUD: bad file size\n"); //;VGM_LOG("RAGE AUD: bad file size\n");
goto fail; return false;
} }
/* channel table (one entry per channel, points to channel info) */ /* channel table (one entry per channel, points to channel info) */
@ -186,7 +186,8 @@ static int parse_aud_header(STREAMFILE* sf, aud_header* aud) {
aud->codec = read_u32(channel_info_offset + 0x1c, sf); aud->codec = read_u32(channel_info_offset + 0x1c, sf);
/* (when codec is IMA) */ /* (when codec is IMA) */
/* 0x20(8): adpcm states offset, 0x38: num states? (reference for seeks?) */ /* 0x20(8): adpcm states offset, 0x38: num states? (reference for seeks?) */
/* rest: unknown data */ /* rest: unknown data (varies per codec?) */
/* in MC:LA there is samples-per-frame and frame size table for MPEG */
/* block table (one entry per block) */ /* block table (one entry per block) */
/* 0x00: data size processed up to this block (doesn't count block padding) */ /* 0x00: data size processed up to this block (doesn't count block padding) */
@ -207,11 +208,11 @@ static int parse_aud_header(STREAMFILE* sf, aud_header* aud) {
/* 0x14(4): unknown */ /* 0x14(4): unknown */
aud->stream_offset = read_u32(0x18, sf); /* base start_offset */ aud->stream_offset = read_u32(0x18, sf); /* base start_offset */
if (target_subsong == 0) target_subsong = 1;
if (target_subsong < 0 || target_subsong > aud->total_subsongs || aud->total_subsongs < 1) goto fail;
if (stream_table_offset != 0x1c) if (stream_table_offset != 0x1c)
goto fail; return false;
if (!check_subsongs(&target_subsong, aud->total_subsongs))
return false;
stream_info_offset = stream_table_offset + 0x10 * aud->total_subsongs; stream_info_offset = stream_table_offset + 0x10 * aud->total_subsongs;
/* stream table (one entry per stream, points to stream info) */ /* stream table (one entry per stream, points to stream info) */
@ -234,13 +235,13 @@ static int parse_aud_header(STREAMFILE* sf, aud_header* aud) {
/* 0x20(8): adpcm states offset, 0x38: num states? (reference for seeks?) */ /* 0x20(8): adpcm states offset, 0x38: num states? (reference for seeks?) */
/* rest: unknown data */ /* rest: unknown data */
/* GTA IV PS3's 0x7D27B1E8' #165 and #166 seem to point to the same wrong offset pointing to the middle of data
* (original bug/unused? maybe should allow MPEG resync?) */
aud->channels = 1; aud->channels = 1;
} }
return 1; return true;
fail:
return 0;
} }
/* ************************************************************************* */ /* ************************************************************************* */
@ -279,11 +280,20 @@ static VGMSTREAM* build_blocks_vgmstream(STREAMFILE* sf, aud_header* aud, int ch
vgmstream->coding_type = coding_FFmpeg; vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none; vgmstream->layout_type = layout_none;
//TODO
//xma_fix_raw_samples(vgmstream, temp_sf, substream_offset, substream_size, 0, 0,0); /* samples are ok? */ //xma_fix_raw_samples(vgmstream, temp_sf, substream_offset, substream_size, 0, 0,0); /* samples are ok? */
break; break;
} }
#endif #endif
#ifdef VGM_USE_MPEG
case 0x0100: { /* MPEG (PS3) */
vgmstream->codec_data = init_mpeg_custom(temp_sf, substream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, NULL);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
break;
}
#endif
default: default:
goto fail; goto fail;
} }
@ -293,7 +303,7 @@ static VGMSTREAM* build_blocks_vgmstream(STREAMFILE* sf, aud_header* aud, int ch
close_streamfile(temp_sf); close_streamfile(temp_sf);
return vgmstream; return vgmstream;
fail: fail:
;VGM_LOG("AUD: can't open decoder\n"); ;VGM_LOG("RAGE AUD: can't open decoder\n");
close_vgmstream(vgmstream); close_vgmstream(vgmstream);
close_streamfile(temp_sf); close_streamfile(temp_sf);
return NULL; return NULL;

View File

@ -3,27 +3,30 @@
#include "deblock_streamfile.h" #include "deblock_streamfile.h"
#include "../util/endianness.h" #include "../util/endianness.h"
#include "../util/log.h" #include "../util/log.h"
#include "../coding/coding.h"
#define RAGE_AUD_MAX_MUSIC_CHANNELS 7 /* max known */ #define RAGE_AUD_MAX_MUSIC_CHANNELS 7 /* known max */
#define RAGE_AUD_FRAME_SIZE 0x800
/* ************************************************************************* */ /* ************************************************************************* */
typedef struct { typedef struct {
int start_entry; /* innacurate! */ int start_entry;
int entries; int entries;
int32_t channel_skip; int32_t channel_skip;
int32_t channel_samples; int32_t channel_samples;
uint32_t channel_size; /* size of this channel's data (not including padding) */
uint32_t frame_size; uint32_t frame_size;
/* derived */ /* derived */
uint32_t chunk_start; /* relative to block offset */ uint32_t chunk_start; /* relative to block offset */
uint32_t chunk_size; /* size of this channel's data (not including padding) */ uint32_t chunk_size; /* size of this channel's data (may include padding) */
} rage_aud_block_t; } rage_aud_block_t;
typedef struct { typedef struct {
int big_endian; int big_endian;
uint8_t codec; int codec;
int channels; int channels;
uint32_t block_offset; uint32_t block_offset;
uint32_t header_size; uint32_t header_size;
@ -43,19 +46,23 @@ typedef struct {
* 0x08: seek table offset * 0x08: seek table offset
* 0x10: seek table offset * 0x10: seek table offset
* - channel info (per channel) * - channel info (per channel)
* 0x00: start entry for that channel? * 0x00: start entry/frame for that channel
* may be off by +1/+2? * Sometimes a channel has N frames while next channel start_entry N+1/2, meaning last frames will be blank/padding
* 0x04: frames in this channel (may be different between channels) * 0x04: entries/frames in this channel (may be different between channels)
* 0x08: samples to discard in the beginning of this block (MPEG/XMA2) * This refers to 1 logical chunk of N sub-frames (XMA1=single XMA super-frame, MPEG=N VBR frames).
* this seems to repeat XMA frames, which decoders don't like, so maybe they just skip whole frames * May be partially/fully blank in MPEG and sizes/paddings aren't very consistent even in similar files (MA:LA's HANGOUT_CROWD_*).
* 0x08: samples to discard in the beginning of this block? (MPEG/XMA2)
* When this is set, channel repeats XMA/MPEG super-frames from prev block. However discard doesn't seem to match
* repeated samples, and skip value may be very small too (ex. just 4 skip samples but repeats make 8416 samples).
* So maybe they just skip data like we do below and don't actually use this value.
* 0x0c: samples in channel without discard? (for MPEG/XMA2 can vary between channels) * 0x0c: samples in channel without discard? (for MPEG/XMA2 can vary between channels)
* (next fields only exists for MPEG) * (next fields only exists for MPEG)
* 0x10: close to number of frames but varies a bit? * 0x10: close to number of VBR frames but varies a bit?
* 0x14: channel chunk size (not counting padding) * 0x14: channel data size (not including padding between channels)
* - seek table (entries for all channels) * - seek table (entries for all channels, 1 per frame)
* 0x00: start? * 0x00: start?
* 0x04: end? * 0x04: end?
* - padding until this block's end * - padding up to data start
*/ */
static bool read_rage_aud_block(STREAMFILE* sf, rage_aud_block_info_t* bi) { static bool read_rage_aud_block(STREAMFILE* sf, rage_aud_block_info_t* bi) {
read_s32_t read_s32 = bi->big_endian ? read_s32be : read_s32le; read_s32_t read_s32 = bi->big_endian ? read_s32be : read_s32le;
@ -63,6 +70,7 @@ static bool read_rage_aud_block(STREAMFILE* sf, rage_aud_block_info_t* bi) {
uint32_t channel_entry_size, seek_entry_size; uint32_t channel_entry_size, seek_entry_size;
uint32_t offset = bi->block_offset; uint32_t offset = bi->block_offset;
int channels = bi->channels; int channels = bi->channels;
/* read stupid block crap + derived info at once so hopefully it's a bit easier to understand */ /* read stupid block crap + derived info at once so hopefully it's a bit easier to understand */
switch(bi->codec) { switch(bi->codec) {
@ -70,8 +78,13 @@ static bool read_rage_aud_block(STREAMFILE* sf, rage_aud_block_info_t* bi) {
channel_entry_size = 0x10; channel_entry_size = 0x10;
seek_entry_size = 0x08; seek_entry_size = 0x08;
break; break;
case 0x0100: /* MPEG */
channel_entry_size = 0x18;
seek_entry_size = 0x08;
break;
default: default:
goto fail; VGM_LOG("RAGE AUD: unknown codec %x\n", bi->codec);
return false;
} }
/* base header */ /* base header */
@ -85,7 +98,9 @@ static bool read_rage_aud_block(STREAMFILE* sf, rage_aud_block_info_t* bi) {
bi->blk[ch].entries = read_s32(offset + 0x04, sf); bi->blk[ch].entries = read_s32(offset + 0x04, sf);
bi->blk[ch].channel_skip = read_s32(offset + 0x08, sf); bi->blk[ch].channel_skip = read_s32(offset + 0x08, sf);
bi->blk[ch].channel_samples = read_s32(offset + 0x0c, sf); bi->blk[ch].channel_samples = read_s32(offset + 0x0c, sf);
/* others: optional */ if (bi->codec == 0x0100) { /* MPEG */
bi->blk[ch].channel_size = read_s32(offset + 0x14, sf);
}
offset += channel_entry_size; offset += channel_entry_size;
} }
@ -97,15 +112,15 @@ static bool read_rage_aud_block(STREAMFILE* sf, rage_aud_block_info_t* bi) {
/* derived info */ /* derived info */
for (int ch = 0; ch < channels; ch++) { for (int ch = 0; ch < channels; ch++) {
bi->blk[ch].frame_size = RAGE_AUD_FRAME_SIZE; /* XMA1 super-frame or MPEG chunk of N VBR frames */
bi->blk[ch].chunk_size = bi->blk[ch].entries * bi->blk[ch].frame_size; /* full size between channels, may be padded */
switch(bi->codec) { switch(bi->codec) {
case 0x0000: /* XMA1 */ case 0x0000: /* XMA1 */
bi->blk[ch].frame_size = 0x800; bi->blk[ch].channel_size = bi->blk[ch].chunk_size; /* no padding */
bi->blk[ch].chunk_size = bi->blk[ch].entries * bi->blk[ch].frame_size;
break; break;
/* in MPEG frames seem to be VBR, so would need channel chunk size */
default: default:
goto fail; break;
} }
} }
@ -115,20 +130,22 @@ static bool read_rage_aud_block(STREAMFILE* sf, rage_aud_block_info_t* bi) {
* a table as big as prev blocks, repeating old values for unused entries, so final header size is consistent */ * a table as big as prev blocks, repeating old values for unused entries, so final header size is consistent */
if (!bi->header_size) if (!bi->header_size)
bi->header_size = offset - bi->block_offset; bi->header_size = offset - bi->block_offset;
offset = bi->block_offset + align_size_to_block(bi->header_size, 0x800); offset = bi->block_offset + align_size_to_block(bi->header_size, RAGE_AUD_FRAME_SIZE);
} }
/* set frame starts per channel */ /* set frame starts per channel */
uint32_t header_chunk = offset - bi->block_offset;
for (int ch = 0; ch < channels; ch++) { for (int ch = 0; ch < channels; ch++) {
bi->blk[ch].chunk_start = offset - bi->block_offset; bi->blk[ch].chunk_start = header_chunk + bi->blk[ch].start_entry * bi->blk[ch].frame_size;
offset += bi->blk[ch].chunk_size;
/* unlike AWC there may be unknown padding between channels, so needs start_entry to calc offset */
//bi->blk[ch].chunk_start = offset - bi->block_offset;
//offset += bi->blk[ch].chunk_size;
} }
/* beyond this is padding until chunk_start */ /* beyond this is padding until chunk_start */
return true; return true;
fail:
return false;
} }
/* Find data that repeats in the beginning of a new block at the end of last block. /* Find data that repeats in the beginning of a new block at the end of last block.
@ -138,15 +155,41 @@ static uint32_t get_block_repeated_size(STREAMFILE* sf, rage_aud_block_info_t* b
if (bi->blk[channel].channel_skip == 0) if (bi->blk[channel].channel_skip == 0)
return 0; return 0;
if (bi->block_offset >= get_streamfile_size(sf))
return 0;
switch(bi->codec) { switch(bi->codec) {
case 0x0000: { /* XMA1 */ case 0x0000: { /* XMA1 */
/* when data repeats seems to clone the last (super-)frame */ /* when data repeats seems to clone the last super-frame */
return bi->blk[channel].frame_size; return bi->blk[channel].frame_size;
} }
case 0x0100: { /* MPEG */
/* first super-frame will repeat N VBR old sub-frames, without crossing frame_size.
* ex. repeated frames' size could be set to 0x774 (7 sub-frames) if adding 1 more would take >0x800.
* After last sub-frame there may be padding up to frame_size (GTA4 only?). */
uint8_t frame[RAGE_AUD_FRAME_SIZE];
uint32_t offset = bi->block_offset + bi->blk[channel].chunk_start;
read_streamfile(frame, offset, sizeof(frame), sf);
/* read sub-frames until padding or end */
int skip_size = 0x00;
while (skip_size < sizeof(frame) - 0x04) {
if (frame[skip_size] == 0x00) /* padding found */
return RAGE_AUD_FRAME_SIZE;
mpeg_frame_info info = {0};
uint32_t header = get_u32be(frame + skip_size);
if (!mpeg_get_frame_info_h(header, &info)) /* ? */
return RAGE_AUD_FRAME_SIZE;
if (skip_size + info.frame_size > sizeof(frame)) /* not a repeated frame */
return skip_size;
skip_size += info.frame_size;
}
return skip_size; /* skip_size fills frame size */
}
default: default:
;VGM_LOG("RAGE_AUD: found channel skip in codec %x\n", bi->codec); /* not seen */ ;VGM_LOG("RAGE_AUD: found channel skip in codec %x\n", bi->codec); /* not seen */
return 0; return 0;
@ -165,6 +208,9 @@ static void block_callback(STREAMFILE *sf, deblock_io_data* data) {
bi.codec = data->cfg.track_type; bi.codec = data->cfg.track_type;
bi.header_size = data->cfg.config; bi.header_size = data->cfg.config;
if (bi.block_offset >= get_streamfile_size(sf))
return;
if (!read_rage_aud_block(sf, &bi)) if (!read_rage_aud_block(sf, &bi))
return; //??? return; //???
data->cfg.config = bi.header_size; /* fixed for all blocks but calc'd on first one */ data->cfg.config = bi.header_size; /* fixed for all blocks but calc'd on first one */
@ -173,11 +219,11 @@ static void block_callback(STREAMFILE *sf, deblock_io_data* data) {
data->block_size = data->cfg.chunk_size; data->block_size = data->cfg.chunk_size;
data->skip_size = bi.blk[channel].chunk_start + repeat_size; data->skip_size = bi.blk[channel].chunk_start + repeat_size;
data->data_size = bi.blk[channel].chunk_size - repeat_size; data->data_size = bi.blk[channel].channel_size - repeat_size;
} }
/* deblocks RAGE_AUD blocks */ /* deblocks RAGE_AUD blocks */
static STREAMFILE* setup_rage_aud_streamfile(STREAMFILE* sf, uint32_t stream_offset, uint32_t stream_size, uint32_t block_size, int channels, int channel, uint8_t codec, int big_endian) { static STREAMFILE* setup_rage_aud_streamfile(STREAMFILE* sf, uint32_t stream_offset, uint32_t stream_size, uint32_t block_size, int channels, int channel, int codec, bool big_endian) {
STREAMFILE* new_sf = NULL; STREAMFILE* new_sf = NULL;
deblock_config_t cfg = {0}; deblock_config_t cfg = {0};

View File

@ -1002,7 +1002,6 @@ STREAMFILE* reopen_streamfile(STREAMFILE* sf, size_t buffer_size) {
/* ************************************************************************* */ /* ************************************************************************* */
/* debug util, mainly for custom IO testing */
void dump_streamfile(STREAMFILE* sf, int num) { void dump_streamfile(STREAMFILE* sf, int num) {
#ifdef VGM_DEBUG_OUTPUT #ifdef VGM_DEBUG_OUTPUT
offv_t offset = 0; offv_t offset = 0;
@ -1019,7 +1018,7 @@ void dump_streamfile(STREAMFILE* sf, int num) {
if (!f) return; if (!f) return;
} }
VGM_LOG("dump streamfile: size %x\n", get_streamfile_size(sf)); VGM_LOG("dump streamfile %i: size %x\n", num, get_streamfile_size(sf));
while (offset < get_streamfile_size(sf)) { while (offset < get_streamfile_size(sf)) {
uint8_t buf[0x8000]; uint8_t buf[0x8000];
size_t bytes; size_t bytes;
@ -1032,8 +1031,9 @@ void dump_streamfile(STREAMFILE* sf, int num) {
if (f) if (f)
fwrite(buf, sizeof(uint8_t), bytes, f); fwrite(buf, sizeof(uint8_t), bytes, f);
else else if (num == -1)
VGM_LOGB(buf, bytes, 0); VGM_LOGB(buf, bytes, 0);
//else: don't do anything (read test)
offset += bytes; offset += bytes;
} }

View File

@ -151,7 +151,7 @@ static inline size_t get_streamfile_size(STREAMFILE* sf) {
return sf->get_size(sf); return sf->get_size(sf);
} }
/* debug util, mainly for custom IO testing (num = writes file N, -1 = printfs, -2 = only reads) */
void dump_streamfile(STREAMFILE* sf, int num); void dump_streamfile(STREAMFILE* sf, int num);
#endif #endif