mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-02-17 11:18:31 +01:00
Improve TXTH dynamic dechunking
This commit is contained in:
parent
5faf24e9e9
commit
5457e3e23c
45
doc/TXTH.md
45
doc/TXTH.md
@ -462,24 +462,29 @@ Optional settings (set before main):
|
||||
- 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
|
||||
- `chunk_value`: ignores chunks that don't match this value at chunk offset 0x00 (32-bit, in `chunk_endianness`). Can be used to ignore video chunks in movie files, for example.
|
||||
- `chunk_size_offset`: reads chunk size at this offset, in header (32-bit in `chunk_endianness`). For chunks of dynamic sizes (no need to set `chunk_size` as will be ignored). Includes header size.
|
||||
- `chunk_data_size_offset`: same, for data sizes (not including headers). Note that you can use header+data+size configs to handle padding between blocks.
|
||||
- `chunk_endianness`: sets endianness of the above two settings.
|
||||
|
||||
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.
|
||||
For technical reasons, "dechunking" activates when setting all main settings, so set optional config first. Note that `chunk_size` is static (read once from a fixed offset) while `chunk_size_offset` is dynamic (read on every chunk).
|
||||
|
||||
```
|
||||
chunk_count = (value)
|
||||
chunk_start = (value)
|
||||
chunk_size = (value)
|
||||
|
||||
# optional - fixed
|
||||
chunk_number = (value)
|
||||
chunk_header_size = (value)
|
||||
chunk_data_size = (value)
|
||||
|
||||
# optional - dynamic
|
||||
chunk_value = (value)
|
||||
chunk_size_offset = (value)
|
||||
chunk_endian = LE|BE
|
||||
chunk_data_size_offset = (value)
|
||||
chunk_endianness = LE|BE
|
||||
|
||||
# main
|
||||
chunk_count = (value)
|
||||
chunk_start = (value)
|
||||
chunk_size = (value)
|
||||
```
|
||||
|
||||
#### NAME TABLE
|
||||
@ -1275,7 +1280,7 @@ loop_start = @0x28:BE
|
||||
loop_flag = @0x2c:BE
|
||||
```
|
||||
|
||||
#### Grand Theft Auto: San Andreas .vgmstream.txth
|
||||
#### Grand Theft Auto: San Andreas (PS2) .vgmstream.txth
|
||||
```
|
||||
# once extracted from bigfiles there are 2 types of files with hardcoded settings,
|
||||
# so we need 2 .txth
|
||||
@ -1345,3 +1350,23 @@ num_samples = data_size
|
||||
#1: 0x1F40, 0x800, 0x00, 0x1000
|
||||
#2: 0x1F50, 0x10000, 0x1000, 0x20000
|
||||
```
|
||||
|
||||
#### LEGO Batman 2 (Wii) .fmv.txth
|
||||
```
|
||||
# "dechunks" videos with dynamic chunks realtime and plays audio only
|
||||
|
||||
codec = IMA
|
||||
channels = 2
|
||||
sample_rate = @0x20
|
||||
|
||||
# each chunk is 0x00: ID + 0x04 data size (not including header)
|
||||
chunk_number = 1
|
||||
chunk_header_size = 0x08
|
||||
chunk_value = 0x00414D46 #"FMA\0" LE
|
||||
chunk_data_size_offset = 0x04
|
||||
|
||||
chunk_count = 1
|
||||
chunk_start = 0x28 #first chunk after header
|
||||
|
||||
num_samples = data_size
|
||||
```
|
||||
|
@ -121,7 +121,7 @@ typedef struct {
|
||||
int subfile_set;
|
||||
uint32_t subfile_offset;
|
||||
uint32_t subfile_size;
|
||||
char subfile_extension[32];
|
||||
char subfile_extension[16];
|
||||
|
||||
uint32_t chunk_number;
|
||||
uint32_t chunk_start;
|
||||
@ -130,8 +130,9 @@ typedef struct {
|
||||
uint32_t chunk_header_size;
|
||||
uint32_t chunk_data_size;
|
||||
uint32_t chunk_value;
|
||||
uint32_t chunk_size_offset;
|
||||
uint32_t chunk_be;
|
||||
uint32_t chunk_bsize_offset;
|
||||
uint32_t chunk_dsize_offset;
|
||||
uint32_t chunk_big_endian;
|
||||
int chunk_start_set;
|
||||
int chunk_size_set;
|
||||
int chunk_count_set;
|
||||
@ -820,9 +821,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_size_offset) ||
|
||||
txth->chunk_start > txth->data_size ||
|
||||
txth->chunk_count == 0)
|
||||
if (txth->chunk_size == 0 && !(txth->chunk_bsize_offset || txth->chunk_dsize_offset))
|
||||
return;
|
||||
if (txth->chunk_start > txth->data_size || txth->chunk_count == 0)
|
||||
return;
|
||||
if (!txth->sf_body)
|
||||
return;
|
||||
@ -843,8 +844,9 @@ static void set_body_chunk(txth_header* txth) {
|
||||
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_bsize_offset = txth->chunk_bsize_offset;
|
||||
cfg.chunk_dsize_offset = txth->chunk_dsize_offset;
|
||||
cfg.chunk_be = txth->chunk_big_endian;
|
||||
|
||||
cfg.chunk_start = txth->chunk_start;
|
||||
cfg.chunk_size = txth->chunk_size;
|
||||
@ -877,7 +879,7 @@ static void set_body_chunk(txth_header* txth) {
|
||||
|
||||
static int parse_keyval(STREAMFILE* sf, txth_header* txth, const char* key, char* val);
|
||||
static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_t* out_value);
|
||||
static int parse_string(STREAMFILE* sf, txth_header* txth, const char* val, char* str);
|
||||
static int parse_string(STREAMFILE* sf, txth_header* txth, const char* val, char* str, int str_len);
|
||||
static int parse_coef_table(STREAMFILE* sf, txth_header* txth, const char* val, uint8_t* out_value, size_t out_size);
|
||||
static int parse_name_table(txth_header* txth, char* val);
|
||||
static int parse_multi_txth(txth_header* txth, char* val);
|
||||
@ -1328,7 +1330,7 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
|
||||
txth->subfile_set = 1;
|
||||
}
|
||||
else if (is_string(key,"subfile_extension")) {
|
||||
if (!parse_string(txth->sf_head,txth,val, txth->subfile_extension)) goto fail;
|
||||
if (!parse_string(txth->sf_head,txth,val, txth->subfile_extension, sizeof(txth->subfile_extension))) goto fail;
|
||||
txth->subfile_set = 1;
|
||||
}
|
||||
|
||||
@ -1442,10 +1444,15 @@ static int parse_keyval(STREAMFILE* sf_, txth_header* txth, const char* key, cha
|
||||
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;
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_bsize_offset)) goto fail;
|
||||
txth->chunk_size_set = 1;
|
||||
}
|
||||
else if (is_string(key,"chunk_data_size_offset")) {
|
||||
if (!parse_num(txth->sf_head,txth,val, &txth->chunk_dsize_offset)) goto fail;
|
||||
txth->chunk_size_set = 1;
|
||||
}
|
||||
else if (is_string(key,"chunk_endianness")) {
|
||||
if (!parse_endianness(txth, val, &txth->chunk_be, NULL)) goto fail;
|
||||
if (!parse_endianness(txth, val, &txth->chunk_big_endian, NULL)) goto fail;
|
||||
}
|
||||
|
||||
|
||||
@ -1615,9 +1622,13 @@ static int is_string_match(const char* text, const char* pattern) {
|
||||
/* either all chars consumed/matched and both pos point to null, or one didn't so string didn't match */
|
||||
return text[t_pos] == '\0' && pattern[p_pos] == '\0';
|
||||
}
|
||||
static int parse_string(STREAMFILE* sf, txth_header* txth, const char* val, char* str) {
|
||||
|
||||
static int parse_string(STREAMFILE* sf, txth_header* txth, const char* val, char* str, int str_len) {
|
||||
int n = 0;
|
||||
|
||||
if (strlen(val) >= str_len)
|
||||
return 0;
|
||||
|
||||
/* read string without trailing spaces */
|
||||
if (sscanf(val, " %s%n[^ ]%n", str, &n, &n) != 1)
|
||||
return 0;
|
||||
@ -2001,7 +2012,8 @@ static int parse_num(STREAMFILE* sf, txth_header* txth, const char* val, uint32_
|
||||
else if ((n = is_string_field(val,"chunk_count"))) value = txth->chunk_count;
|
||||
else if ((n = is_string_field(val,"chunk_start"))) value = txth->chunk_start;
|
||||
else if ((n = is_string_field(val,"chunk_size"))) value = txth->chunk_size;
|
||||
else if ((n = is_string_field(val,"chunk_size_offset"))) value = txth->chunk_size_offset;
|
||||
else if ((n = is_string_field(val,"chunk_size_offset"))) value = txth->chunk_bsize_offset;
|
||||
else if ((n = is_string_field(val,"chunk_data_size_offset")))value = txth->chunk_dsize_offset;
|
||||
else if ((n = is_string_field(val,"chunk_number"))) value = txth->chunk_number;
|
||||
else if ((n = is_string_field(val,"chunk_data_size"))) value = txth->chunk_data_size;
|
||||
else if ((n = is_string_field(val,"chunk_header_size"))) value = txth->chunk_header_size;
|
||||
|
@ -14,7 +14,8 @@ typedef struct {
|
||||
int chunk_number;
|
||||
|
||||
uint32_t chunk_value;
|
||||
uint32_t chunk_size_offset;
|
||||
uint32_t chunk_bsize_offset;
|
||||
uint32_t chunk_dsize_offset;
|
||||
int chunk_be;
|
||||
} txth_io_config_data;
|
||||
|
||||
@ -71,11 +72,26 @@ static size_t txth_io_read(STREAMFILE* sf, uint8_t* dest, off_t offset, size_t l
|
||||
}
|
||||
|
||||
/* chunk size reader (overwrites the above) */
|
||||
if (data->cfg.chunk_header_size && data->cfg.chunk_size_offset) {
|
||||
if (data->cfg.chunk_header_size && (data->cfg.chunk_bsize_offset || data->cfg.chunk_dsize_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;
|
||||
data->block_size = 0;
|
||||
data->data_size = 0;
|
||||
|
||||
if (data->cfg.chunk_bsize_offset)
|
||||
data->block_size = read_u32(data->physical_offset + data->cfg.chunk_bsize_offset, sf);
|
||||
if (data->cfg.chunk_dsize_offset)
|
||||
data->data_size = read_u32(data->physical_offset + data->cfg.chunk_dsize_offset, sf);
|
||||
|
||||
if (!data->block_size && !data->data_size) { /* bad read? */
|
||||
data->block_size = data->cfg.chunk_header_size;
|
||||
data->data_size = data->cfg.chunk_header_size;
|
||||
}
|
||||
|
||||
if (!data->block_size)
|
||||
data->block_size = data->data_size + data->cfg.chunk_header_size;
|
||||
if (!data->data_size)
|
||||
data->data_size = data->block_size - data->cfg.chunk_header_size; /* may be 0 */
|
||||
|
||||
/* skip chunk if doesn't match expected header value */
|
||||
if (data->cfg.chunk_value) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user