mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-17 11:18:31 +01:00
Fix some MPEG .awc [GTA V (PS3)]
This commit is contained in:
parent
88c5abba00
commit
d520593fcb
@ -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};
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user