mirror of
https://github.com/vgmstream/vgmstream.git
synced 2025-01-17 23:36:41 +01:00
commit
50cb942206
94
doc/TXTH.md
94
doc/TXTH.md
@ -49,7 +49,7 @@ The following can be used in place of `(value)` for `(key) = (value)` commands.
|
||||
* `$1|2|3|4`: value has size of 8/16/24/32 bit (optional, defaults to 4)
|
||||
* Example: `@0x10:BE$2` means `get big endian 16b value at 0x10`
|
||||
- `(field)`: uses current value of some fields. Accepted strings:
|
||||
- `interleave, interleave_last, channels, sample_rate, start_offset, data_size, num_samples, loop_start_sample, loop_end_sample, subsong_count, subsong_offset, subfile_offset, subfile_size, name_valueX`
|
||||
- `interleave, interleave_last, channels, sample_rate, start_offset, data_size, num_samples, loop_start_sample, loop_end_sample, subsong_count, subsong_offset, subfile_offset, subfile_size, base_offset, name_valueX`
|
||||
- `(other)`: other special values for certain keys, described per key
|
||||
|
||||
|
||||
@ -664,6 +664,19 @@ chunk_size = 0x8000
|
||||
num_samples = data_size
|
||||
```
|
||||
|
||||
### Base offset chaining
|
||||
Some formats read an offset to another part of the file, then another offset, then other, etc.
|
||||
|
||||
You can simulate this chaining multiple `base_offset`
|
||||
```
|
||||
base_offset = @0x10 #sets at 0x1000
|
||||
channels = @0x04 #reads at 0x1004
|
||||
base_offset = base_offset + @0x10 #sets at 0x1000 + 0x200 = 0x1200
|
||||
sample_rate = @0x04 #reads at 0x1204
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
**Colin McRae DiRT (PC) .wip.txth**
|
||||
@ -827,7 +840,7 @@ JIN003.XAG: 0x180
|
||||
```
|
||||
|
||||
|
||||
** Grandia (PS1) **
|
||||
**Grandia (PS1) bgm.txth**
|
||||
```
|
||||
header_file = GM1.IDX
|
||||
body_file = GM1.STZ
|
||||
@ -839,3 +852,80 @@ subfile_offset = (@0x00 & 0xFFFFF) * 0x800
|
||||
subfile_extension = seb
|
||||
subfile_size = ((@0x04 - @0x00) & 0xFFFFF) * 0x800
|
||||
```
|
||||
|
||||
|
||||
**Zack & Wiki (Wii) .ssd.txth**
|
||||
```
|
||||
header_file = bgm_S01.srt
|
||||
name_table = .names.txt
|
||||
|
||||
base_offset = @0x0c:BE
|
||||
base_offset = base_offset + @0x08:BE + name_value
|
||||
base_offset = base_offset + @0x00:BE
|
||||
|
||||
codec = NGC_DSP
|
||||
channels = 2
|
||||
interleave = half_size
|
||||
sample_rate = @0x08:BE
|
||||
loop_flag = @0x04:BE
|
||||
|
||||
sample_type = bytes
|
||||
loop_start_sample = @0x10:BE
|
||||
loop_end_sample = @0x14:BE
|
||||
num_samples = @0x18:BE
|
||||
|
||||
coef_offset = 0x20
|
||||
coef_spacing = 0x40
|
||||
coef_endianness = BE
|
||||
```
|
||||
*.names.txt*
|
||||
```
|
||||
st_s01_00a.ssd: 0*0x04
|
||||
st_s01_00b.ssd: 1*0x04
|
||||
st_s01_00c.ssd: 2*0x04
|
||||
st_s01_01a.ssd: 3*0x04
|
||||
st_s01_01b.ssd: 4*0x04
|
||||
st_s01_02a.ssd: 5*0x04
|
||||
st_s01_02b.ssd: 6*0x04
|
||||
st_s01_02c.ssd: 7*0x04
|
||||
```
|
||||
|
||||
|
||||
**Zack & Wiki (Wii) st_s01_00a.txth**
|
||||
```
|
||||
#alt from above with untouched folders
|
||||
header_file = Sound/BGM/bgm_S01.srt
|
||||
body_file = snd/stream/st_s01_00a.ssd
|
||||
name_table = .names.txt
|
||||
|
||||
base_offset = @0x0c:BE
|
||||
base_offset = base_offset + @0x08:BE + name_value
|
||||
base_offset = base_offset + @0x00:BE
|
||||
|
||||
codec = NGC_DSP
|
||||
channels = 2
|
||||
interleave = half_size
|
||||
sample_rate = @0x08:BE
|
||||
loop_flag = @0x04:BE
|
||||
|
||||
sample_type = bytes
|
||||
loop_start_sample = @0x10:BE
|
||||
loop_end_sample = @0x14:BE
|
||||
num_samples = @0x18:BE
|
||||
|
||||
coef_offset = 0x20
|
||||
coef_spacing = 0x40
|
||||
coef_endianness = BE
|
||||
```
|
||||
*.names.txt*
|
||||
```
|
||||
*snd/stream/st_s01_00a.ssd: 0*0x04
|
||||
*snd/stream/st_s01_00b.ssd: 1*0x04
|
||||
*snd/stream/st_s01_00c.ssd: 2*0x04
|
||||
*snd/stream/st_s01_01a.ssd: 3*0x04
|
||||
*snd/stream/st_s01_01b.ssd: 4*0x04
|
||||
*snd/stream/st_s01_02a.ssd: 5*0x04
|
||||
*snd/stream/st_s01_02b.ssd: 6*0x04
|
||||
*snd/stream/st_s01_02c.ssd: 7*0x04
|
||||
# uses wildcards for full paths from plugins
|
||||
```
|
||||
|
@ -461,6 +461,7 @@ static const char* extension_list[] = {
|
||||
"spsd",
|
||||
"spw",
|
||||
"ss2",
|
||||
"ssd", //txth/reserved [Zack & Wiki (Wii)]
|
||||
"ssm",
|
||||
"sss",
|
||||
"ster",
|
||||
|
@ -1063,9 +1063,10 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
|
||||
/* COEFS */
|
||||
else if (is_string(key,"coef_offset")) {
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->coef_offset)) goto fail;
|
||||
/* special adjustment */
|
||||
/* special adjustments */
|
||||
txth->coef_offset += txth->base_offset;
|
||||
if (txth->subsong_offset)
|
||||
txth->coef_offset = txth->base_offset + txth->coef_offset + txth->subsong_offset * (txth->target_subsong - 1);
|
||||
txth->coef_offset += txth->subsong_offset * (txth->target_subsong - 1);
|
||||
}
|
||||
else if (is_string(key,"coef_spacing")) {
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->coef_spacing)) goto fail;
|
||||
@ -1090,8 +1091,9 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->hist_offset)) goto fail;
|
||||
txth->hist_set = 1;
|
||||
/* special adjustment */
|
||||
txth->hist_offset += txth->hist_offset;
|
||||
if (txth->subsong_offset)
|
||||
txth->hist_offset = txth->base_offset + txth->hist_offset + txth->subsong_offset * (txth->target_subsong - 1);
|
||||
txth->hist_offset += txth->subsong_offset * (txth->target_subsong - 1);
|
||||
}
|
||||
else if (is_string(key,"hist_spacing")) {
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->hist_spacing)) goto fail;
|
||||
@ -1115,8 +1117,9 @@ static int parse_keyval(STREAMFILE * streamFile_, txth_header * txth, const char
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->name_offset)) goto fail;
|
||||
txth->name_offset_set = 1;
|
||||
/* special adjustment */
|
||||
txth->name_offset += txth->base_offset;
|
||||
if (txth->subsong_offset)
|
||||
txth->name_offset = txth->base_offset + txth->name_offset + txth->subsong_offset * (txth->target_subsong - 1);
|
||||
txth->name_offset += txth->subsong_offset * (txth->target_subsong - 1);
|
||||
}
|
||||
else if (is_string(key,"name_size")) {
|
||||
if (!parse_num(txth->streamHead,txth,val, &txth->name_size)) goto fail;
|
||||
@ -1314,6 +1317,8 @@ static uint16_t get_string_wchar(const char * val, int pos, int *csize) {
|
||||
|
||||
if (wchar >= 0x41 && wchar <= 0x5a)
|
||||
wchar += 0x20;
|
||||
if (wchar == '\\')
|
||||
wchar = '/'; /* normalize paths */
|
||||
}
|
||||
|
||||
return wchar;
|
||||
@ -1408,11 +1413,12 @@ fail:
|
||||
static int parse_name_table(txth_header * txth, char * name_list) {
|
||||
STREAMFILE *nameFile = NULL;
|
||||
off_t txt_offset, file_size;
|
||||
char fullname[PATH_LIMIT];
|
||||
char filename[PATH_LIMIT];
|
||||
char basename[PATH_LIMIT];
|
||||
|
||||
/* just in case */
|
||||
if (txth->streamfile_is_txth || !txth->streamText || !txth->streamFile)
|
||||
if (!txth->streamText || !txth->streamBody)
|
||||
goto fail;
|
||||
|
||||
/* trim name_list just in case */
|
||||
@ -1426,15 +1432,16 @@ static int parse_name_table(txth_header * txth, char * name_list) {
|
||||
}
|
||||
}
|
||||
|
||||
//;VGM_LOG("TXTH: name_list2='%s'\n", name_list);
|
||||
//;VGM_LOG("TXTH: name_list='%s'\n", name_list);
|
||||
|
||||
/* open companion file near .txth */
|
||||
nameFile = open_streamfile_by_filename(txth->streamText, name_list);
|
||||
if (!nameFile) goto fail;
|
||||
|
||||
get_streamfile_filename(txth->streamFile, filename, sizeof(filename));
|
||||
get_streamfile_basename(txth->streamFile, basename, sizeof(basename));
|
||||
//;VGM_LOG("TXTH: filename=%s, basename=%s\n", filename, basename);
|
||||
get_streamfile_name(txth->streamBody, fullname, sizeof(filename));
|
||||
get_streamfile_filename(txth->streamBody, filename, sizeof(filename));
|
||||
get_streamfile_basename(txth->streamBody, basename, sizeof(basename));
|
||||
//;VGM_LOG("TXTH: names full=%s, file=%s, base=%s\n", fullname, filename, basename);
|
||||
|
||||
txt_offset = 0x00;
|
||||
file_size = get_streamfile_size(nameFile);
|
||||
@ -1477,7 +1484,10 @@ static int parse_name_table(txth_header * txth, char * name_list) {
|
||||
|
||||
//;VGM_LOG("TXTH: compare name '%s'\n", key);
|
||||
/* parse values if key (name) matches default ("") or filename with/without extension */
|
||||
if (key[0]=='\0' || is_string_match(filename, key) || is_string_match(basename, key)) {
|
||||
if (key[0]=='\0'
|
||||
|| is_string_match(filename, key)
|
||||
|| is_string_match(basename, key)
|
||||
|| is_string_match(fullname, key)) {
|
||||
int n;
|
||||
char subval[TXT_LINE_MAX];
|
||||
const char *current = val;
|
||||
@ -1616,6 +1626,7 @@ static int parse_num(STREAMFILE * streamFile, txth_header * txth, const char * v
|
||||
else if ((n = is_string_field(val,"subsong_offset"))) value = txth->subsong_offset;
|
||||
else if ((n = is_string_field(val,"subfile_offset"))) value = txth->subfile_offset;
|
||||
else if ((n = is_string_field(val,"subfile_size"))) value = txth->subfile_size;
|
||||
else if ((n = is_string_field(val,"base_offset"))) value = txth->base_offset;
|
||||
//todo whatever, improve
|
||||
else if ((n = is_string_field(val,"name_value"))) value = txth->name_values[0];
|
||||
else if ((n = is_string_field(val,"name_value1"))) value = txth->name_values[0];
|
||||
|
Loading…
x
Reference in New Issue
Block a user