From 8094ce9ebd2cbeb3c77d5eebec55be43624e2a3c Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 17 Jul 2021 19:00:40 +0200 Subject: [PATCH 1/2] Fix clamped subfiles read bug --- src/streamfile.c | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/streamfile.c b/src/streamfile.c index 6c878ec3..424202f4 100644 --- a/src/streamfile.c +++ b/src/streamfile.c @@ -433,8 +433,8 @@ STREAMFILE* open_wrap_streamfile(STREAMFILE *streamfile) { return &this_sf->sf; } -STREAMFILE* open_wrap_streamfile_f(STREAMFILE *streamfile) { - STREAMFILE *new_sf = open_wrap_streamfile(streamfile); +STREAMFILE* open_wrap_streamfile_f(STREAMFILE* streamfile) { + STREAMFILE* new_sf = open_wrap_streamfile(streamfile); if (!new_sf) close_streamfile(streamfile); return new_sf; @@ -445,28 +445,36 @@ STREAMFILE* open_wrap_streamfile_f(STREAMFILE *streamfile) { typedef struct { STREAMFILE sf; - STREAMFILE *inner_sf; + STREAMFILE* inner_sf; off_t start; size_t size; } CLAMP_STREAMFILE; -static size_t clamp_read(CLAMP_STREAMFILE *streamfile, uint8_t *dst, off_t offset, size_t length) { +static size_t clamp_read(CLAMP_STREAMFILE* streamfile, uint8_t* dst, off_t offset, size_t length) { off_t inner_offset = streamfile->start + offset; - size_t clamp_length = length > (streamfile->size - offset) ? (streamfile->size - offset) : length; + size_t clamp_length = length; + + if (offset + length > streamfile->size) { + if (offset >= streamfile->size) + clamp_length = 0; + else + clamp_length = streamfile->size - offset; + } + return streamfile->inner_sf->read(streamfile->inner_sf, dst, inner_offset, clamp_length); } -static size_t clamp_get_size(CLAMP_STREAMFILE *streamfile) { +static size_t clamp_get_size(CLAMP_STREAMFILE* streamfile) { return streamfile->size; } -static off_t clamp_get_offset(CLAMP_STREAMFILE *streamfile) { +static off_t clamp_get_offset(CLAMP_STREAMFILE* streamfile) { return streamfile->inner_sf->get_offset(streamfile->inner_sf) - streamfile->start; } -static void clamp_get_name(CLAMP_STREAMFILE *streamfile, char *buffer, size_t length) { +static void clamp_get_name(CLAMP_STREAMFILE* streamfile, char* buffer, size_t length) { streamfile->inner_sf->get_name(streamfile->inner_sf, buffer, length); /* default */ } -static STREAMFILE *clamp_open(CLAMP_STREAMFILE *streamfile, const char * const filename, size_t buffersize) { +static STREAMFILE* clamp_open(CLAMP_STREAMFILE* streamfile, const char* const filename, size_t buffersize) { char original_filename[PATH_LIMIT]; - STREAMFILE *new_inner_sf = NULL; + STREAMFILE* new_inner_sf = NULL; new_inner_sf = streamfile->inner_sf->open(streamfile->inner_sf,filename,buffersize); streamfile->inner_sf->get_name(streamfile->inner_sf, original_filename, PATH_LIMIT); @@ -478,13 +486,13 @@ static STREAMFILE *clamp_open(CLAMP_STREAMFILE *streamfile, const char * const f return new_inner_sf; } } -static void clamp_close(CLAMP_STREAMFILE *streamfile) { +static void clamp_close(CLAMP_STREAMFILE* streamfile) { streamfile->inner_sf->close(streamfile->inner_sf); free(streamfile); } -STREAMFILE* open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t size) { - CLAMP_STREAMFILE *this_sf = NULL; +STREAMFILE* open_clamp_streamfile(STREAMFILE* streamfile, off_t start, size_t size) { + CLAMP_STREAMFILE* this_sf = NULL; if (!streamfile || size == 0) return NULL; if (start + size > get_streamfile_size(streamfile)) return NULL; @@ -507,8 +515,8 @@ STREAMFILE* open_clamp_streamfile(STREAMFILE *streamfile, off_t start, size_t si return &this_sf->sf; } -STREAMFILE* open_clamp_streamfile_f(STREAMFILE *streamfile, off_t start, size_t size) { - STREAMFILE *new_sf = open_clamp_streamfile(streamfile, start, size); +STREAMFILE* open_clamp_streamfile_f(STREAMFILE* streamfile, off_t start, size_t size) { + STREAMFILE* new_sf = open_clamp_streamfile(streamfile, start, size); if (!new_sf) close_streamfile(streamfile); return new_sf; @@ -899,8 +907,12 @@ STREAMFILE* open_streamfile_by_filename(STREAMFILE* sf, const char* filename) { /* check for non-normalized paths first (ex. txth) */ path = strrchr(fullname, '/'); otherpath = strrchr(fullname, '\\'); - if (otherpath > path) + if (otherpath > path) { //todo cast to ptr? + /* foobar makes paths like "(fake protocol)://(windows path with \)". + * Hack to work around both separators, though probably foo_streamfile + * should just return and handle normalized paths without protocol. */ path = otherpath; + } if (path) { path[1] = '\0'; /* remove name after separator */ From 3741e021b33d000401caa2a6a4e74a8405e53d3f Mon Sep 17 00:00:00 2001 From: bnnm Date: Sat, 17 Jul 2021 19:01:50 +0200 Subject: [PATCH 2/2] Add TXTH first interleave, tweak dual stereo --- doc/TXTH.md | 32 ++++++++++++++++++++++++++++++++ src/meta/txth.c | 39 +++++++++++++++++++++++++++++++++++++-- src/vgmstream.c | 7 +++++-- 3 files changed, 74 insertions(+), 4 deletions(-) diff --git a/doc/TXTH.md b/doc/TXTH.md index 473af4e5..161da158 100644 --- a/doc/TXTH.md +++ b/doc/TXTH.md @@ -204,6 +204,20 @@ Special values: interleave_last = (value)|auto ``` +#### INTERLEAVE IN THE FIRST BLOCK +Similar to the above, in rare cases the file starts with a different interleave (bigger or smaller), then uses another value. + +For example, file has `start_offset` at 0x100, first `interleave_first` of 0x800 then `interleave` of 0x400. + +In trickier cases, file at 0x100 has 0x10 garbage (before each channel data), then data up to 0x800, then interleave of 0x800. So interleave sizes are consistent, but first block has less data. Here we need to set `interleave_first_skip = 0x10` so block sizes can be properly calculated and garbage skipped. Notice that if file was 4ch this means total garbage of 0x40 (`(0x10 garbage + 0x7F0 data) * 4`). + +Be aware that certain features like autodetecting PS-ADPCM loop points may not handle interleave_first at the moment. + +``` +interleave_first = (value) +interleave_first_skip = (value) +``` + #### ID VALUES Validates that `id_value` (normally set as constant value) matches value read at `id_check`. The file will be rejected and won't play if values don't match. @@ -785,6 +799,24 @@ num_samples = data_size interleave_last = auto ``` +#### Kaiketsu Zorori: Mezase! Itazura King (PS2) .txth +``` +codec = PSX + +channels = @0x8 + 1 +sample_rate = 48000 + +interleave = 0x1000 +interleave_first = 0x2000 +interleave_first_skip = 0x10 + +padding_size = auto-empty +num_samples = data_size + +#@0x00 interleave? +#@0x04 number of 0x800 sectors +``` + #### Colin McRae DiRT (PC) .wip.txth ``` # first check that value at 0x00 is really 0x00000000 (rarely needed though) diff --git a/src/meta/txth.c b/src/meta/txth.c index 16ade7b4..74f514ae 100644 --- a/src/meta/txth.c +++ b/src/meta/txth.c @@ -58,6 +58,8 @@ typedef struct { uint32_t interleave; uint32_t interleave_last; + uint32_t interleave_first; + uint32_t interleave_first_skip; uint32_t channels; uint32_t sample_rate; @@ -330,6 +332,8 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) { /* high nibble or low nibble first */ vgmstream->codec_config = txth.codec_mode; } + + vgmstream->allow_dual_stereo = 1; /* AICA and PSX */ break; case coding_PCFX: @@ -369,6 +373,8 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) { vgmstream->interleave_block_size = txth.interleave; vgmstream->layout_type = layout_none; + + vgmstream->allow_dual_stereo = 1; //??? break; case coding_MSADPCM: @@ -456,6 +462,7 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) { } } + vgmstream->allow_dual_stereo = 1; break; #ifdef VGM_USE_MPEG @@ -568,9 +575,19 @@ VGMSTREAM* init_vgmstream_txth(STREAMFILE* sf) { } #endif + if (vgmstream->interleave_block_size) { + if (txth.interleave_first_skip && !txth.interleave_first) + txth.interleave_first = txth.interleave; + if (txth.interleave_first > txth.interleave_first_skip) + txth.interleave_first -= txth.interleave_first_skip; + vgmstream->interleave_first_block_size = txth.interleave_first; + vgmstream->interleave_first_skip = txth.interleave_first_skip; + txth.start_offset += txth.interleave_first_skip; + } + + vgmstream->coding_type = coding; vgmstream->meta_type = meta_TXTH; - vgmstream->allow_dual_stereo = 1; if (!vgmstream_open_stream(vgmstream, txth.sf_body, txth.start_offset)) @@ -648,6 +665,10 @@ static VGMSTREAM* init_subfile(txth_header* txth) { txth->interleave = vgmstream->interleave_block_size; if (!txth->interleave_last) txth->interleave_last = vgmstream->interleave_last_block_size; + if (!txth->interleave_first) + txth->interleave_first = vgmstream->interleave_first_block_size; + if (!txth->interleave_first_skip) + txth->interleave_first_skip = vgmstream->interleave_first_skip; //if (!txth->loop_flag) //? // txth->loop_flag = vgmstream->loop_flag; /* sometimes headers set loop start but getting loop_end before subfile init is hard */ @@ -965,6 +986,19 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha if (!parse_num(txth->sf_head,txth,val, &txth->interleave_last)) goto fail; } } + else if (is_string(key,"interleave_first")) { + if (!parse_num(txth->sf_head,txth,val, &txth->interleave_first)) goto fail; + } + else if (is_string(key,"interleave_first_skip")) { + if (!parse_num(txth->sf_head,txth,val, &txth->interleave_first_skip)) goto fail; + + /* apply */ + if (!txth->data_size_set) { + int skip = txth->interleave_first_skip * txth->channels; + if (txth->data_size && txth->data_size > skip) + txth->data_size -= skip; + } + } /* BASE CONFIG */ else if (is_string(key,"channels")) { @@ -978,7 +1012,6 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha else if (is_string(key,"start_offset")) { if (!parse_num(txth->sf_head,txth,val, &txth->start_offset)) goto fail; - /* apply */ if (!txth->data_size_set) { @@ -1805,6 +1838,8 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_ else { /* known field */ if ((n = is_string_field(val,"interleave"))) value = txth->interleave; else if ((n = is_string_field(val,"interleave_last"))) value = txth->interleave_last; + else if ((n = is_string_field(val,"interleave_first"))) value = txth->interleave_first; + else if ((n = is_string_field(val,"interleave_first_skip")))value = txth->interleave_first_skip; else if ((n = is_string_field(val,"channels"))) value = txth->channels; else if ((n = is_string_field(val,"sample_rate"))) value = txth->sample_rate; else if ((n = is_string_field(val,"start_offset"))) value = txth->start_offset; diff --git a/src/vgmstream.c b/src/vgmstream.c index bd5f3209..64f13f6c 100644 --- a/src/vgmstream.c +++ b/src/vgmstream.c @@ -1648,9 +1648,12 @@ int vgmstream_open_stream_bf(VGMSTREAM* vgmstream, STREAMFILE* sf, off_t start_o offset = start_offset; } else if (is_stereo_codec) { int ch_mod = (ch & 1) ? ch - 1 : ch; /* adjust odd channels (ch 0,1,2,3,4,5 > ch 0,0,2,2,4,4) */ - offset = start_offset + vgmstream->interleave_block_size*ch_mod; + offset = start_offset + vgmstream->interleave_block_size * ch_mod; + } else if (vgmstream->interleave_first_block_size) { + /* start_offset assumes + vgmstream->interleave_first_block_size, maybe should do it here */ + offset = start_offset + (vgmstream->interleave_first_block_size + vgmstream->interleave_first_skip) * ch; } else { - offset = start_offset + vgmstream->interleave_block_size*ch; + offset = start_offset + vgmstream->interleave_block_size * ch; } /* open new one if needed, useful to avoid jumping around when each channel data is too apart