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_EAL32S, /* EALayer3 v2 "Spike", custom frames with v2 header + smaller PCM blocks? */
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_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);
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);
#endif

View File

@ -264,7 +264,7 @@ fail:
* 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.
*/
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 */
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 };
@ -330,12 +330,12 @@ static int mpeg_get_frame_info_h(uint32_t header, mpeg_frame_info* info) {
default: goto fail;
}
return 1;
return true;
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);
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 */
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;
int i, ok;
int ok;
/* init codec */
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 */
data->custom = 1;
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->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_EAL32P:
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;
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 = 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]->m = init_mpg123_handle(); /* decoder not shared as may need several frames to decode)*/
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_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_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;
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\mpeg_custom_utils.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_eamp3.c" />
<ClCompile Include="coding\mpeg_decoder.c" />

View File

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

View File

@ -6,9 +6,10 @@
#include "awc_decryption_streamfile.h"
typedef struct {
int big_endian;
int is_encrypted;
int is_streamed; /* implicit: streams=music, sfx=memory */
bool big_endian;
bool is_encrypted;
bool is_streamed; /* implicit: streams=music, sfx=memory */
bool is_alt;
int total_subsongs;
@ -115,15 +116,17 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
#ifdef VGM_USE_MPEG
case 0x07: { /* MPEG (PS3) */
mpeg_custom_config cfg = {0};
cfg.chunk_size = awc.block_chunk;
cfg.big_endian = awc.big_endian;
vgmstream->codec_data = init_mpeg_custom(sf_body, awc.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_AWC, &cfg);
if (awc.is_streamed) {
vgmstream->layout_data = build_layered_awc(sf_body, &awc);
if (!vgmstream->layout_data) goto fail;
vgmstream->layout_type = layout_layered;
vgmstream->coding_type = coding_FFmpeg;
}
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;
vgmstream->layout_type = layout_none;
}
break;
}
#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) */
if ((flags & 0xFF00FFFF) != 0xFF000001 || (flags & 0x00F00000)) {
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) */
@ -288,17 +291,16 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
offset += 0x2 * entries;
/* seems to indicate chunks are not ordered (ie. header structures from tags may go after data), usually for non-streams */
//if (flags % 0x00020000)
// awc->is_unordered = 1;
//if (flags & 0x00020000)
// awc->is_unordered = true;
/* stream/multichannel flag (rare, GTA5/RDR2) */
//if (flags % 0x00040000)
// awc->is_multichannel = 1;
/* stream/multichannel flag? (GTA5, some RDR2), can be used to detect some odd behavior in GTA5 vs RDR1 */
if (flags & 0x00040000)
awc->is_alt = true;
/* encrypted data chunk (most of GTA5 PC for licensed audio) */
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.
* 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);
/* 0x04: -1? */
/* 0x04: -1? (loop related?) */
awc->sample_rate = read_u16(tag_offset + 0x08,sf);
/* 0x0a: headroom */
/* 0x0c: unknown */
/* 0x0e: unknown */
/* 0x10: unknown */
/* 0x0c: unknown (loop related?) */
/* 0x0e: unknown (loop related?) */
/* 0x10: unknown (loop related?) */
/* 0x12: null? */
awc->codec = read_u8(tag_offset + 0x13, sf);
/* 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) */
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;
substream_offset = 0x00;
@ -591,6 +593,14 @@ static VGMSTREAM* build_blocks_vgmstream(STREAMFILE* sf, awc_header* awc, int ch
break;
}
#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
case 0x08: {
vorbis_custom_config cfg = {0};

View File

@ -4,20 +4,22 @@
#include "../util/endianness.h"
#define AWC_MAX_MUSIC_CHANNELS 32 /* seen ~24 */
#define AWC_FRAME_SIZE 0x800
/* ************************************************************************* */
typedef struct {
int start_entry; /* innacurate! */
int start_entry; /* inaccurate! */
int entries;
int32_t channel_skip;
int32_t channel_samples;
uint32_t channel_size; /* size of this channel's data (not including padding) */
uint32_t frame_size;
/* derived */
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;
typedef struct {
@ -36,24 +38,26 @@ typedef struct {
* - usually there is padding between channels or blocks (usually 0s but seen 0x97 in AT9)
*
* Header format:
* - per channel (frame start table)
* 0x00: start entry for that channel? (-1 in vorbis)
* may be off by +1/+2?
* 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, a start entry is: ch0=0x0000, ch1=0x02AA (AT9) !!
* (sum of all values from all channels may go beyond all posible frames, no idea)
* 0x04: frames in this channel (may be different between channels)
* 'frames' here may be actual single decoder frames or a chunk of frames
* - channel info (per channel)
* 0x00: start entry/frame for that channel? (-1 in vorbis, innacurate in MPEG/AT9, ? in XMA)
* 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 0x02A9 frames, start entry is: ch0=0x0000, ch1=0x02AA (AT9) !! (would be 1 frame into ch2)
* 0x04: entries/frames in this channel (may be different between channels)
* This refers to 1 logical chunk of N sub-frames
* MPEG padding works differently vs RAGE-aud too.
* 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)
* full samples without removing samples to discard
* (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?
* 0x14: (MPEG only, empty otherwise) channel chunk size (not counting padding)
* - for each channel (seek table)
* 32b * entries = global samples per frame in each block (for MPEG probably per full frame)
* - seek table (entries for all channels, 1 per 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)
* - per channel (ATRAC9/DSP extra info):
* - extra info (per channel, ATRAC9/DSP ony):
* 0x00: "D11A"
* 0x04: frame size
* 0x06: frame samples
@ -61,11 +65,7 @@ typedef struct {
* 0x0a: sample rate
* 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)
* - padding until channel data start, depending on codec (DSP/ATRAC9: one, 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
* - padding up to data start, depending on codec (DSP/ATRAC9: none, others: aligned to 0x800)
*/
static bool read_awc_block(STREAMFILE* sf, awc_block_info_t* bi) {
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 offset = bi->block_offset;
int channels = bi->channels;
/* read stupid block crap + derived info at once so hopefully it's a bit easier to understand */
switch(bi->codec) {
@ -83,6 +84,7 @@ static bool read_awc_block(STREAMFILE* sf, awc_block_info_t* bi) {
extra_entry_size = 0x00;
header_padding = 0x800;
break;
case 0x07: /* MPEG */
case 0x08: /* Vorbis */
channel_entry_size = 0x18;
seek_entry_size = 0x04;
@ -96,7 +98,7 @@ static bool read_awc_block(STREAMFILE* sf, awc_block_info_t* bi) {
header_padding = 0x00;
break;
default:
goto fail;
return false;
}
/* 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].channel_skip = read_s32(offset + 0x08, 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;
}
@ -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
* (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. */
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].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;
case 0x0F: /* ATRAC9 */
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].channel_size = bi->blk[ch].chunk_size;
break;
default:
goto fail;
return false;
}
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 */
return true;
fail:
return false;
}
/* 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?).
* 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)
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 */
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 */
default:
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.codec = data->cfg.track_type;
if (bi.block_offset >= get_streamfile_size(sf))
return;
if (!read_awc_block(sf, &bi))
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->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 */
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;
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.track_type = codec;
cfg.big_endian = big_endian;
cfg.config = is_alt;
//cfg.physical_offset = stream_offset;
//cfg.logical_size = awc_io_size(sf, &cfg); /* force init */
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) */
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->logical_offset = 0x00;
data->block_size = 0;

View File

@ -1,6 +1,7 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
#include "../util.h"
#include "../util/endianness.h"
#include "rage_aud_streamfile.h"
@ -22,7 +23,7 @@ typedef struct {
uint32_t stream_size;
} 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);
@ -88,15 +89,14 @@ VGMSTREAM* init_vgmstream_rage_aud(STREAMFILE* sf) {
#ifdef VGM_USE_MPEG
case 0x0100: { /* MPEG (PS3) */
mpeg_custom_config cfg = {0};
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 {
cfg.chunk_size = aud.block_chunk;
cfg.big_endian = aud.big_endian;
vgmstream->codec_data = init_mpeg_custom(sf, aud.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, &cfg);
vgmstream->codec_data = init_mpeg_custom(sf, aud.stream_offset, &vgmstream->coding_type, vgmstream->channels, MPEG_STANDARD, NULL);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
}
@ -126,7 +126,7 @@ fail:
}
/* 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;
read_u64_t read_u64;
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;
uint64_t table_offset = read_u64(0x00, sf);
if (table_offset > 0x18000) /* typically 0x1c~0x1000, seen 0x17BE8 */
return 0;
if (table_offset > 0x20000 || table_offset < 0x1c) /* typically 0x1c~0x1000, seen ~0x19000 */
return false;
/* use bank's stream count to detect */
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);
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
* 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;
if (expected_size != get_streamfile_size(sf) && expected_size + aud->block_chunk != get_streamfile_size(sf)) {
VGM_LOG("RAGE AUD: bad file size\n");
goto fail;
//;VGM_LOG("RAGE AUD: bad file size\n");
return false;
}
/* 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);
/* (when codec is IMA) */
/* 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) */
/* 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 */
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)
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 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?) */
/* 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;
}
return 1;
fail:
return 0;
return true;
}
/* ************************************************************************* */
@ -279,11 +280,20 @@ static VGMSTREAM* build_blocks_vgmstream(STREAMFILE* sf, aud_header* aud, int ch
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
//TODO
//xma_fix_raw_samples(vgmstream, temp_sf, substream_offset, substream_size, 0, 0,0); /* samples are ok? */
break;
}
#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:
goto fail;
}
@ -293,7 +303,7 @@ static VGMSTREAM* build_blocks_vgmstream(STREAMFILE* sf, aud_header* aud, int ch
close_streamfile(temp_sf);
return vgmstream;
fail:
;VGM_LOG("AUD: can't open decoder\n");
;VGM_LOG("RAGE AUD: can't open decoder\n");
close_vgmstream(vgmstream);
close_streamfile(temp_sf);
return NULL;

View File

@ -3,27 +3,30 @@
#include "deblock_streamfile.h"
#include "../util/endianness.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 {
int start_entry; /* innacurate! */
int start_entry;
int entries;
int32_t channel_skip;
int32_t channel_samples;
uint32_t channel_size; /* size of this channel's data (not including padding) */
uint32_t frame_size;
/* derived */
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;
typedef struct {
int big_endian;
uint8_t codec;
int codec;
int channels;
uint32_t block_offset;
uint32_t header_size;
@ -43,19 +46,23 @@ typedef struct {
* 0x08: seek table offset
* 0x10: seek table offset
* - channel info (per channel)
* 0x00: start entry for that channel?
* may be off by +1/+2?
* 0x04: frames in this channel (may be different between channels)
* 0x08: samples to discard in the beginning of this block (MPEG/XMA2)
* this seems to repeat XMA frames, which decoders don't like, so maybe they just skip whole frames
* 0x00: start entry/frame for that channel
* Sometimes a channel has N frames while next channel start_entry N+1/2, meaning last frames will be blank/padding
* 0x04: entries/frames in this channel (may be different between channels)
* This refers to 1 logical chunk of N sub-frames (XMA1=single XMA super-frame, MPEG=N VBR 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)
* (next fields only exists for MPEG)
* 0x10: close to number of frames but varies a bit?
* 0x14: channel chunk size (not counting padding)
* - seek table (entries for all channels)
* 0x10: close to number of VBR frames but varies a bit?
* 0x14: channel data size (not including padding between channels)
* - seek table (entries for all channels, 1 per frame)
* 0x00: start?
* 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) {
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 offset = bi->block_offset;
int channels = bi->channels;
/* read stupid block crap + derived info at once so hopefully it's a bit easier to understand */
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;
seek_entry_size = 0x08;
break;
case 0x0100: /* MPEG */
channel_entry_size = 0x18;
seek_entry_size = 0x08;
break;
default:
goto fail;
VGM_LOG("RAGE AUD: unknown codec %x\n", bi->codec);
return false;
}
/* 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].channel_skip = read_s32(offset + 0x08, 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;
}
@ -97,15 +112,15 @@ static bool read_rage_aud_block(STREAMFILE* sf, rage_aud_block_info_t* bi) {
/* derived info */
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) {
case 0x0000: /* XMA1 */
bi->blk[ch].frame_size = 0x800;
bi->blk[ch].chunk_size = bi->blk[ch].entries * bi->blk[ch].frame_size;
bi->blk[ch].channel_size = bi->blk[ch].chunk_size; /* no padding */
break;
/* in MPEG frames seem to be VBR, so would need channel chunk size */
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 */
if (!bi->header_size)
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 */
uint32_t header_chunk = offset - bi->block_offset;
for (int ch = 0; ch < channels; ch++) {
bi->blk[ch].chunk_start = offset - bi->block_offset;
offset += bi->blk[ch].chunk_size;
bi->blk[ch].chunk_start = header_chunk + bi->blk[ch].start_entry * bi->blk[ch].frame_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 */
return true;
fail:
return false;
}
/* 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)
return 0;
if (bi->block_offset >= get_streamfile_size(sf))
return 0;
switch(bi->codec) {
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;
}
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:
;VGM_LOG("RAGE_AUD: found channel skip in codec %x\n", bi->codec); /* not seen */
return 0;
@ -165,6 +208,9 @@ static void block_callback(STREAMFILE *sf, deblock_io_data* data) {
bi.codec = data->cfg.track_type;
bi.header_size = data->cfg.config;
if (bi.block_offset >= get_streamfile_size(sf))
return;
if (!read_rage_aud_block(sf, &bi))
return; //???
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->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 */
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;
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) {
#ifdef VGM_DEBUG_OUTPUT
offv_t offset = 0;
@ -1019,7 +1018,7 @@ void dump_streamfile(STREAMFILE* sf, int num) {
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)) {
uint8_t buf[0x8000];
size_t bytes;
@ -1032,8 +1031,9 @@ void dump_streamfile(STREAMFILE* sf, int num) {
if (f)
fwrite(buf, sizeof(uint8_t), bytes, f);
else
else if (num == -1)
VGM_LOGB(buf, bytes, 0);
//else: don't do anything (read test)
offset += bytes;
}

View File

@ -151,7 +151,7 @@ static inline size_t get_streamfile_size(STREAMFILE* 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);
#endif