diff --git a/include/QF/progs.h b/include/QF/progs.h index c08bf12d5..760819941 100644 --- a/include/QF/progs.h +++ b/include/QF/progs.h @@ -1344,6 +1344,18 @@ string_t PR_NewMutableString (progs_t *pr); */ string_t PR_SetDynamicString (progs_t *pr, const char *s); +/** Convert an ephemeral string to a dynamic string. + + Valid strings that are not ephemeral (static, dynamic, mutable) will not + be affected, but temp and return strings will be marked dynamic, requiring + a call to PR_FreeString to return their memory. + + \param pr pointer to ::progs_t VM struct + \param str The string to be "held" (made non-ephemeral). Safe to call + on any valid string, but affects only ephemeral strings. +*/ +void PR_HoldString (progs_t *pr, string_t str); + /** Destroy a mutable, dynamic or temporary string. \param pr pointer to ::progs_t VM struct \param str string index of the string to be destroyed diff --git a/libs/gamecode/pr_strings.c b/libs/gamecode/pr_strings.c index 754ab7e38..f2db346f0 100644 --- a/libs/gamecode/pr_strings.c +++ b/libs/gamecode/pr_strings.c @@ -449,8 +449,8 @@ PR_SetReturnString (progs_t *pr, const char *s) } // grab the string ref from the oldest slot, or make a new one if the - // slot is empty - if ((sr = res->rs_slot->strref)) { + // slot is empty or the string has been held + if ((sr = res->rs_slot->strref) && sr->type != str_dynamic) { if (sr->type != str_return || sr->rs_slot != res->rs_slot) { PR_Error (pr, "internal string error: %d", __LINE__); } @@ -592,6 +592,33 @@ PR_NewMutableString (progs_t *pr) return string_index (res, sr); } +VISIBLE void +PR_HoldString (progs_t *pr, string_t str) +{ + prstr_resources_t *res = pr->pr_string_resources; + strref_t *sr = get_strref (res, str); + + if (sr) { + switch (sr->type) { + case str_temp: + case str_return: + break; + case str_static: + case str_mutable: + case str_dynamic: + // non-ephemeral string, no-op + return; + default: + PR_Error (pr, "internal string error: %d", __LINE__); + } + sr->type = str_dynamic; + return; + } + if (!PR_StringValid (pr, str)) { + PR_RunError (pr, "attempt to hold invalid string %d", str); + } +} + VISIBLE void PR_FreeString (progs_t *pr, string_t str) { @@ -629,6 +656,11 @@ PR_FreeTempStrings (progs_t *pr) for (sr = pr->pr_xtstr; sr; sr = t) { t = sr->next; + if (sr->type == str_dynamic) { + // the string has been held, so simply remove the ref from the + // queue + continue; + } if (sr->type != str_temp) PR_Error (pr, "internal string error: %d", __LINE__); if (R_STRING (pr) < 0 && string_index (res, sr) == R_STRING (pr)