Improve Vorbis .awc [Red Dead Redemption 2 (PC)]

This commit is contained in:
bnnm 2023-11-28 01:13:23 +01:00
parent 3ab1178294
commit c4e9a9ed11
6 changed files with 385 additions and 384 deletions

View File

@ -1103,7 +1103,8 @@ different internally (encrypted, different versions, etc) and not always can be
- **awc.c**
- Rockstar AWC header [*AWC*]
- *awc*: `.awc`
- Codecs: PCM16BE PCM16LE AWC_IMA XMA2 MPEG VORBIS_custom
- Subfiles: *riff*
- Codecs: PCM16BE PCM16LE AWC_IMA XMA2 MPEG VORBIS_custom ATRAC9 NGC_DSP
- **opus.c**
- Nintendo Switch OPUS header [*OPUS*]
- *opus_std*: `.opus .lopus .bgm .opu + .psi`
@ -1491,7 +1492,7 @@ different internally (encrypted, different versions, etc) and not always can be
- *awb*
- Subfiles: *awb_memory*
- *awb_memory*: `.awb .afs2 + .acb .(external)`
- Subfiles: *adx_subkey hca_subkey vag riff bcwav ngc_dsp_std dsp_cwac mp4_aac_ffmpeg*
- Subfiles: *adx_subkey hca_subkey vag riff bcwav ngc_dsp_std dsp_cwac mp4_aac_ffmpeg opus_std*
- **acb.c**
- (container)
- *acb*: `.acb`
@ -1531,8 +1532,9 @@ different internally (encrypted, different versions, etc) and not always can be
- **nub.c**
- Namco NUB header [*NUB*]
- *nub*: `.nub .nub2 + .(external)`
- Subfiles: *nub_xma nub_wav nub_vag nub_at3 nub_dsp nub_idsp nub_is14 function*
- Subfiles: *nub_xma nub_wav nub_vag nub_at3 nub_dsp nub_idsp nub_is14 nub_caf function*
- *nub_wav*: `.wav .lwav`
- Subfiles: *riff*
- *nub_vag*: `.vag`
- *nub_at3*: `.at3`
- Subfiles: *riff*
@ -1543,6 +1545,8 @@ different internally (encrypted, different versions, etc) and not always can be
- Subfiles: *idsp_namco*
- *nub_is14*: `.is14`
- Subfiles: *bnsf*
- *nub_caf*: `.caf`
- Subfiles: *apple_caff*
- Codecs: PCM16BE PSX XMA
- **ubi_hx.c**
- Ubisoft HXx header [*UBI_HX*]

View File

@ -120,7 +120,7 @@
<ClInclude Include="meta\adx_keys.h" />
<ClInclude Include="meta\ahx_keys.h" />
<ClInclude Include="meta\aix_streamfile.h" />
<ClInclude Include="meta\awc_xma_streamfile.h" />
<ClInclude Include="meta\awc_streamfile.h" />
<ClInclude Include="meta\bar_streamfile.h" />
<ClInclude Include="meta\bgw_streamfile.h" />
<ClInclude Include="meta\bnsf_keys.h" />

View File

@ -185,7 +185,7 @@
<ClInclude Include="meta\aix_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\awc_xma_streamfile.h">
<ClInclude Include="meta\awc_streamfile.h">
<Filter>meta\Header Files</Filter>
</ClInclude>
<ClInclude Include="meta\bar_streamfile.h">

View File

@ -1,10 +1,8 @@
#include "meta.h"
#include "../coding/coding.h"
#include "../layout/layout.h"
#include "awc_xma_streamfile.h"
#define AWC_MAX_MUSIC_CHANNELS 20
#include "../util/endianness.h"
#include "awc_streamfile.h"
typedef struct {
int big_endian;
@ -15,16 +13,22 @@ typedef struct {
int channels;
int sample_rate;
int codec;
int num_samples;
uint8_t codec;
int block_count;
int block_chunk;
off_t stream_offset;
size_t stream_size;
off_t vorbis_offset[VGMSTREAM_MAX_CHANNELS];
uint32_t tags_offset;
uint32_t stream_offset;
uint32_t stream_size;
uint32_t vorbis_offset[AWC_MAX_MUSIC_CHANNELS];
uint32_t channel_hash[AWC_MAX_MUSIC_CHANNELS];
struct {
uint32_t hash_id;
int tag_count;
} stream_info[AWC_MAX_MUSIC_CHANNELS];
} awc_header;
static int parse_awc_header(STREAMFILE* sf, awc_header* awc);
@ -74,52 +78,11 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
#ifdef VGM_USE_FFMPEG
case 0x05: { /* XMA2 (X360) */
uint32_t substream_size, substream_offset;
if (awc.is_streamed) {
/* 1ch XMAs in blocks, we'll use layered layout + custom IO to get multi-FFmpegs working */
int i;
layered_layout_data * data = NULL;
/* init layout */
data = init_layout_layered(awc.channels);
if (!data) goto fail;
vgmstream->layout_data = data;
vgmstream->layout_data = build_layered_awc(sf, &awc);
if (!vgmstream->layout_data) goto fail;
vgmstream->layout_type = layout_layered;
vgmstream->coding_type = coding_FFmpeg;
/* open each layer subfile */
for (i = 0; i < awc.channels; i++) {
STREAMFILE* temp_sf = NULL;
int layer_channels = 1;
/* build the layer VGMSTREAM */
data->layers[i] = allocate_vgmstream(layer_channels, 0);
if (!data->layers[i]) goto fail;
data->layers[i]->meta_type = meta_AWC;
data->layers[i]->coding_type = coding_FFmpeg;
data->layers[i]->layout_type = layout_none;
data->layers[i]->sample_rate = awc.sample_rate;
data->layers[i]->num_samples = awc.num_samples;
/* setup custom IO streamfile, pass to FFmpeg and hope it's fooled */
temp_sf = setup_awc_xma_streamfile(sf, awc.stream_offset, awc.stream_size, awc.block_chunk, awc.channels, i);
if (!temp_sf) goto fail;
substream_offset = 0x00; /* where FFmpeg thinks data starts, which our custom sf will clamp */
substream_size = get_streamfile_size(temp_sf); /* data of one XMA substream without blocks */
data->layers[i]->codec_data = init_ffmpeg_xma2_raw(temp_sf, substream_offset, substream_size, awc.num_samples, layer_channels, awc.sample_rate, 0, 0);
if (data->layers[i])
xma_fix_raw_samples(data->layers[i], temp_sf, substream_offset, substream_size, 0, 0,0); /* samples are ok? */
close_streamfile(temp_sf);
if (!data->layers[i]->codec_data) goto fail;
}
/* setup layered VGMSTREAMs */
if (!setup_layout_layered(data))
goto fail;
}
else {
/* regular XMA for sfx */
@ -151,7 +114,7 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
#endif
#ifdef VGM_USE_VORBIS
case 0x08: { /* Vorbis (PC) [Red Dead Redemption 2 (PC)] */
case 0x08: { /* Vorbis (PC) [Red Dead Redemption 2 (PC)] */
if (awc.is_streamed) {
vgmstream->layout_data = build_layered_awc(sf, &awc);
if (!vgmstream->layout_data) goto fail;
@ -175,7 +138,7 @@ VGMSTREAM* init_vgmstream_awc(STREAMFILE* sf) {
#endif
#ifdef VGM_USE_ATRAC9
case 0x0F: { /* ATRAC9 (PC) [Red Dead Redemption (PS4)] */
case 0x0F: { /* ATRAC9 (PC) [Red Dead Redemption (PS4)] */
if (awc.is_streamed) {
vgmstream->layout_data = build_layered_awc(sf, &awc);
if (!vgmstream->layout_data) goto fail;
@ -262,9 +225,9 @@ fail:
* - data from tags (headers, tables, audio data, etc)
*/
static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
uint64_t (*read_u64)(off_t,STREAMFILE*) = NULL; //TODO endian
uint32_t (*read_u32)(off_t,STREAMFILE*) = NULL;
uint16_t (*read_u16)(off_t,STREAMFILE*) = NULL;
read_u64_t read_u64 = NULL;
read_u32_t read_u32 = NULL;
read_u16_t read_u16 = NULL;
int entries;
uint32_t flags, tag_count = 0, tags_skip = 0;
uint32_t offset;
@ -369,15 +332,26 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
/** stream ids and tag counts **/
for (int i = 0; i < entries; i++) {
uint32_t info_header = read_u32(offset + 0x04*i, sf);
tag_count = (info_header >> 29) & 0x7; /* 3b */
//hash_id = (info_header >> 0) & 0x1FFFFFFF; /* 29b */
if (target_subsong - 1 == i)
break;
tags_skip += tag_count; /* tags to skip to reach target's tags, in the next header */
uint32_t info_header = read_u32(offset + 0x00, sf);
int entry_count = (info_header >> 29) & 0x7; /* 3b */
uint32_t hash_id = (info_header >> 0) & 0x1FFFFFFF; /* 29b */
if (i + 1 < target_subsong)
tags_skip += entry_count; /* tags to skip to reach target's tags, in the next header */
if (target_subsong == i + 1)
tag_count = entry_count;
if (awc->is_streamed) {
awc->stream_info[i].hash_id = hash_id;
awc->stream_info[i].tag_count = entry_count;
}
offset += 0x04;
}
offset += 0x04 * entries;
offset += 0x08 * tags_skip;
awc->tags_offset = offset; /* where tags for stream start */
offset += 0x08 * tags_skip; /* ignore tags for other streams */
/** tags per stream **/
@ -386,7 +360,6 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
uint8_t tag_type = ((tag_header >> 56) & 0xFF); /* 8b */
uint32_t tag_size = ((tag_header >> 28) & 0x0FFFFFFF); /* 28b */
uint32_t tag_offset = ((tag_header >> 0) & 0x0FFFFFFF); /* 28b */
//;VGM_LOG("AWC: tag %i/%i at %x: t=%x, o=%x, s=%x\n", i+1, tag_count, offset + 0x08*i, tag_type, tag_offset, tag_size);
/* types are apparently part of a hash derived from a word ("data", "format", etc). */
switch(tag_type) {
@ -407,19 +380,20 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
if (awc->channels != entries - 1) { /* not counting id-0 */
VGM_LOG("AWC: number of music channels doesn't match entries\n");
goto fail;
/* extremely rare but doesn't seem to matter, some streams are dummies (RDR2 STREAMS/ABIGAIL_HUMMING_*) */
//goto fail;
}
for (int ch = 0; ch < awc->channels; ch++) {
int num_samples, sample_rate, codec;
/* 0x00: reference stream hash/id */
awc->channel_hash[ch] = read_u32(tag_offset + 0x0c + 0x10*ch + 0x00,sf); /* reference, for vorbis */
num_samples = read_u32(tag_offset + 0x0c + 0x10*ch + 0x04,sf);
/* 0x08: headroom */
sample_rate = read_u16(tag_offset + 0x0c + 0x10*ch + 0x0a,sf);
codec = read_u8(tag_offset + 0x0c + 0x10*ch + 0x0c,sf);
/* 0x0d(8): round size? */
/* 0x0e: unknown (zero/-1) */
/* 0x0e: unknown (zero/-1, loop flag? BOB_FINALE_1_A.awc, but also set in stingers) */
/* validate channels differences */
if ((awc->num_samples && !(awc->num_samples >= num_samples - 10 && awc->num_samples <= num_samples + 10)) ||
@ -478,11 +452,23 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
/* 0x10: unknown */
awc->codec = read_u8(tag_offset + 0x1c, sf); /* 16b? */
/* 0x1e: vorbis setup size */
awc->vorbis_offset[0] = tag_offset + 0x20; /* data up to vorbis setup size */
if (read_u16(tag_offset + 0x1e,sf))/* rarely not set and uses a tag below */
awc->vorbis_offset[0] = tag_offset + 0x20; /* data up to vorbis setup size */
awc->channels = 1;
break;
case 0x7F: /* vorbis setup */
if (awc->is_streamed) {
/* music stream doesn't have this (instead every channel-strem have one, parsed later) */
VGM_LOG("AWC: vorbis setup found but streamed\n");
goto fail;
}
/* very rarely used for sfx: SS_AM/GESTURE01.awc */
awc->vorbis_offset[0] = tag_offset;
break;
case 0x68: /* midi data [Red Dead Redemption 2 (PC)] */
/* set fake info so awc doesn't break */
awc->stream_offset = tag_offset;
@ -494,6 +480,7 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
awc->channels = 1;
break;
case 0xA3: /* block-to-sample table (32b x number of blocks w/ num_samples at the start of each block)
* or frame-size table (16b x number of frames) in some cases (ex. sfx+mpeg but not sfx+vorbis) */
case 0xBD: /* events (32bx4): type_hash, params_hash, timestamp_ms, flags */
@ -501,21 +488,42 @@ static int parse_awc_header(STREAMFILE* sf, awc_header* awc) {
case 0x81: /* animation/CSR info? */
case 0x36: /* list of hash-things? */
case 0x2B: /* events/sizes? */
case 0x7f: /* vorbis setup (for streams) */
default: /* 0x68=midi?, 0x5A/0xD9=? */
//VGM_LOG("AWC: ignoring unknown tag 0x%02x\n", tag);
break;
}
}
/* in music mode there tags for other streams we don't need, except for vorbis that have one setup packet */
//TODO not correct (assumes 1 tag per stream and channel order doesn't match stream order)
// would need to read N tags and match channel id<>stream id, all vorbis setups are the same though)
/* in music mode there tags for other streams we don't use, except for vorbis. streams have vorbis setup info for channels, but
* channel<>stream order doesn't match, so we need to assign setup to channels. All setups seem to be the same though. */
if (awc->is_streamed && awc->codec == 0x08) {
offset += 0x08 * tag_count;
offset = awc->tags_offset;
offset += 0x08 * awc->stream_info[0].tag_count; /* ignore 1st/music stream */
for (int ch = 0; ch < awc->channels; ch++) {
awc->vorbis_offset[ch] = read_u16(offset + 0x08*ch + 0x00, sf); /* tag offset */
for (int stream = 1; stream < entries; stream++) {
for (int tag = 0; tag < awc->stream_info[stream].tag_count; tag++) {
uint64_t tag_header = read_u64(offset,sf);
uint8_t tag_type = ((tag_header >> 56) & 0xFF); /* 8b */
//uint32_t tag_size = ((tag_header >> 28) & 0x0FFFFFFF); /* 28b */
uint32_t tag_offset = ((tag_header >> 0) & 0x0FFFFFFF); /* 28b */
switch(tag_type) {
case 0x7f: /* vorbis setup */
/* find which channel uses this stream's data */
for (int ch = 0; ch < awc->channels; ch++) {
if (awc->channel_hash[ch] == awc->stream_info[stream].hash_id) {
awc->vorbis_offset[ch] = tag_offset;
//awc->vorbis_size[ch] = tag_size; /* not needed (implicit)*/
break;
}
}
break;
default:
break;
}
offset += 0x08;
}
}
}
@ -531,166 +539,54 @@ fail:
/* ************************************************************************* */
typedef struct {
int start_entry;
int entries;
int32_t channel_skip;
int32_t channel_samples;
uint32_t extradata;
/* derived */
uint32_t chunk_start;
uint32_t chunk_size;
} awc_block_t;
typedef struct {
awc_block_t blk[AWC_MAX_MUSIC_CHANNELS];
} awc_block_info_t;
/* Block format:
* - block header for all channels (needed to find frame start)
* - frames from channel 1
* - ...
* - 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
* 0x08: samples to discard in the beginning of this block (MPEG/XMA2/Vorbis only?)
* 0x0c: samples in channel (for MPEG/XMA2 can vary between channels)
* full samples without removing samples to discard
* (next fields don't exist in later versions for IMA or AT9)
* 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):
* 0x00: "D11A"
* 0x04: frame size
* 0x06: frame samples
* 0x08: flags? (0x0103=AT9, 0x0104=DSP)
* 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 depending on codec (AT9/DSP: none, MPEG/XMA: closest 0x800)
*/
static bool read_awb_block(STREAMFILE* sf, awc_header* awc, awc_block_info_t* bi, uint32_t block_offset) {
uint32_t channel_entry_size, seek_entry_size, extra_entry_size, header_padding;
uint32_t offset = block_offset;
/* read stupid block crap + derived info at once so hopefully it's a bit easier to understand */
switch(awc->codec) {
case 0x08: /* Vorbis */
channel_entry_size = 0x18;
seek_entry_size = 0x04;
extra_entry_size = 0x00;
header_padding = 0x800;
break;
case 0x0F: /* ATRAC9 */
channel_entry_size = 0x10;
seek_entry_size = 0x00;
extra_entry_size = 0x70;
header_padding = 0x00;
break;
default:
goto fail;
}
/* channel info table */
for (int ch = 0; ch < awc->channels; ch++) {
bi->blk[ch].start_entry = read_u32le(offset + 0x00, sf);
bi->blk[ch].entries = read_u32le(offset + 0x04, sf);
bi->blk[ch].channel_skip = read_u32le(offset + 0x08, sf);
bi->blk[ch].channel_samples = read_u32le(offset + 0x0c, sf);
/* others: optional */
offset += channel_entry_size;
}
/* seek table */
for (int ch = 0; ch < awc->channels; ch++) {
offset += bi->blk[ch].entries * seek_entry_size;
}
/* extra table and derived info */
for (int ch = 0; ch < awc->channels; ch++) {
switch(awc->codec) {
case 0x08: /* Vorbis */
/* each "frame" here is actually N vorbis frames then padding up to 0x800 (more or less like a big Ogg page) */
bi->blk[ch].chunk_size = bi->blk[ch].entries * 0x800;
break;
case 0x0F: { /* ATRAC9 */
uint16_t frame_size = read_u16le(offset + 0x04, sf);
bi->blk[ch].chunk_size = bi->blk[ch].entries * frame_size;
bi->blk[ch].extradata = read_u32be(offset + 0x0c, sf);
break;
}
default:
goto fail;
}
offset += extra_entry_size;
}
/* header done, move into data start */
if (header_padding) {
/* padding on the current size rather than file offset (block meant to be read into memory, probably) */
uint32_t header_size = offset - block_offset;
offset = block_offset + align_size_to_block(header_size, header_padding);
}
/* set frame starts per channel */
for (int ch = 0; ch < awc->channels; ch++) {
bi->blk[ch].chunk_start = offset;
offset += bi->blk[ch].chunk_size;
}
/* beyond this is padding until awc.block_chunk */
return true;
fail:
return false;
}
/* ************************************************************************* */
static VGMSTREAM* build_block_vgmstream(STREAMFILE* sf, awc_header* awc, int channel, awc_block_info_t* bi) {
static VGMSTREAM* build_blocks_vgmstream(STREAMFILE* sf, awc_header* awc, int channel) {
VGMSTREAM* vgmstream = NULL;
awc_block_t* blk = &bi->blk[channel];
STREAMFILE* temp_sf = NULL;
int block_channels = 1;
uint32_t substream_size, substream_offset;
//;VGM_LOG("AWC: build ch%i at o=%x, s=%x\n", channel, blk->chunk_start, blk->chunk_size);
/* 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);
if (!temp_sf) goto fail;
substream_offset = 0x00;
substream_size = get_streamfile_size(temp_sf);
/* build the VGMSTREAM */
vgmstream = allocate_vgmstream(block_channels, 0);
if (!vgmstream) goto fail;
vgmstream->sample_rate = awc->sample_rate;
vgmstream->num_samples = blk->channel_samples - blk->channel_skip;
vgmstream->stream_size = blk->chunk_size;
vgmstream->meta_type = meta_AWC;
vgmstream->sample_rate = awc->sample_rate;
vgmstream->num_samples = awc->num_samples;
vgmstream->stream_size = awc->stream_size;
vgmstream->stream_size = substream_size;
switch(awc->codec) {
#ifdef VGM_USE_FFMPEG
case 0x05: { /* XMA2 (X360) */
vgmstream->codec_data = init_ffmpeg_xma2_raw(temp_sf, substream_offset, substream_size, awc->num_samples, block_channels, awc->sample_rate, 0, 0);
if (!vgmstream->codec_data) goto fail;
vgmstream->coding_type = coding_FFmpeg;
vgmstream->layout_type = layout_none;
xma_fix_raw_samples(vgmstream, temp_sf, substream_offset, substream_size, 0, 0,0); /* samples are ok? */
break;
}
#endif
#ifdef VGM_USE_VORBIS
case 0x08: {
vorbis_custom_config cfg = {0};
cfg.channels = 1;
cfg.sample_rate = awc->sample_rate;
cfg.header_offset = awc->vorbis_offset[channel]; /* setup page goes first */
//cfg.skip_samples = skip_samples; //todo
vgmstream->codec_data = init_vorbis_custom(sf, blk->chunk_start, VORBIS_AWC, &cfg);
cfg.header_offset = awc->vorbis_offset[channel]; /* setup page goes separate */
/* note that it needs sf on init to read the header + start offset for later, and temp_sf on decode to read data */
vgmstream->codec_data = init_vorbis_custom(sf, substream_offset, VORBIS_AWC, &cfg);
if (!vgmstream->codec_data) goto fail;
vgmstream->layout_type = layout_none;
vgmstream->coding_type = coding_VORBIS_custom;
@ -701,10 +597,12 @@ static VGMSTREAM* build_block_vgmstream(STREAMFILE* sf, awc_header* awc, int cha
case 0x0F: {
atrac9_config cfg = {0};
/* read from first block (all blocks have it but same thing), see awc_streamfile.h */
uint32_t extradata_offset = awc->stream_offset + 0x10 * awc->channels + 0x70 * channel + 0x0c;
cfg.channels = block_channels;
cfg.encoder_delay = blk->channel_skip;
cfg.config_data = blk->extradata;
;VGM_ASSERT(blk->channel_skip, "AWC discard found\n");
cfg.encoder_delay = 0; //?
cfg.config_data = read_u32be(extradata_offset, sf);
vgmstream->codec_data = init_atrac9(&cfg);
if (!vgmstream->codec_data) goto fail;
@ -718,53 +616,14 @@ static VGMSTREAM* build_block_vgmstream(STREAMFILE* sf, awc_header* awc, int cha
goto fail;
}
if (!vgmstream_open_stream(vgmstream, sf, blk->chunk_start))
if (!vgmstream_open_stream(vgmstream, temp_sf, substream_offset))
goto fail;
close_streamfile(temp_sf);
return vgmstream;
fail:
;VGM_LOG("AWB: can't open decoder\n");
close_vgmstream(vgmstream);
return NULL;
}
/* per channel to possibly simplify block entry skips, though can't be handled right now */
static VGMSTREAM* build_blocks_vgmstream(STREAMFILE* sf, awc_header* awc, int channel) {
VGMSTREAM* vgmstream = NULL;
segmented_layout_data* data = NULL;
int blocks = awc->block_count;
awc_block_info_t bi = {0};
/* init layout */
data = init_layout_segmented(blocks);
if (!data) goto fail;
/* one segment per block of this channel */
for (int i = 0; i < blocks; i++) {
uint32_t block_offset = awc->stream_offset + awc->block_chunk * i;
if (!read_awb_block(sf, awc, &bi, block_offset))
goto fail;
//;VGM_LOG("AWC: block=%i at %x\n", i, block_offset);
data->segments[i] = build_block_vgmstream(sf, awc, channel, &bi);
if (!data->segments[i]) goto fail;
}
/* setup VGMSTREAMs */
if (!setup_layout_segmented(data))
goto fail;
/* build the layout VGMSTREAM */
vgmstream = allocate_segmented_vgmstream(data, 0, 0, 0);
if (!vgmstream) goto fail;
return vgmstream;
fail:
close_vgmstream(vgmstream);
if (!vgmstream)
free_layout_segmented(data);
close_streamfile(temp_sf);
return NULL;
}
@ -810,4 +669,3 @@ fail:
free_layout_layered(data);
return NULL;
}

253
src/meta/awc_streamfile.h Normal file
View File

@ -0,0 +1,253 @@
#ifndef _AWC_STREAMFILE_H_
#define _AWC_STREAMFILE_H_
#include "deblock_streamfile.h"
#include "../util/endianness.h"
#define AWC_MAX_MUSIC_CHANNELS 32 /* seen ~24 */
/* ************************************************************************* */
typedef struct {
int start_entry; /* innacurate! */
int entries;
int32_t channel_skip;
int32_t channel_samples;
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) */
} awc_block_t;
typedef struct {
int big_endian;
uint8_t codec;
int channels;
uint32_t block_offset;
awc_block_t blk[AWC_MAX_MUSIC_CHANNELS];
} awc_block_info_t;
/* Block format:
* - block header for all channels (needed to find frame start)
* - frames from channel 1
* - ...
* - 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
* 0x08: samples to discard in the beginning of this block (MPEG/XMA2/Vorbis only?)
* 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):
* 0x00: "D11A"
* 0x04: frame size
* 0x06: frame samples
* 0x08: flags? (0x0103=AT9, 0x0104=DSP)
* 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
*/
static bool read_awb_block(STREAMFILE* sf, awc_block_info_t* bi) {
read_s32_t read_s32 = bi->big_endian ? read_s32be : read_s32le;
read_u16_t read_u16 = bi->big_endian ? read_u16be : read_u16le;
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) {
case 0x05: /* XMA2 */
channel_entry_size = 0x10;
seek_entry_size = 0x04;
extra_entry_size = 0x00;
header_padding = 0x800;
break;
case 0x08: /* Vorbis */
channel_entry_size = 0x18;
seek_entry_size = 0x04;
extra_entry_size = 0x00;
header_padding = 0x800;
break;
case 0x0F: /* ATRAC9 */
channel_entry_size = 0x10;
seek_entry_size = 0x00;
extra_entry_size = 0x70;
header_padding = 0x00;
break;
default:
goto fail;
}
/* channel info table */
for (int ch = 0; ch < bi->channels; ch++) {
bi->blk[ch].start_entry = read_s32(offset + 0x00, sf);
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 */
offset += channel_entry_size;
}
/* seek table */
for (int ch = 0; ch < channels; ch++) {
offset += bi->blk[ch].entries * seek_entry_size;
}
/* extra table and derived info */
for (int ch = 0; ch < channels; ch++) {
switch(bi->codec) {
case 0x05: /* XMA2 */
case 0x08: /* Vorbis */
/* 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].chunk_size = bi->blk[ch].entries * bi->blk[ch].frame_size;
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;
break;
default:
goto fail;
}
offset += extra_entry_size;
}
/* header done, move into data start */
if (header_padding) {
/* padding on the current size rather than file offset (block meant to be read into memory, probably) */
uint32_t header_size = offset - bi->block_offset;
offset = bi->block_offset + align_size_to_block(header_size, header_padding);
}
/* set frame starts per channel */
for (int ch = 0; ch < channels; ch++) {
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.
* 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) {
if (bi->blk[channel].channel_skip == 0)
return 0;
switch(bi->codec) {
case 0x05: { /* XMA2 */
const uint32_t samples_per_subframe = 512;
uint32_t samples_this_frame;
uint8_t subframes;
uint32_t offset = bi->block_offset + bi->blk[channel].chunk_start;
int repeat_samples = bi->blk[channel].channel_skip;
//TODO: fix (needs proper decoder + sample discard)
/* Repeat samples are the number of decoded samples to discard, but in this streamfile we can't do that.
* Also XMA is VBR, and may encode silent frames with up to 63 subframes yet we may have few repeat samples.
* We could find out how many subframes of 512 samples to skip, then adjust the XMA frame header, though
* output will be slightly off since subframes are related.
*
* For now just skip a full frame depending on the number of subframes vs repeat samples.
* Most files work ok-ish but channels may desync slightly. */
subframes = (read_u8(offset,sf) >> 2) & 0x3F; /* peek into frame header */
samples_this_frame = subframes * samples_per_subframe;
if (repeat_samples >= (int)(samples_this_frame * 0.13)) { /* skip mosts */
return bi->blk[channel].frame_size;
}
else {
return 0;
}
}
case 0x08: /* Vorbis */
/* when data repeats seems to clone exactly the last super-frame */
return bi->blk[channel].frame_size;
case 0x0F: /* ATRAC9 */
default:
VGM_LOG("AWC: found channel skip in codec %x\n", bi->codec); /* not seen */
return 0;
}
}
/* ************************************************************************* */
static void block_callback(STREAMFILE *sf, deblock_io_data* data) {
int channel = data->cfg.track_number;
awc_block_info_t bi = {0};
bi.big_endian = data->cfg.big_endian;
bi.block_offset = data->physical_offset;
bi.channels = data->cfg.track_count;
bi.codec = data->cfg.track_type;
if (!read_awb_block(sf, &bi))
return; //???
uint32_t repeat_size = get_block_repeated_size(sf, &bi, channel);
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;
}
/* 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) {
STREAMFILE* new_sf = NULL;
deblock_config_t cfg = {0};
if (channels >= AWC_MAX_MUSIC_CHANNELS)
return NULL;
cfg.track_number = channel;
cfg.track_count = channels;
cfg.stream_start = stream_offset;
cfg.stream_size = stream_size;
cfg.chunk_size = block_size;
cfg.track_type = codec;
cfg.big_endian = big_endian;
//cfg.physical_offset = stream_offset;
//cfg.logical_size = awc_xma_io_size(sf, &cfg); /* force init */
cfg.block_callback = block_callback;
new_sf = open_wrap_streamfile(sf);
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
//new_sf = open_buffer_streamfile_f(new_sf, 0);
return new_sf;
}
#endif

View File

@ -1,114 +0,0 @@
#ifndef _AWC_XMA_STREAMFILE_H_
#define _AWC_XMA_STREAMFILE_H_
#include "deblock_streamfile.h"
static size_t get_block_header_size(STREAMFILE* sf, off_t offset, int channels);
static size_t get_repeated_data_size(STREAMFILE* sf, off_t next_offset, size_t repeat_samples);
static size_t get_block_skip_count(STREAMFILE* sf, off_t offset, int channel);
static void block_callback(STREAMFILE *sf, deblock_io_data* data) {
const size_t frame_size = 0x800;
int channel = data->cfg.track_number;
int channels = data->cfg.track_count;
/* Blocks have a header then data per channel, each with a different num_samples/frames,
* separate (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 "repeat samples"). */
size_t header_size = get_block_header_size(sf, data->physical_offset, channels);
/* header table entries = frames... I hope */
size_t others_size = get_block_skip_count(sf, data->physical_offset, channel) * frame_size;
//size_t skip_size = read_u32be(data->physical_offset + 0x10*channel + 0x00, sf) * frame_size;
size_t data_size = read_u32be(data->physical_offset + 0x10*channel + 0x04, sf) * frame_size;
size_t repeat_samples = read_u32be(data->physical_offset + 0x10*channel + 0x08, sf);
size_t repeat_size = 0;
data->block_size = data->cfg.chunk_size;
/* if there are repeat samples current block repeats some frames from last block, find out size */
if (repeat_samples) {
off_t data_offset = data->physical_offset + header_size + others_size;
repeat_size = get_repeated_data_size(sf, data_offset, repeat_samples);
}
data->skip_size = header_size + others_size + repeat_size;
data->data_size = data_size - repeat_size;
}
/* block header size, aligned/padded to 0x800 */
static size_t get_block_header_size(STREAMFILE* sf, off_t offset, int channels) {
size_t header_size = 0;
int i;
for (i = 0; i < channels; i++) {
header_size += 0x10;
header_size += read_u32be(offset + 0x10*i + 0x04, sf) * 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* sf, off_t next_offset, size_t repeat_samples) {
const size_t frame_size = 0x800;
const size_t samples_per_subframe = 512;
size_t samples_this_frame;
uint8_t subframes;
//todo: fix this
/* Repeat samples are the number of decoded samples to discard, but in this streamfile we can't do that.
* Also XMA is VBR, and may encode silent frames with up to 63 subframes yet we may have few repeat samples.
* We could find out how many subframes of 512 samples to skip, then adjust the XMA frame header, though
* output will be slightly off since subframes are related.
*
* For now just skip a full frame depending on the number of subframes vs repeat samples.
* Most files work ok-ish but channels may desync slightly. */
subframes = ((uint8_t)read_8bit(next_offset,sf) >> 2) & 0x3F; /* peek into frame header */
samples_this_frame = subframes*samples_per_subframe;
if (repeat_samples >= (int)(samples_this_frame*0.13)) { /* skip mosts */
return frame_size;
}
else {
return 0;
}
}
/* header has a skip value, but somehow it's sometimes bigger than expected (WHY!?!?) so just sum all */
static size_t get_block_skip_count(STREAMFILE* sf, off_t offset, int channel) {
size_t skip_count = 0;
int i;
//skip_size = read_u32be(offset + 0x10*channel + 0x00, sf); /* wrong! */
for (i = 0; i < channel; i++) {
skip_count += read_u32be(offset + 0x10*i + 0x04, sf); /* number of frames of this channel */
}
return skip_count;
}
/* Deblocks interleaved XMA in AWC blocks */
static STREAMFILE* setup_awc_xma_streamfile(STREAMFILE *sf, off_t stream_offset, size_t stream_size, size_t block_size, int channel_count, int channel) {
STREAMFILE *new_sf = NULL;
deblock_config_t cfg = {0};
cfg.track_number = channel;
cfg.track_count = channel_count;
cfg.stream_start = stream_offset;
cfg.stream_size = stream_size;
cfg.chunk_size = block_size;
//cfg.physical_offset = stream_offset;
//cfg.logical_size = awc_xma_io_size(sf, &cfg); /* force init */
cfg.block_callback = block_callback;
new_sf = open_wrap_streamfile(sf);
new_sf = open_io_deblock_streamfile_f(new_sf, &cfg);
//new_sf = open_buffer_streamfile_f(new_sf, 0);
return new_sf;
}
#endif /* _AWC_XMA_STREAMFILE_H_ */