feat(script): implement FrameScript_Sprintf

This commit is contained in:
VDm 2024-12-10 01:34:59 +04:00
parent 382d962900
commit 02e683d699
4 changed files with 221 additions and 159 deletions

View File

@ -7,160 +7,6 @@
#include <cctype>
// TEMPORARY SOLUTION (based on lstrlib.c from LUA)
/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */
#define MAX_ITEM 512
/* valid flags in a format specification */
#define FLAGS "-+ #0"
/*
** maximum size of each format specification (such as '%-099.99d')
** (+10 accounts for %99.99x plus margin of error)
*/
#define MAX_FORMAT (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10)
#define uchar(c) ((unsigned char)(c))
static void addchar(char* b, char ch) {
auto len = strlen(b);
b[len++] = ch;
b[len] = '\0';
}
static void addquoted(lua_State* L, char* b, int arg) {
size_t l;
const char* s = luaL_checklstring(L, arg, &l);
addchar(b, '"');
while (l--) {
switch (*s) {
case '"':
case '\\':
case '\n': {
addchar(b, '\\');
addchar(b, *s);
break;
}
case '\r': {
strcat(b, "\\r");
break;
}
case '\0': {
strcat(b, "\\000");
break;
}
default: {
addchar(b, *s);
break;
}
}
s++;
}
addchar(b, '"');
}
static const char* scanformat(lua_State* L, const char* strfrmt, char* form) {
const char* p = strfrmt;
while (*p != '\0' && strchr(FLAGS, *p) != NULL)
p++; /* skip flags */
if ((size_t)(p - strfrmt) >= sizeof(FLAGS))
luaL_error(L, "invalid format (repeated flags)");
if (isdigit(uchar(*p)))
p++; /* skip width */
if (isdigit(uchar(*p)))
p++; /* (2 digits at most) */
if (*p == '.') {
p++;
if (isdigit(uchar(*p)))
p++; /* skip precision */
if (isdigit(uchar(*p)))
p++; /* (2 digits at most) */
}
if (isdigit(uchar(*p)))
luaL_error(L, "invalid format (width or precision too long)");
*(form++) = '%';
strncpy(form, strfrmt, p - strfrmt + 1);
form += p - strfrmt + 1;
*form = '\0';
return p;
}
static void addintlen(char* form) {
size_t l = strlen(form);
char spec = form[l - 1];
strcpy(form + l - 1, LUA_INTFRMLEN);
form[l + sizeof(LUA_INTFRMLEN) - 2] = spec;
form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0';
}
static int str_format(lua_State* L, char* b) {
int arg = 2;
size_t sfl;
const char* strfrmt = luaL_checklstring(L, arg, &sfl);
const char* strfrmt_end = strfrmt + sfl;
while (strfrmt < strfrmt_end) {
if (*strfrmt != '%')
addchar(b, *strfrmt++);
else if (*++strfrmt == '%')
addchar(b, *strfrmt++); /* %% */
else { /* format item */
char form[MAX_FORMAT]; /* to store the format (`%...') */
char buff[MAX_ITEM]; /* to store the formatted item */
arg++;
strfrmt = scanformat(L, strfrmt, form);
switch (*strfrmt++) {
case 'c': {
sprintf(buff, form, (int)luaL_checknumber(L, arg));
break;
}
case 'd':
case 'i': {
addintlen(form);
sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg));
break;
}
case 'o':
case 'u':
case 'x':
case 'X': {
addintlen(form);
sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg));
break;
}
case 'e':
case 'E':
case 'f':
case 'g':
case 'G': {
sprintf(buff, form, (double)luaL_checknumber(L, arg));
break;
}
case 'q': {
addquoted(L, b, arg);
continue; /* skip the 'addsize' at the end */
}
case 's': {
size_t l;
const char* s = luaL_checklstring(L, arg, &l);
if (!strchr(form, '.') && l >= 100) {
/* no precision and string is too long to be formatted;
keep original string */
continue; /* skip the `addsize' at the end */
} else {
sprintf(buff, form, s);
break;
}
}
default: { /* also treat cases `pnLlh' */
return luaL_error(L, "invalid option " LUA_QL("%%%c") " to " LUA_QL("format"), *(strfrmt - 1));
}
}
strcat(b, buff);
}
}
return 1;
}
// END OF TEMPORARY SOLUTION
int32_t CSimpleFontString_IsObjectType(lua_State* L) {
WHOA_UNIMPLEMENTED(0);
}
@ -301,9 +147,9 @@ int32_t CSimpleFontString_SetFormattedText(lua_State* L) {
return 0;
}
char b[2048] = {};
str_format(L, b);
string->SetText(b, 0);
char text[2048] = {};
FrameScript_Sprintf(L, 2, text, sizeof(text));
string->SetText(text, 0);
return 0;
}

View File

@ -10,6 +10,7 @@
#include <storm/Array.hpp>
#include <storm/String.hpp>
#include <tempest/Vector.hpp>
#include <cctype>
const char* g_glueScriptEvents[41];
const char* g_scriptEvents[722];
@ -867,6 +868,216 @@ void FrameScript_UnregisterScriptEvent(FrameScript_Object* object, FrameScript_E
}
}
static void addchar(char* buffer, size_t bufferSize, char ch) {
auto length = SStrLen(buffer);
if (length + 1 < bufferSize)
{
buffer[length++] = ch;
buffer[length] = '\0';
}
}
static void addstring(char* buffer, size_t bufferSize, const char* source) {
uint32_t dsize = 0;
uint32_t size = 0;
dsize = SStrLen(buffer);
size = SStrLen(source);
if (dsize + size >= bufferSize) {
size = bufferSize - dsize;
// Check for space for trailing zero
if (size < 2) {
size = 0;
} else {
size--;
}
}
if (size > 0)
memmove(&buffer[dsize], source, size);
buffer[dsize + size] = '\0';
}
static void addstring(char* buffer, size_t bufferSize, const char* source, size_t count) {
uint32_t dsize = 0;
uint32_t size = 0;
dsize = SStrLen(buffer);
size = std::min(SStrLen(source), count);
if (dsize + size >= bufferSize) {
size = bufferSize - dsize;
// Check for space for trailing zero
if (size < 2) {
size = 0;
} else {
size--;
}
}
if (size > 0)
memmove(&buffer[dsize], source, size);
buffer[dsize + size] = '\0';
}
static void addquoted(lua_State* L, char* buffer, size_t bufferSize, int arg) {
size_t l;
const char* s = luaL_checklstring(L, arg, &l);
addchar(buffer, bufferSize, '"');
while (l--) {
switch (*s) {
case '"':
case '\\':
case '\n': {
addchar(buffer, bufferSize, '\\');
addchar(buffer, bufferSize, *s);
break;
}
case '\r': {
addstring(buffer, bufferSize, "\\r");
break;
}
case '\0': {
addstring(buffer, bufferSize, "\\000");
break;
}
default: {
addchar(buffer, bufferSize, *s);
break;
}
}
s++;
}
addchar(buffer, bufferSize, '"');
}
#define FORMAT_FLAGS "-+ #0"
static const char* scanformat(lua_State* L, const char* strfrmt, char* form) {
const char* flags = "-+ #0";
const char* p = strfrmt;
while (*p != '\0' && SStrChrR(FORMAT_FLAGS, *p) != NULL) {
p++; /* skip flags */
}
if ((size_t)(p - strfrmt) >= sizeof(FORMAT_FLAGS)) {
luaL_error(L, "invalid format (repeated flags)");
}
if (isdigit((unsigned char)(*p))) {
p++; /* skip width */
}
if (isdigit((unsigned char)(*p))) {
p++; /* (2 digits at most) */
}
if (*p == '.') {
p++;
if (isdigit((unsigned char)(*p))) {
p++; /* skip precision */
}
if (isdigit((unsigned char)(*p))) {
p++; /* (2 digits at most) */
}
}
if (isdigit((unsigned char)(*p))) {
luaL_error(L, "invalid format (width or precision too long)");
}
*(form++) = '%';
strncpy(form, strfrmt, p - strfrmt + 1);
form += p - strfrmt + 1;
*form = '\0';
return p;
}
static void addintlen(char* form) {
size_t l = SStrLen(form);
char spec = form[l - 1];
strcpy(form + l - 1, LUA_INTFRMLEN);
form[l + sizeof(LUA_INTFRMLEN) - 2] = spec;
form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0';
}
void FrameScript_Sprintf(lua_State* L, int startIndex, char* buffer, uint32_t bufferSize) {
// maximum size of each formatted item (> len(format('%99.99f', -1e308)))
const size_t MAX_ITEM = 512;
// maximum size of each format specification (such as '%-099.99d')
// (+10 accounts for %99.99x plus margin of error)
const size_t MAX_FORMAT = sizeof(FORMAT_FLAGS) + sizeof(LUA_INTFRMLEN) + 10;
int arg = startIndex;
size_t sfl;
const char* strfrmt = luaL_checklstring(L, arg, &sfl);
const char* strfrmt_end = strfrmt + sfl;
while (strfrmt < strfrmt_end) {
if (*strfrmt != '%') {
addchar(buffer, bufferSize, *strfrmt++);
} else if (*++strfrmt == '%') {
addchar(buffer, bufferSize, *strfrmt++); /* %% */
} else { /* format item */
char form[MAX_FORMAT]; /* to store the format (`%...') */
char buff[MAX_ITEM]; /* to store the formatted item */
arg++;
strfrmt = scanformat(L, strfrmt, form);
switch (*strfrmt++) {
case 'c': {
sprintf(buff, form, (int)luaL_checknumber(L, arg));
break;
}
case 'd':
case 'i': {
addintlen(form);
sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg));
break;
}
case 'o':
case 'u':
case 'x':
case 'X': {
addintlen(form);
sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg));
break;
}
case 'e':
case 'E':
case 'f':
case 'g':
case 'G': {
sprintf(buff, form, (double)luaL_checknumber(L, arg));
break;
}
case 'q': {
addquoted(L, buffer, bufferSize, arg);
continue; /* skip the 'addsize' at the end */
}
case 's': {
size_t l;
const char* s = luaL_checklstring(L, arg, &l);
if (!strchr(form, '.') && l >= 100) {
/* no precision and string is too long to be formatted;
keep original string */
continue; /* skip the `addsize' at the end */
} else {
sprintf(buff, form, s);
break;
}
}
default: { /* also treat cases `pnLlh' */
luaL_error(L, "invalid option " LUA_QL("%%%c") " to " LUA_QL("format"), *(strfrmt - 1));
}
}
addstring(buffer, bufferSize, buff);
}
}
}
void GlueScriptEventsInitialize() {
g_glueScriptEvents[0] = "SET_GLUE_SCREEN";
g_glueScriptEvents[1] = "START_GLUE_MUSIC";

View File

@ -100,6 +100,8 @@ void FrameScript_SignalEvent(uint32_t index, const char* format, ...);
void FrameScript_UnregisterScriptEvent(FrameScript_Object* object, FrameScript_EventObject* event);
void FrameScript_Sprintf(lua_State* L, int startIndex, char* buffer, uint32_t bufferSize);
void GlueScriptEventsInitialize();
void ScriptEventsInitialize();

View File

@ -19,8 +19,11 @@ int32_t Script_IsShiftKeyDown(lua_State* L) {
}
int32_t Script_GetBuildInfo(lua_State* L) {
lua_pushstring(L, "WHOA");
lua_pushstring(L, "Release");
auto szVersion = FrameScript_GetText("VERSION", -1, GENDER_NOT_APPLICABLE);
auto szVersionType = FrameScript_GetText("RELEASE_BUILD", -1, GENDER_NOT_APPLICABLE);
lua_pushstring(L, szVersion);
lua_pushstring(L, szVersionType);
lua_pushstring(L, "3.3.5");
lua_pushstring(L, "12340");
lua_pushstring(L, "Jun 24 2010");