From 02e683d699fb3e2fa341880d0107ef045aaadb9e Mon Sep 17 00:00:00 2001 From: VDm Date: Tue, 10 Dec 2024 01:34:59 +0400 Subject: [PATCH] feat(script): implement FrameScript_Sprintf --- src/ui/CSimpleFontStringScript.cpp | 160 +--------------- src/ui/FrameScript.cpp | 211 +++++++++++++++++++++ src/ui/FrameScript.hpp | 2 + src/ui/ScriptFunctionsGlueScriptEvents.cpp | 7 +- 4 files changed, 221 insertions(+), 159 deletions(-) diff --git a/src/ui/CSimpleFontStringScript.cpp b/src/ui/CSimpleFontStringScript.cpp index 489e9e5..247918b 100644 --- a/src/ui/CSimpleFontStringScript.cpp +++ b/src/ui/CSimpleFontStringScript.cpp @@ -7,160 +7,6 @@ #include -// 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; } diff --git a/src/ui/FrameScript.cpp b/src/ui/FrameScript.cpp index 558c517..fb51ca6 100644 --- a/src/ui/FrameScript.cpp +++ b/src/ui/FrameScript.cpp @@ -10,6 +10,7 @@ #include #include #include +#include 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"; diff --git a/src/ui/FrameScript.hpp b/src/ui/FrameScript.hpp index debf40f..6221927 100644 --- a/src/ui/FrameScript.hpp +++ b/src/ui/FrameScript.hpp @@ -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(); diff --git a/src/ui/ScriptFunctionsGlueScriptEvents.cpp b/src/ui/ScriptFunctionsGlueScriptEvents.cpp index e97945e..e34011a 100644 --- a/src/ui/ScriptFunctionsGlueScriptEvents.cpp +++ b/src/ui/ScriptFunctionsGlueScriptEvents.cpp @@ -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");