[gamecode] Add optional support for %@ strings

If no handler has been registered, then the corresponding parameter is
printed as a pointer but with surrounding brackets (eg, [0xfc48]). This
will allow the ruamoko runtime to implement object printing.
This commit is contained in:
Bill Currie 2022-09-09 09:27:36 +09:00
parent 1f9553d13e
commit 6db44afedc
2 changed files with 60 additions and 0 deletions

View file

@ -1628,6 +1628,32 @@ void PR_FreeString (progs_t *pr, pr_string_t str);
*/
void PR_FreeTempStrings (progs_t *pr);
/** Callback for handling %@ in PR_Sprintf format strings.
\param pr pointer to ::progs_t VM struct
\param at_param Parameter to be converted to a string. Usually a progs
pointer value. It is the callee's responsibility to
interpret the value as a pointer.
\param data User data pointer as passed to PR_Sprintf_SetAtHandler
\return String to be printed in place of %@
*/
typedef const char *(*prstr_at_handler_t) (progs_t *pr, pr_ptr_t at_param,
void *data);
/** Set the at_handler callback and its user data pointer.
The at_handler callback defaults to null resulting in [ptr] in the output
string. Setting the callback allows the at_handler to interpret the value
(nominally pr_ptr_t, but always a 32-bit value) as it chooses.
\param pr pointer to ::progs_t VM struct
\param at_handler Function pointer for callback to interpret the parameter
corresponding to the %@ format specifier.
\param data User data pointer, passed to \a at_handler.
*/
void PR_Sprintf_SetAtHandler (progs_t *pr, prstr_at_handler_t at_handler,
void *data);
/** Formatted printing similar to C's vsprintf, but using QC types.
The format string is a string of characters (other than \c \%) to be
printed that includes optional format specifiers, one for each arg to be

View file

@ -90,6 +90,8 @@ typedef struct prstr_resources_s {
int num_strings;
fmt_item_t *free_fmt_items;
dstring_t *print_str;
prstr_at_handler_t at_handler;
void *at_handler_data;
} prstr_resources_t;
typedef enum {
@ -866,6 +868,8 @@ typedef struct fmt_state_s {
fmt_item_t *fmt_items;
fmt_item_t **fi;
int fmt_count;
prstr_at_handler_t at_handler;
void *at_handler_data;
} fmt_state_t;
static inline void
@ -1052,6 +1056,25 @@ fmt_state_conversion (fmt_state_t *state)
switch ((conv = *state->c++)) {
case '@':
// object
pr_ptr_t at_param = P_UINT (pr, state->fmt_count);
if (state->at_handler) {
const char *at_str = state->at_handler (pr, at_param,
state->at_handler_data);
(*state->fi)->type = 's';
(*state->fi)->data.string_var = at_str;
} else {
(*state->fi)->type = 's';
(*state->fi)->data.string_var = "[";
fmt_append_item (state);
(*state->fi)->flags |= FMT_ALTFORM;
(*state->fi)->type = 'x';
(*state->fi)->data.uint_var = at_param;
fmt_append_item (state);
(*state->fi)->type = 's';
(*state->fi)->data.string_var = "]";
}
state->fmt_count++;
fmt_append_item (state);
break;
@ -1200,6 +1223,15 @@ fmt_state_format (fmt_state_t *state)
}
///@}
VISIBLE void
PR_Sprintf_SetAtHandler (progs_t *pr, prstr_at_handler_t at_handler,
void *data)
{
prstr_resources_t *res = pr->pr_string_resources;
res->at_handler = at_handler;
res->at_handler_data = data;
}
VISIBLE void
PR_Sprintf (progs_t *pr, dstring_t *result, const char *name,
const char *format, int count, pr_type_t **args)
@ -1214,6 +1246,8 @@ PR_Sprintf (progs_t *pr, dstring_t *result, const char *name,
*state.fi = new_fmt_item (res);
state.c = format;
state.state = fmt_state_format;
state.at_handler = res->at_handler;
state.at_handler_data = res->at_handler_data;
if (!name)
name = "PR_Sprintf";