Add TXTH name_table wildcards + fix doc

This commit is contained in:
bnnm 2019-07-07 18:10:43 +02:00
parent 023e5b8d99
commit 0523b36a62
2 changed files with 82 additions and 5 deletions

View File

@ -378,13 +378,13 @@ chunk_data_size = (value)
chunk_size = (value) chunk_size = (value)
``` ```
#### NAME LIST #### NAME TABLE
Some games have headers for all files pasted together separate from the actual data, but this order may be hard-coded or even alphabetically ordered by filename. In those cases you can set a "name list" that assigns constant values (one or many) to filenames. This list is loaded from an external text file (for clarity) and can be set to any name, for example `name_list = .names.txt` Some games have headers for all files pasted together separate from the actual data, but this order may be hard-coded or even alphabetically ordered by filename. In those cases you can set a "name table" that assigns constant values (one or many) to filenames. This table is loaded from an external text file (for clarity) and can be set to any name, for example `name_table = .names.txt`
``` ```
name_list = (filename) name_table = (filename)
``` ```
Inside the list you define lines mapping a filename to a bunch of values, in this format: Inside the table you define lines mapping a filename to a bunch of values, in this format:
``` ```
# base definition # base definition
(filename1): (value) (filename1): (value)
@ -397,6 +397,13 @@ Inside the list you define lines mapping a filename to a bunch of values, in thi
``` ```
Then I'll find your current file name, and you can then reference its numbers from the list as a `name_value` field, like `base_offset = name_value`, `start_offset = 0x1000 + name_value1`, `interleave = name_value5`, etc. `(filename)` can be with or without extension (like `bgm01.vag` or just `bgm01`), and if the file's name isn't found it'll use default values, and if those aren't defined you'll get 0 instead. Being "values" they can be use math or offsets too. Then I'll find your current file name, and you can then reference its numbers from the list as a `name_value` field, like `base_offset = name_value`, `start_offset = 0x1000 + name_value1`, `interleave = name_value5`, etc. `(filename)` can be with or without extension (like `bgm01.vag` or just `bgm01`), and if the file's name isn't found it'll use default values, and if those aren't defined you'll get 0 instead. Being "values" they can be use math or offsets too.
You can use wildcards to match multiple names too (it stops on first name that matches), and UTF-8 names should work, case insensitive even.
```
bgm_??_4: 4 # 4ch: files like bgm_00_4, bgm_01_4, etc
bgm*_M: 1 # 1ch: some files end with _M for mono
bgm*: 2 # 2ch: all other files, notice order matters
```
While you can put anything in the numbers, this feature is meant to be used to store some number that points to the actual data inside a real multi-header, that could be set with `header_file`. If you need to store many constant values there is good chance this can be supported in some better way. While you can put anything in the numbers, this feature is meant to be used to store some number that points to the actual data inside a real multi-header, that could be set with `header_file`. If you need to store many constant values there is good chance this can be supported in some better way.

View File

@ -1245,6 +1245,76 @@ static int is_string_field(const char * val, const char * cmp) {
return is_substring(val, cmp, 1); return is_substring(val, cmp, 1);
} }
static uint16_t get_string_wchar(const char * val, int pos, int *csize) {
uint16_t wchar = 0;
if ((val[pos] & 0x80) && val[pos+1] != '\0') {
wchar = (((val[pos] << 8u) & 0xFF00) | (val[pos+1] & 0xFF));
//wchar = ((((uint16_t)val[pos] << 8u)) | ((uint16_t)val[pos+1]));
if (csize) *csize = 2;
if (wchar >= 0xc380 && wchar <= 0xc39f) /* ghetto lowercase for common letters */
wchar += 0x20;
} else {
wchar = val[pos];
if (csize) *csize = 1;
if (wchar >= 0x41 && wchar <= 0x5a)
wchar += 0x20;
}
return wchar;
}
static int is_string_match(const char * text, const char * pattern) {
int t_pos = 0, p_pos = 0;
int p_size, t_size;
uint16_t p_char, t_char;
;VGM_LOG("TXTH: match '%s' vs '%s'\n", text,pattern);
/* compares 2 strings (case insensitive, to a point) allowing wildcards
* ex. for "test": match = "Test*", "*est", "*teSt","T*ES*T"; fail = "tst", "teest"
*
* does some bleh UTF-8 handling, consuming dual bytes if needed (codepages set char's eighth bit).
* as such it's slower than standard funcs, but it's not like we need it to be ultra fast.
* */
while (text[t_pos] != '\0' && pattern[p_pos] != '\0') {
//;VGM_LOG("TXTH: compare '%s' vs '%s'\n", (text+t_pos), (pattern+p_pos));
if (pattern[p_pos] == '*') {
/* consume text wchars until one matches next pattern char */
p_pos++;
p_char = get_string_wchar(pattern, p_pos, NULL); /* stop char, or null */
while(text[t_pos] != '\0') {
t_char = get_string_wchar(text, t_pos, &t_size);
;VGM_LOG("TXTH: consume %i '%s'\n", t_size, (text+t_pos) );
if (t_char == p_char)
break;
t_pos += t_size;
}
}
else if (pattern[p_pos] == '?') {
/* skip next text wchar */
get_string_wchar(text, t_pos, &t_size);
p_pos++;
t_pos += t_size;
}
else { /* must match 1:1 */
t_char = get_string_wchar(text, t_pos, &t_size);
p_char = get_string_wchar(pattern, p_pos, &p_size);
if (p_char != t_char)
break;
p_pos += p_size;
t_pos += t_size;
}
}
//;VGM_LOG("TXTH: match '%s' vs '%s' = %s\n", text,pattern, (text[t_pos] == '\0' && pattern[p_pos] == '\0') ? "true" : "false");
/* 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 * streamFile, txth_header * txth, const char * val, char * str) { static int parse_string(STREAMFILE * streamFile, txth_header * txth, const char * val, char * str) {
int n = 0; int n = 0;
@ -1354,7 +1424,7 @@ static int parse_name_table(txth_header * txth, char * name_list) {
//;VGM_LOG("TXTH: compare name '%s'\n", key); //;VGM_LOG("TXTH: compare name '%s'\n", key);
/* parse values if key (name) matches default ("") or filename with/without extension */ /* parse values if key (name) matches default ("") or filename with/without extension */
if (key[0]=='\0' || strcasecmp(key, filename)==0 || strcasecmp(key, basename)==0) { if (key[0]=='\0' || is_string_match(filename, key) || is_string_match(basename, key)) {
int n; int n;
char subval[TXT_LINE_MAX]; char subval[TXT_LINE_MAX];
const char *current = val; const char *current = val;