mirror of
https://github.com/vgmstream/vgmstream.git
synced 2024-11-24 23:10:10 +01:00
Clean DSP meta once again
This commit is contained in:
parent
cc537270c1
commit
751d5c5f0f
@ -5,7 +5,6 @@
|
||||
|
||||
/* If these variables are packed properly in the struct (one after another)
|
||||
* then this is actually how they are laid out in the file, albeit big-endian */
|
||||
|
||||
struct dsp_header {
|
||||
uint32_t sample_count;
|
||||
uint32_t nibble_count;
|
||||
@ -66,57 +65,73 @@ static int read_dsp_header_le(struct dsp_header *header, off_t offset, STREAMFIL
|
||||
return read_dsp_header_endian(header, offset, file, 0);
|
||||
}
|
||||
|
||||
/* ********************************* */
|
||||
|
||||
static void setup_vgmstream_dsp(VGMSTREAM* vgmstream, struct dsp_header* ch_header) {
|
||||
typedef struct {
|
||||
/* basic config */
|
||||
int little_endian;
|
||||
int channel_count;
|
||||
int max_channels;
|
||||
|
||||
off_t header_offset; /* standard DSP header */
|
||||
size_t header_spacing; /* distance between DSP header of other channels */
|
||||
off_t start_offset; /* data start */
|
||||
size_t interleave; /* distance between data of other channels */
|
||||
|
||||
meta_t meta_type;
|
||||
|
||||
/* hacks */
|
||||
int force_loop; /* force full loop */
|
||||
int fix_looping; /* fix loop end going past num_samples */
|
||||
int fix_loop_start; /* weird files with bad loop start */
|
||||
int single_header; /* all channels share header, thus totals are off */
|
||||
int ignore_header_agreement; /* sometimes there are minor differences between headers */
|
||||
} dsp_meta;
|
||||
|
||||
#define COMMON_DSP_MAX_CHANNELS 6
|
||||
|
||||
/* Common parser for most DSPs that are basically the same with minor changes.
|
||||
* Custom variants will just concatenate or interleave standard DSP headers and data,
|
||||
* so we make sure to validate read vs expected values, based on dsp_meta config. */
|
||||
static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *dspm) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
int i, j;
|
||||
int loop_flag;
|
||||
struct dsp_header ch_header[COMMON_DSP_MAX_CHANNELS];
|
||||
|
||||
/* set coeffs and initial history (usually 0) */
|
||||
for (i = 0; i < vgmstream->channels; i++){
|
||||
for (j = 0; j < 16; j++) {
|
||||
vgmstream->ch[i].adpcm_coef[j] = ch_header[i].coef[j];
|
||||
}
|
||||
vgmstream->ch[i].adpcm_history1_16 = ch_header[i].initial_hist1;
|
||||
vgmstream->ch[i].adpcm_history2_16 = ch_header[i].initial_hist2;
|
||||
}
|
||||
}
|
||||
|
||||
static int dsp_load_header_endian(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t spacing, int big_endian) {
|
||||
int i;
|
||||
if (dspm->channel_count > dspm->max_channels)
|
||||
goto fail;
|
||||
if (dspm->channel_count > COMMON_DSP_MAX_CHANNELS)
|
||||
goto fail;
|
||||
|
||||
/* load standard dsp header per channel */
|
||||
for (i = 0; i < channels; i++) {
|
||||
if (read_dsp_header_endian(&ch_header[i], offset + i*spacing, streamFile, big_endian))
|
||||
/* load standard DSP header per channel */
|
||||
{
|
||||
for (i = 0; i < dspm->channel_count; i++) {
|
||||
if (read_dsp_header_endian(&ch_header[i], dspm->header_offset + i*dspm->header_spacing, streamFile, !dspm->little_endian))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
//static int dsp_load_header(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t spacing) {
|
||||
// return dsp_load_header_endian(ch_header, channels, streamFile, offset, spacing, 1);
|
||||
//}
|
||||
//static int dsp_load_header_le(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t spacing) {
|
||||
// return dsp_load_header_endian(ch_header, channels, streamFile, offset, spacing, 0);
|
||||
//}
|
||||
static int check_dsp_format(struct dsp_header* ch_header, int channels) {
|
||||
int i;
|
||||
|
||||
/* fix bad/fixed value in loop start */
|
||||
if (dspm->fix_loop_start) {
|
||||
for (i = 0; i < dspm->channel_count; i++) {
|
||||
if (ch_header[i].loop_flag)
|
||||
ch_header[i].loop_start_offset = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
/* check type==0 and gain==0 */
|
||||
for (i = 0; i < channels; i++) {
|
||||
{
|
||||
for (i = 0; i < dspm->channel_count; i++) {
|
||||
if (ch_header[i].format || ch_header[i].gain)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
static int check_dsp_samples(struct dsp_header* ch_header, int channels) {
|
||||
int i;
|
||||
|
||||
/* check for agreement between channels */
|
||||
for (i = 0; i < channels - 1; i++) {
|
||||
if (!dspm->ignore_header_agreement) {
|
||||
for (i = 0; i < dspm->channel_count - 1; i++) {
|
||||
if (ch_header[i].sample_count != ch_header[i+1].sample_count ||
|
||||
ch_header[i].nibble_count != ch_header[i+1].nibble_count ||
|
||||
ch_header[i].sample_rate != ch_header[i+1].sample_rate ||
|
||||
@ -126,120 +141,46 @@ static int check_dsp_samples(struct dsp_header* ch_header, int channels) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
static int check_dsp_initial_ps(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t interleave) {
|
||||
int i;
|
||||
|
||||
/* check initial predictor/scale */
|
||||
/* check expected initial predictor/scale */
|
||||
{
|
||||
int channels = dspm->channel_count;
|
||||
if (dspm->single_header)
|
||||
channels = 1;
|
||||
|
||||
for (i = 0; i < channels; i++) {
|
||||
off_t start_offset = offset + i*interleave;
|
||||
if (ch_header[i].initial_ps != (uint8_t)read_8bit(start_offset, streamFile)){
|
||||
off_t channel_offset = dspm->start_offset + i*dspm->interleave;
|
||||
if (ch_header[i].initial_ps != (uint8_t)read_8bit(channel_offset, streamFile))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
static int check_dsp_loop_ps(struct dsp_header* ch_header, int channels, STREAMFILE *streamFile, off_t offset, size_t interleave) {
|
||||
int i;
|
||||
/* check expected loop predictor/scale */
|
||||
if (ch_header[0].loop_flag) {
|
||||
int channels = dspm->channel_count;
|
||||
if (dspm->single_header)
|
||||
channels = 1;
|
||||
|
||||
if (!ch_header[0].loop_flag)
|
||||
return 1;
|
||||
|
||||
/* check loop predictor/scale */
|
||||
for (i = 0; i < channels; i++) {
|
||||
off_t loop_offset = ch_header[i].loop_start_offset;
|
||||
if (interleave) {
|
||||
if (dspm->interleave) {
|
||||
loop_offset = loop_offset / 16 * 8;
|
||||
loop_offset = (loop_offset / interleave * interleave * channels) + (loop_offset % interleave);
|
||||
loop_offset = (loop_offset / dspm->interleave * dspm->interleave * channels) + (loop_offset % dspm->interleave);
|
||||
}
|
||||
|
||||
if (ch_header[i].loop_ps != (uint8_t)read_8bit(offset + i*interleave + loop_offset,streamFile))
|
||||
if (ch_header[i].loop_ps != (uint8_t)read_8bit(dspm->start_offset + i*dspm->interleave + loop_offset,streamFile))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 1;
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ********************************* */
|
||||
|
||||
/* common parser config as most DSPs are basically the same with minor changes */
|
||||
typedef struct {
|
||||
int little_endian;
|
||||
int channel_count;
|
||||
int max_channels;
|
||||
|
||||
int force_loop; /* force full loop */
|
||||
int fix_looping; /* fix loop end going past num_samples */
|
||||
int fix_loop_start; /* weird files with bad loop start */
|
||||
int single_header; /* all channels share header, thus totals are off */
|
||||
int ignore_header_agreement; /* sometimes there are minor differences between headers */
|
||||
int ignore_loop_check; /* loop info in header should match data, but sometimes it's weird */ //todo check if needed anymore
|
||||
|
||||
off_t header_offset;
|
||||
size_t header_spacing;
|
||||
off_t start_offset;
|
||||
size_t interleave;
|
||||
|
||||
meta_t meta_type;
|
||||
} dsp_meta;
|
||||
|
||||
#define COMMON_DSP_MAX_CHANNELS 6
|
||||
static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *dspm) {
|
||||
VGMSTREAM * vgmstream = NULL;
|
||||
int loop_flag;
|
||||
struct dsp_header ch_header[COMMON_DSP_MAX_CHANNELS];
|
||||
|
||||
if (dspm->channel_count > dspm->max_channels)
|
||||
goto fail;
|
||||
if (dspm->channel_count > COMMON_DSP_MAX_CHANNELS)
|
||||
goto fail;
|
||||
|
||||
|
||||
/* read dsp */
|
||||
if (!dsp_load_header_endian(ch_header, dspm->channel_count, streamFile,dspm->header_offset,dspm->header_spacing, !dspm->little_endian))
|
||||
goto fail;
|
||||
|
||||
if (dspm->fix_loop_start) {
|
||||
int i;
|
||||
for (i = 0; i < dspm->channel_count; i++) {
|
||||
/* bad/fixed value in loop start */
|
||||
if (ch_header[i].loop_flag)
|
||||
ch_header[i].loop_start_offset = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
if (!check_dsp_format(ch_header, dspm->channel_count))
|
||||
goto fail;
|
||||
|
||||
if (!dspm->ignore_header_agreement && !check_dsp_samples(ch_header, dspm->channel_count))
|
||||
goto fail;
|
||||
|
||||
if (dspm->single_header && !check_dsp_initial_ps(ch_header, 1, streamFile,dspm->start_offset,dspm->interleave))
|
||||
goto fail;
|
||||
if (!dspm->single_header && !check_dsp_initial_ps(ch_header, dspm->channel_count, streamFile,dspm->start_offset,dspm->interleave))
|
||||
goto fail;
|
||||
|
||||
if (!dspm->ignore_loop_check) {
|
||||
if (dspm->single_header && !check_dsp_loop_ps(ch_header, 1, streamFile,dspm->start_offset,dspm->interleave))
|
||||
goto fail;
|
||||
if (!dspm->single_header && !check_dsp_loop_ps(ch_header, dspm->channel_count, streamFile,dspm->start_offset,dspm->interleave))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* all done, must be DSP */
|
||||
|
||||
loop_flag = ch_header[0].loop_flag;
|
||||
if (dspm->force_loop)
|
||||
loop_flag = 1;
|
||||
|
||||
|
||||
/* build the VGMSTREAM */
|
||||
vgmstream = allocate_vgmstream(dspm->channel_count,loop_flag);
|
||||
if (!vgmstream) goto fail;
|
||||
@ -258,7 +199,16 @@ static VGMSTREAM * init_vgmstream_dsp_common(STREAMFILE *streamFile, dsp_meta *d
|
||||
vgmstream->layout_type = layout_none;
|
||||
vgmstream->interleave_block_size = dspm->interleave;
|
||||
|
||||
setup_vgmstream_dsp(vgmstream, ch_header);
|
||||
{
|
||||
/* set coefs and initial history (usually 0) */
|
||||
for (i = 0; i < vgmstream->channels; i++) {
|
||||
for (j = 0; j < 16; j++) {
|
||||
vgmstream->ch[i].adpcm_coef[j] = ch_header[i].coef[j];
|
||||
}
|
||||
vgmstream->ch[i].adpcm_history1_16 = ch_header[i].initial_hist1;
|
||||
vgmstream->ch[i].adpcm_history2_16 = ch_header[i].initial_hist2;
|
||||
}
|
||||
}
|
||||
|
||||
/* don't know why, but it does happen*/
|
||||
if (dspm->fix_looping && vgmstream->loop_end_sample > vgmstream->num_samples)
|
||||
@ -291,7 +241,8 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std(STREAMFILE *streamFile) {
|
||||
int i, channel_count;
|
||||
|
||||
/* checks */
|
||||
/* .dsp: standard, .adp: Dr. Muto/Battalion Wars (GC) mono files */
|
||||
/* .dsp: standard
|
||||
* .adp: Dr. Muto/Battalion Wars (GC) mono files */
|
||||
if (!check_extensions(streamFile, "dsp,adp"))
|
||||
goto fail;
|
||||
|
||||
@ -519,9 +470,7 @@ fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Some very simple stereo variants of standard dsp just use the standard header
|
||||
* twice and add interleave, or just concatenate the channels. We'll support them all here. */
|
||||
/* ********************************* */
|
||||
|
||||
/* .stm - Intelligent Systems + others (same programmers) full interleaved dsp [Paper Mario TTYD (GC), Fire Emblem: POR (GC), Cubivore (GC)] */
|
||||
VGMSTREAM * init_vgmstream_ngc_dsp_stm(STREAMFILE *streamFile) {
|
||||
@ -613,7 +562,6 @@ VGMSTREAM * init_vgmstream_ngc_dsp_std_int(STREAMFILE *streamFile) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
return init_vgmstream_dsp_common(streamFile, &dspm);
|
||||
fail:
|
||||
return NULL;
|
||||
|
Loading…
Reference in New Issue
Block a user