Add TXTH/GENH interleave_last option

This commit is contained in:
bnnm 2019-01-20 00:17:06 +01:00
parent 8f1ec86bda
commit 29bed4b1e0
3 changed files with 36 additions and 5 deletions

View File

@ -10,7 +10,7 @@ When an unsupported file is loaded (for instance "bgm01.snd"), vgmstream tries t
If found and parsed correctly (the TXTH may be rejected if incorrect commands are found) vgmstream will try to play the file as described. Extension must be accepted/added to vgmstream (plugins like foobar2000 only load extensions from a whitelist in formats.c), or one could rename to any supported extension (like .vgmstream), or leave the file extensionless.
You can also use ".(sub).(ext).txth" (if the file is "filename.sub.ext"), to allow mixing slightly different files in the same folder. The "sub" part doesn't need to be an extension, for example:
- 001.1ch.str, 001.1ch.str may use .1ch.txth
- 001.1ch.str, 001.1ch.str may use .1ch.txth
- 003.2ch.str, 003.2ch.str may use .2ch.txth
- etc
@ -51,7 +51,7 @@ A text file with the above commands must be saved as ".vag.txth" or ".txth", not
# * $1|2|3|4: value has size of 8/16/24/32 bit (optional, defaults to 4)
# Examples: @0x10:BE$2 (get big endian 16b value at 0x10)
# - (field): uses current value of a field. Accepted strings:
# - interleave, channels, sample_rate
# - interleave, interleave_last, channels, sample_rate
# - start_offset, data_size
# - num_samples, loop_start_sample, loop_end_sample
# - subsong_count, subsong_offset
@ -116,6 +116,14 @@ value_sub|value_- = (number)|(offset)|(field)
# Interleave 0 means "stereo mode" for some codecs (IMA, AICA, etc).
interleave = (number)|(offset)|(field)|half_size
# Interleave in the last block [OPTIONAL]
# - auto: calculate based on channels, interleave and data_size/start_offset
# In some files with interleaved data the last block is smaller than interleave,
# so interleave must be smaller in the last block. This fixes decoding glitches
# for those files. Note that this doesn't affect files with padding data in the
# last block (as the interleave itself is constant).
interleave_last = (number)|(auto)
# Validate that id_value matches value at id_offset [OPTIONAL]
# Can be redefined several times, it's checked whenever a new id_offset is found.
id_value = (number)|(offset)|(field)

View File

@ -39,8 +39,9 @@ typedef enum {
typedef struct {
genh_type codec;
int codec_mode;
size_t interleave;
size_t interleave;
size_t interleave_last;
int channels;
int32_t sample_rate;
@ -153,6 +154,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
case coding_AICA:
case coding_APPLE_IMA4:
vgmstream->interleave_block_size = genh.interleave;
vgmstream->interleave_last_block_size = genh.interleave_last;
if (vgmstream->channels > 1)
{
if (coding == coding_SDX2) {
@ -202,6 +204,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
case coding_PCFX:
vgmstream->interleave_block_size = genh.interleave;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_last_block_size = genh.interleave_last;
if (genh.codec_mode >= 0 && genh.codec_mode <= 3)
vgmstream->codec_config = genh.codec_mode;
break;
@ -227,11 +230,13 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
if (genh.codec_mode == 1) { /* mono interleave */
coding = coding_XBOX_IMA_int;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_last_block_size = genh.interleave_last;
vgmstream->interleave_block_size = genh.interleave;
}
else { /* 1ch mono, or stereo interleave */
vgmstream->layout_type = genh.interleave ? layout_interleave : layout_none;
vgmstream->interleave_block_size = genh.interleave;
vgmstream->interleave_last_block_size = genh.interleave_last;
if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0)
goto fail; /* only 2ch+..+2ch layout is known */
}
@ -245,6 +250,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
if (!genh.interleave) goto fail;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = genh.interleave;
vgmstream->interleave_last_block_size = genh.interleave_last;
} else if (genh.coef_interleave_type == 1) {
if (!genh.interleave) goto fail;
coding = coding_NGC_DSP_subint;
@ -419,8 +425,8 @@ static int parse_genh(STREAMFILE * streamFile, genh_header * genh) {
genh->coef_split_spacing = read_32bitLE(0x38,streamFile);
}
/* extended fields */
if (header_size >= 0x54) {
/* extended + reserved fields */
if (header_size >= 0x100) {
genh->num_samples = read_32bitLE(0x40,streamFile);
genh->skip_samples = read_32bitLE(0x44,streamFile); /* for FFmpeg based codecs */
genh->skip_samples_mode = read_8bit(0x48,streamFile); /* 0=autodetect, 1=force manual value @ 0x44 */
@ -430,6 +436,7 @@ static int parse_genh(STREAMFILE * streamFile, genh_header * genh) {
if ((genh->codec == XMA1 || genh->codec == XMA2) && genh->codec_mode==0)
genh->codec_mode = read_8bit(0x4a,streamFile);
genh->data_size = read_32bitLE(0x50,streamFile);
genh->interleave_last = read_32bitLE(0x54,streamFile);
}
if (genh->data_size == 0)

View File

@ -49,6 +49,7 @@ typedef struct {
uint32_t id_offset;
uint32_t interleave;
uint32_t interleave_last;
uint32_t channels;
uint32_t sample_rate;
@ -228,6 +229,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
case coding_AICA:
case coding_APPLE_IMA4:
vgmstream->interleave_block_size = txth.interleave;
vgmstream->interleave_last_block_size = txth.interleave_last;
if (vgmstream->channels > 1)
{
if (coding == coding_SDX2) {
@ -277,6 +279,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
case coding_PCFX:
vgmstream->interleave_block_size = txth.interleave;
vgmstream->interleave_last_block_size = txth.interleave_last;
vgmstream->layout_type = layout_interleave;
if (txth.codec_mode >= 0 && txth.codec_mode <= 3)
vgmstream->codec_config = txth.codec_mode;
@ -304,10 +307,12 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
coding = coding_XBOX_IMA_int;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_block_size = txth.interleave;
vgmstream->interleave_last_block_size = txth.interleave_last;
}
else { /* 1ch mono, or stereo interleave */
vgmstream->layout_type = txth.interleave ? layout_interleave : layout_none;
vgmstream->interleave_block_size = txth.interleave;
vgmstream->interleave_last_block_size = txth.interleave_last;
if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0)
goto fail; /* only 2ch+..+2ch layout is known */
}
@ -320,6 +325,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
if (txth.channels > 1 && txth.codec_mode == 0) {
if (!txth.interleave) goto fail;
vgmstream->layout_type = layout_interleave;
vgmstream->interleave_last_block_size = txth.interleave_last;
vgmstream->interleave_block_size = txth.interleave;
} else if (txth.channels > 1 && txth.codec_mode == 1) {
if (!txth.interleave) goto fail;
@ -655,6 +661,15 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
if (!parse_num(txth->streamHead,txth,val, &txth->interleave)) goto fail;
}
}
else if (0==strcmp(key,"interleave_last")) {
if (0==strcmp(val,"auto")) {
if (txth->channels > 0 && txth->interleave > 0)
txth->interleave_last = (txth->data_size % (txth->interleave * txth->channels)) / txth->channels;
}
else {
if (!parse_num(txth->streamHead,txth,val, &txth->interleave_last)) goto fail;
}
}
else if (0==strcmp(key,"channels")) {
if (!parse_num(txth->streamHead,txth,val, &txth->channels)) goto fail;
}
@ -901,6 +916,7 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v
}
else { /* known field */
if (0==strcmp(val,"interleave")) *out_value = txth->interleave;
if (0==strcmp(val,"interleave_last")) *out_value = txth->interleave_last;
else if (0==strcmp(val,"channels")) *out_value = txth->channels;
else if (0==strcmp(val,"sample_rate")) *out_value = txth->sample_rate;
else if (0==strcmp(val,"start_offset")) *out_value = txth->start_offset;