Add more TXTH chunk options

This commit is contained in:
bnnm 2021-10-10 13:32:38 +02:00
parent d864d0ce45
commit c1e0143881
3 changed files with 114 additions and 52 deletions

View File

@ -410,28 +410,42 @@ subfile_extension = (string)
```
#### CHUNK DEINTERLEAVING
Some files interleave data chunks, for example 3 stereo songs pasted together, alternating 0x10000 bytes of data each. These settings allow vgmstream to play one of the chunks while ignoring the rest (read 0x10000 data, skip 0x10000*2).
File is first "dechunked" then played with using other settings (`start_offset` would point within the internal dechunked" file). It can be used to remove garbage data that affects decoding, too.
Some files interleave data chunks, for example 3 stereo songs pasted together, alternating 0x10000 bytes of data each. Or maybe 0x100 of useless header + 0x10000 of valid data. Chunk settings allow vgmstream to play valid chunks while ignoring the rest (read 0x10000 data, skip rest).
File is first "dechunked" before being played, so other settings work over this final file (`start_offset` would be a point within the internal dechunked" file). Use combinations of chunk settings to make vgmstream "see" only actual codec data.
You need to set:
Main settings:
- `chunk_count`: total number of interleaved chunks (ex. 3=3 interleaved songs)
- `chunk_number`: first chunk to start (ex. 1=0x00000, 2=0x10000, 3=0x20000...)
* If you set `subsong_count` and `chunk_count` first, `chunk_number` will be auto-set per subsong (subsong 1 starts from chunk number 1, subsong 2 from chunk 2, etc)
- `chunk_start`: absolute offset where chunks start (normally 0x00)
- `chunk_size`: amount of data in a single chunk (ex. 0x10000)
For fine-tuning you can optionally set (before `chunk_size`, for reasons):
- `chunk_header_size`: header to skip before chunk data (part of chunk_size)
- `chunk_data_size`: actual data size (part of chunk_size, rest is header/padding)
So, if you set size to 0x1000, header_size 0x100, data_size is implicitly 0xF00, or if size is 0x1000 and data_size 0x800 last 0x200 is ignored padding. Use combinations of the above to make vgmstream "see" only actual codec data.
Optional settings (set before main):
- `chunk_number`: first chunk to start (ex. 1=0x00000, 2=0x10000, 3=0x20000...)
- If you set `subsong_count` and `chunk_count` first, `chunk_number` will be auto-set per subsong (subsong 1 starts from chunk number 1, subsong 2 from chunk 2, etc)
- `chunk_header_size`: header to skip before chunk data (part of chunk_size)
- If size is 0x1000 and header_size 0x100, data_size is implicitly set to 0xF00
- `chunk_data_size`: actual data size (part of chunk_size, rest is header/padding)
- If size is 0x1000 and data_size 0x800 last 0x200 is ignored padding.
Dynamic settings (set before main, requires `chunk_header_size`):
- `chunk_value`: ignores chunks that don't match this value at chunk offset 0x00 (32-bit, in `chunk_endianness`)
- `chunk_size_offset`: reads chunk size at this offset, in header (32-bit in `chunk_endianness`).
- `chunk_endianness`: sets endianness of the above values
For technical reasons, "dechunking" activates when setting all main settings, so set optional config first. Note that config is static (not per-chunk), so `chunk_size = @0x10` is read from the beginning of the file once, not every time a new chunk is found.
```
chunk_count = (value)
chunk_number = (value)
chunk_start = (value)
chunk_size = (value)
chunk_number = (value)
chunk_header_size = (value)
chunk_data_size = (value)
chunk_size = (value)
chunk_value = (value)
chunk_size_offset = (value)
chunk_endian = LE|BE
```
#### NAME TABLE

View File

@ -118,6 +118,9 @@ typedef struct {
uint32_t chunk_count;
uint32_t chunk_header_size;
uint32_t chunk_data_size;
uint32_t chunk_value;
uint32_t chunk_size_offset;
uint32_t chunk_be;
int chunk_start_set;
int chunk_size_set;
int chunk_count_set;
@ -791,7 +794,9 @@ static void set_body_chunk(txth_header* txth) {
//todo maybe should only be done once, or have some count to retrigger to simplify?
if (!txth->chunk_start_set || !txth->chunk_size_set || !txth->chunk_count_set)
return;
if (txth->chunk_size == 0 || txth->chunk_start > txth->data_size || txth->chunk_count == 0)
if ((txth->chunk_size == 0 && ! txth->chunk_size_offset) ||
txth->chunk_start > txth->data_size ||
txth->chunk_count == 0)
return;
if (!txth->sf_body)
return;
@ -807,18 +812,22 @@ static void set_body_chunk(txth_header* txth) {
{
txth_io_config_data cfg = {0};
cfg.chunk_start = txth->chunk_start;
cfg.chunk_number = txth->chunk_number - 1; /* 1-index to 0-index */
cfg.chunk_header_size = txth->chunk_header_size;
cfg.chunk_data_size = txth->chunk_data_size;
cfg.chunk_value = txth->chunk_value;
cfg.chunk_size_offset = txth->chunk_size_offset;
cfg.chunk_be = txth->chunk_be;
cfg.chunk_start = txth->chunk_start;
cfg.chunk_size = txth->chunk_size;
cfg.chunk_count = txth->chunk_count;
cfg.chunk_number = txth->chunk_number - 1; /* 1-index to 0-index */
temp_sf = setup_txth_streamfile(txth->sf_body, cfg, txth->sf_body_opened);
if (!temp_sf) return;
}
/* closing is handled by temp_sf */
//if (txth->sf_body_opened) {
// close_streamfile(txth->sf_body);
@ -953,6 +962,19 @@ static txth_codec_t parse_codec(txth_header* txth, const char* val) {
return UNKNOWN;
}
static int parse_be(txth_header* txth, const char* val, uint32_t* p_value) {
if (is_string(val, "BE"))
*p_value = 1;
else if (is_string(val, "LE"))
*p_value = 0;
else
if (!parse_num(txth->sf_head,txth,val, p_value))
goto fail;
return 1;
fail:
return 0;
}
static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, char* val) {
//;VGM_LOG("TXTH: key=%s, val=%s\n", key, val);
@ -1185,11 +1207,7 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
if (!parse_num(txth->sf_head,txth,val, &txth->coef_spacing)) goto fail;
}
else if (is_string(key,"coef_endianness")) {
if (is_string(val, "BE"))
txth->coef_big_endian = 1;
else if (is_string(val, "LE"))
txth->coef_big_endian = 0;
else if (!parse_num(txth->sf_head,txth,val, &txth->coef_big_endian)) goto fail;
if (!parse_be(txth, val, &txth->coef_big_endian)) goto fail;
}
else if (is_string(key,"coef_mode")) {
if (!parse_num(txth->sf_head,txth,val, &txth->coef_mode)) goto fail;
@ -1212,11 +1230,7 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
if (!parse_num(txth->sf_head,txth,val, &txth->hist_spacing)) goto fail;
}
else if (is_string(key,"hist_endianness")) {
if (is_string(val, "BE"))
txth->hist_big_endian = 1;
else if (is_string(val, "LE"))
txth->hist_big_endian = 0;
else if (!parse_num(txth->sf_head,txth,val, &txth->hist_big_endian)) goto fail;
if (!parse_be(txth, val, &txth->hist_big_endian)) goto fail;
}
/* SUBSONGS */
@ -1340,34 +1354,41 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
}
/* CHUNKS */
else if (is_string(key,"chunk_number")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_number)) goto fail;
else if (is_string(key,"chunk_count")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_count)) goto fail;
txth->chunk_count_set = 1;
set_body_chunk(txth);
}
else if (is_string(key,"chunk_start")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_start)) goto fail;
txth->chunk_start_set = 1;
set_body_chunk(txth);
}
else if (is_string(key,"chunk_header_size")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_header_size)) goto fail;
//txth->chunk_header_size_set = 1;
//set_body_chunk(txth); /* optional and should go before chunk_size */
}
else if (is_string(key,"chunk_data_size")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_data_size)) goto fail;
//txth->chunk_data_size_set = 1;
//set_body_chunk(txth); /* optional and should go before chunk_size */
}
else if (is_string(key,"chunk_size")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_size)) goto fail;
txth->chunk_size_set = 1;
set_body_chunk(txth);
}
else if (is_string(key,"chunk_count")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_count)) goto fail;
txth->chunk_count_set = 1;
set_body_chunk(txth);
/* optional and should go before the above */
else if (is_string(key,"chunk_number")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_number)) goto fail;
}
else if (is_string(key,"chunk_header_size")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_header_size)) goto fail;
}
else if (is_string(key,"chunk_data_size")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_data_size)) goto fail;
}
else if (is_string(key,"chunk_value")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_value)) goto fail;
}
else if (is_string(key,"chunk_size_offset")) {
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_size_offset)) goto fail;
}
else if (is_string(key,"chunk_endianness")) {
if (!parse_be(txth, val, &txth->chunk_be)) goto fail;
}
/* BASE OFFSET */
else if (is_string(key,"base_offset")) {

View File

@ -1,30 +1,36 @@
#ifndef _TXTH_STREAMFILE_H_
#define _TXTH_STREAMFILE_H_
#include "../streamfile.h"
#include "../util/endianness.h"
typedef struct {
off_t chunk_start;
size_t chunk_size;
size_t chunk_header_size;
size_t chunk_data_size;
uint32_t chunk_start;
uint32_t chunk_size;
uint32_t chunk_header_size;
uint32_t chunk_data_size;
int chunk_count;
int chunk_number;
uint32_t chunk_value;
uint32_t chunk_size_offset;
int chunk_be;
} txth_io_config_data;
typedef struct {
/* config */
txth_io_config_data cfg;
size_t stream_size;
uint32_t stream_size;
/* state */
off_t logical_offset; /* fake offset */
off_t physical_offset; /* actual offset */
size_t block_size; /* current size */
size_t skip_size; /* size from block start to reach data */
size_t data_size; /* usable size in a block */
uint32_t logical_offset; /* fake offset */
uint32_t physical_offset; /* actual offset */
uint32_t block_size; /* current size */
uint32_t skip_size; /* size from block start to reach data */
uint32_t data_size; /* usable size in a block */
size_t logical_size;
uint32_t logical_size;
} txth_io_data;
@ -64,6 +70,27 @@ static size_t txth_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t l
data->data_size = data->cfg.chunk_data_size;
}
/* chunk size reader (overwrites the above) */
if (data->cfg.chunk_header_size && data->cfg.chunk_size_offset) {
read_u32_t read_u32 = data->cfg.chunk_be ? read_u32be : read_u32le;
data->block_size = read_u32(data->physical_offset + data->cfg.chunk_size_offset, sf);
data->data_size = data->block_size - data->cfg.chunk_header_size;
VGM_LOG("bs %x = %x\n", data->physical_offset, data->block_size);
/* skip chunk if doesn't match expected header value */
if (data->cfg.chunk_value) {
uint32_t value = read_u32(data->physical_offset + 0x00, sf);
if (value != data->cfg.chunk_value) {
VGM_LOG("skip %x vs %x at %x\n", value, data->cfg.chunk_value, data->physical_offset);
data->data_size = 0;
}
}
else {
VGM_LOG("not skip at %x\n", data->physical_offset) ;
}
}
/* clamp for games where last block is smaller */ //todo not correct for all cases
if (data->physical_offset + data->block_size > data->cfg.chunk_start + data->stream_size) {
data->block_size = (data->cfg.chunk_start + data->stream_size) - data->physical_offset;