mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-25 22:38:11 +01:00
Add TXTH/GENH interleave_last option
This commit is contained in:
parent
8f1ec86bda
commit
29bed4b1e0
10
doc/TXTH.md
10
doc/TXTH.md
@ -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)
|
# * $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)
|
# Examples: @0x10:BE$2 (get big endian 16b value at 0x10)
|
||||||
# - (field): uses current value of a field. Accepted strings:
|
# - (field): uses current value of a field. Accepted strings:
|
||||||
# - interleave, channels, sample_rate
|
# - interleave, interleave_last, channels, sample_rate
|
||||||
# - start_offset, data_size
|
# - start_offset, data_size
|
||||||
# - num_samples, loop_start_sample, loop_end_sample
|
# - num_samples, loop_start_sample, loop_end_sample
|
||||||
# - subsong_count, subsong_offset
|
# - 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 0 means "stereo mode" for some codecs (IMA, AICA, etc).
|
||||||
interleave = (number)|(offset)|(field)|half_size
|
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]
|
# 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.
|
# Can be redefined several times, it's checked whenever a new id_offset is found.
|
||||||
id_value = (number)|(offset)|(field)
|
id_value = (number)|(offset)|(field)
|
||||||
|
@ -39,8 +39,9 @@ typedef enum {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
genh_type codec;
|
genh_type codec;
|
||||||
int codec_mode;
|
int codec_mode;
|
||||||
size_t interleave;
|
|
||||||
|
|
||||||
|
size_t interleave;
|
||||||
|
size_t interleave_last;
|
||||||
int channels;
|
int channels;
|
||||||
int32_t sample_rate;
|
int32_t sample_rate;
|
||||||
|
|
||||||
@ -153,6 +154,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
|
|||||||
case coding_AICA:
|
case coding_AICA:
|
||||||
case coding_APPLE_IMA4:
|
case coding_APPLE_IMA4:
|
||||||
vgmstream->interleave_block_size = genh.interleave;
|
vgmstream->interleave_block_size = genh.interleave;
|
||||||
|
vgmstream->interleave_last_block_size = genh.interleave_last;
|
||||||
if (vgmstream->channels > 1)
|
if (vgmstream->channels > 1)
|
||||||
{
|
{
|
||||||
if (coding == coding_SDX2) {
|
if (coding == coding_SDX2) {
|
||||||
@ -202,6 +204,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
|
|||||||
case coding_PCFX:
|
case coding_PCFX:
|
||||||
vgmstream->interleave_block_size = genh.interleave;
|
vgmstream->interleave_block_size = genh.interleave;
|
||||||
vgmstream->layout_type = layout_interleave;
|
vgmstream->layout_type = layout_interleave;
|
||||||
|
vgmstream->interleave_last_block_size = genh.interleave_last;
|
||||||
if (genh.codec_mode >= 0 && genh.codec_mode <= 3)
|
if (genh.codec_mode >= 0 && genh.codec_mode <= 3)
|
||||||
vgmstream->codec_config = genh.codec_mode;
|
vgmstream->codec_config = genh.codec_mode;
|
||||||
break;
|
break;
|
||||||
@ -227,11 +230,13 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
|
|||||||
if (genh.codec_mode == 1) { /* mono interleave */
|
if (genh.codec_mode == 1) { /* mono interleave */
|
||||||
coding = coding_XBOX_IMA_int;
|
coding = coding_XBOX_IMA_int;
|
||||||
vgmstream->layout_type = layout_interleave;
|
vgmstream->layout_type = layout_interleave;
|
||||||
|
vgmstream->interleave_last_block_size = genh.interleave_last;
|
||||||
vgmstream->interleave_block_size = genh.interleave;
|
vgmstream->interleave_block_size = genh.interleave;
|
||||||
}
|
}
|
||||||
else { /* 1ch mono, or stereo interleave */
|
else { /* 1ch mono, or stereo interleave */
|
||||||
vgmstream->layout_type = genh.interleave ? layout_interleave : layout_none;
|
vgmstream->layout_type = genh.interleave ? layout_interleave : layout_none;
|
||||||
vgmstream->interleave_block_size = genh.interleave;
|
vgmstream->interleave_block_size = genh.interleave;
|
||||||
|
vgmstream->interleave_last_block_size = genh.interleave_last;
|
||||||
if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0)
|
if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0)
|
||||||
goto fail; /* only 2ch+..+2ch layout is known */
|
goto fail; /* only 2ch+..+2ch layout is known */
|
||||||
}
|
}
|
||||||
@ -245,6 +250,7 @@ VGMSTREAM * init_vgmstream_genh(STREAMFILE *streamFile) {
|
|||||||
if (!genh.interleave) goto fail;
|
if (!genh.interleave) goto fail;
|
||||||
vgmstream->layout_type = layout_interleave;
|
vgmstream->layout_type = layout_interleave;
|
||||||
vgmstream->interleave_block_size = genh.interleave;
|
vgmstream->interleave_block_size = genh.interleave;
|
||||||
|
vgmstream->interleave_last_block_size = genh.interleave_last;
|
||||||
} else if (genh.coef_interleave_type == 1) {
|
} else if (genh.coef_interleave_type == 1) {
|
||||||
if (!genh.interleave) goto fail;
|
if (!genh.interleave) goto fail;
|
||||||
coding = coding_NGC_DSP_subint;
|
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);
|
genh->coef_split_spacing = read_32bitLE(0x38,streamFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* extended fields */
|
/* extended + reserved fields */
|
||||||
if (header_size >= 0x54) {
|
if (header_size >= 0x100) {
|
||||||
genh->num_samples = read_32bitLE(0x40,streamFile);
|
genh->num_samples = read_32bitLE(0x40,streamFile);
|
||||||
genh->skip_samples = read_32bitLE(0x44,streamFile); /* for FFmpeg based codecs */
|
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 */
|
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)
|
if ((genh->codec == XMA1 || genh->codec == XMA2) && genh->codec_mode==0)
|
||||||
genh->codec_mode = read_8bit(0x4a,streamFile);
|
genh->codec_mode = read_8bit(0x4a,streamFile);
|
||||||
genh->data_size = read_32bitLE(0x50,streamFile);
|
genh->data_size = read_32bitLE(0x50,streamFile);
|
||||||
|
genh->interleave_last = read_32bitLE(0x54,streamFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (genh->data_size == 0)
|
if (genh->data_size == 0)
|
||||||
|
@ -49,6 +49,7 @@ typedef struct {
|
|||||||
uint32_t id_offset;
|
uint32_t id_offset;
|
||||||
|
|
||||||
uint32_t interleave;
|
uint32_t interleave;
|
||||||
|
uint32_t interleave_last;
|
||||||
uint32_t channels;
|
uint32_t channels;
|
||||||
uint32_t sample_rate;
|
uint32_t sample_rate;
|
||||||
|
|
||||||
@ -228,6 +229,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
|||||||
case coding_AICA:
|
case coding_AICA:
|
||||||
case coding_APPLE_IMA4:
|
case coding_APPLE_IMA4:
|
||||||
vgmstream->interleave_block_size = txth.interleave;
|
vgmstream->interleave_block_size = txth.interleave;
|
||||||
|
vgmstream->interleave_last_block_size = txth.interleave_last;
|
||||||
if (vgmstream->channels > 1)
|
if (vgmstream->channels > 1)
|
||||||
{
|
{
|
||||||
if (coding == coding_SDX2) {
|
if (coding == coding_SDX2) {
|
||||||
@ -277,6 +279,7 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
|||||||
|
|
||||||
case coding_PCFX:
|
case coding_PCFX:
|
||||||
vgmstream->interleave_block_size = txth.interleave;
|
vgmstream->interleave_block_size = txth.interleave;
|
||||||
|
vgmstream->interleave_last_block_size = txth.interleave_last;
|
||||||
vgmstream->layout_type = layout_interleave;
|
vgmstream->layout_type = layout_interleave;
|
||||||
if (txth.codec_mode >= 0 && txth.codec_mode <= 3)
|
if (txth.codec_mode >= 0 && txth.codec_mode <= 3)
|
||||||
vgmstream->codec_config = txth.codec_mode;
|
vgmstream->codec_config = txth.codec_mode;
|
||||||
@ -304,10 +307,12 @@ VGMSTREAM * init_vgmstream_txth(STREAMFILE *streamFile) {
|
|||||||
coding = coding_XBOX_IMA_int;
|
coding = coding_XBOX_IMA_int;
|
||||||
vgmstream->layout_type = layout_interleave;
|
vgmstream->layout_type = layout_interleave;
|
||||||
vgmstream->interleave_block_size = txth.interleave;
|
vgmstream->interleave_block_size = txth.interleave;
|
||||||
|
vgmstream->interleave_last_block_size = txth.interleave_last;
|
||||||
}
|
}
|
||||||
else { /* 1ch mono, or stereo interleave */
|
else { /* 1ch mono, or stereo interleave */
|
||||||
vgmstream->layout_type = txth.interleave ? layout_interleave : layout_none;
|
vgmstream->layout_type = txth.interleave ? layout_interleave : layout_none;
|
||||||
vgmstream->interleave_block_size = txth.interleave;
|
vgmstream->interleave_block_size = txth.interleave;
|
||||||
|
vgmstream->interleave_last_block_size = txth.interleave_last;
|
||||||
if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0)
|
if (vgmstream->channels > 2 && vgmstream->channels % 2 != 0)
|
||||||
goto fail; /* only 2ch+..+2ch layout is known */
|
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.channels > 1 && txth.codec_mode == 0) {
|
||||||
if (!txth.interleave) goto fail;
|
if (!txth.interleave) goto fail;
|
||||||
vgmstream->layout_type = layout_interleave;
|
vgmstream->layout_type = layout_interleave;
|
||||||
|
vgmstream->interleave_last_block_size = txth.interleave_last;
|
||||||
vgmstream->interleave_block_size = txth.interleave;
|
vgmstream->interleave_block_size = txth.interleave;
|
||||||
} else if (txth.channels > 1 && txth.codec_mode == 1) {
|
} else if (txth.channels > 1 && txth.codec_mode == 1) {
|
||||||
if (!txth.interleave) goto fail;
|
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;
|
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")) {
|
else if (0==strcmp(key,"channels")) {
|
||||||
if (!parse_num(txth->streamHead,txth,val, &txth->channels)) goto fail;
|
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 */
|
else { /* known field */
|
||||||
if (0==strcmp(val,"interleave")) *out_value = txth->interleave;
|
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,"channels")) *out_value = txth->channels;
|
||||||
else if (0==strcmp(val,"sample_rate")) *out_value = txth->sample_rate;
|
else if (0==strcmp(val,"sample_rate")) *out_value = txth->sample_rate;
|
||||||
else if (0==strcmp(val,"start_offset")) *out_value = txth->start_offset;
|
else if (0==strcmp(val,"start_offset")) *out_value = txth->start_offset;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user