Skip to content

Commit

Permalink
Do string table lookups for string parameters in variable argument li…
Browse files Browse the repository at this point in the history
…sts.
  • Loading branch information
nmlgc committed Nov 6, 2013
1 parent e492cb5 commit ff80749
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 0 deletions.
94 changes: 94 additions & 0 deletions thcrap/src/strings.c
Expand Up @@ -42,13 +42,107 @@ const char* strings_lookup(const char *in, size_t *out_len)
return ret;
}

va_list printf_parse_num(va_list va, const char **p)
{
if(**p == '*') {
// width specified in next variable argument
(*p)++;
va_arg(va, int);
} else while(isdigit(**p)) {
(*p)++;
}
return va;
}

// The printf format parsing below is based on Wine's implementation for
// msvcrt.dll (dlls/msvcrt/printf.h).
void strings_va_lookup(va_list va, const char *format)
{
const char *p = format;
while(*p) {
char fmt;
int flag_double = 0;

// Skip characters before '%'
for(; *p && *p != '%'; p++);
if(!*p) {
break;
}

// *p == '%' here
p++;

// output a single '%' character
if(*p == '%') {
p++;
continue;
}

// Skip flags. From left to right:
// prefix sign, prefix space, left-align, zero padding, alternate
while(strchr("+ -0#", *p) != NULL) {
p++;
}

// Width
va = printf_parse_num(va, &p);

// Precision
if(*p == '.') {
p++;
va = printf_parse_num(va, &p);
}

// Argument size modifier
while(*p) {
if(*p=='l' && *(p+1)=='l') {
flag_double = 1;
p += 2;
} else if(*p=='h' || *p=='l' || *p=='L') {
p++;
} else if(*p == 'I') {
if(*(p+1)=='6' && *(p+2)=='4') {
flag_double = 1;
p += 3;
} else if(*(p+1)=='3' && *(p+2)=='2') {
p += 3;
} else if(isdigit(*(p+1)) || !*(p+1)) {
break;
} else {
p++;
}
} else if(*p == 'w' || *p == 'F') {
p++;
} else {
break;
}
}
fmt = *p;
if(fmt == 's' || fmt == 'S') {
*(const char**)va = strings_lookup(*(const char**)va, NULL);
}
// Advance [va] if the format is among the valid ones
if(strchr("aeEfgG", fmt)) {
va_arg(va, double);
}
if(strchr("sScCpndiouxX", fmt)) {
va_arg(va, int);
if(flag_double) {
va_arg(va, int);
}
}
p++;
}
}

const char* strings_vsprintf(const size_t addr, const char *format, va_list va)
{
char *ret = NULL;
size_t str_len;
char addr_key[addr_key_len];

format = strings_lookup(format, NULL);
strings_va_lookup(va, format);

if(!format) {
return NULL;
Expand Down
4 changes: 4 additions & 0 deletions thcrap/src/strings.h
Expand Up @@ -21,6 +21,10 @@ const char* strings_get(const char *id);
// if not NULL.
const char* strings_lookup(const char *in, size_t *out_len);

// String lookup for variable argument lists. Parses [format] and calls
// [strings_lookup] for every string parameter in [va].
void strings_va_lookup(va_list va, const char *format);

// Safe and persistent sprintf handler.
// This function should be inserted via binary hacks everywhere a game
// calls sprintf, as it guarantees a sufficiently large buffer for the result.
Expand Down
1 change: 1 addition & 0 deletions thcrap/thcrap.def
Expand Up @@ -147,6 +147,7 @@ EXPORTS
; ----------------------------
strings_get
strings_lookup
strings_va_lookup
strings_sprintf
strings_vsprintf

Expand Down

0 comments on commit ff80749

Please sign in to comment.