mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-24 06:50:20 +01:00
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:
commit
c39d1cad92
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
@ -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" />
|
||||
|
@ -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>
|
||||
|
@ -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};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user