mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-12-21 19:05:52 +01:00
115 lines
4.7 KiB
C
115 lines
4.7 KiB
C
#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_ */
|