Fix some MPEG .awc [GTA V (PS3)]

This commit is contained in:
bnnm 2024-05-03 13:48:23 +02:00
parent 88c5abba00
commit d520593fcb
2 changed files with 117 additions and 58 deletions

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 (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
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_start; /* relative to block offset */
uint32_t chunk_size; /* size of this channel's data (may include padding) */
} awc_block_t;
typedef struct {
@ -34,26 +36,28 @@ typedef struct {
* - ...
* - frames from channel N
* - 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)
* (AT9 doesn't have a seek table as it's CBR)
* - per channel (ATRAC9/DSP extra info):
* - 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)
* - 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;