mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Added "string_format" script function
Some code refactoring.
This commit is contained in:
+7
-7
@@ -707,29 +707,29 @@ CreditsAtBottom=0
|
||||
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
|
||||
;To change the path and filename of the critical table file, uncomment the next line
|
||||
;OverrideCriticalFile=CriticalOverrides.ini
|
||||
;OverrideCriticalFile=sfall\CriticalOverrides.ini
|
||||
|
||||
;To change the relationship between SPECIAL stats and derived stats, uncomment the next line
|
||||
;See the Stats.ini in the modders pack for an example file
|
||||
;DerivedStats=Stats.ini
|
||||
;DerivedStats=sfall\Stats.ini
|
||||
|
||||
;Allows you to edit the skill tables
|
||||
;Point the next line to an ini file containing the replacement skill data
|
||||
;SkillsFile=Skills.ini
|
||||
;SkillsFile=sfall\Skills.ini
|
||||
|
||||
;To add additional perks to the game, uncomment the next line and set it to point to a file containing perk information
|
||||
;PerksFile=Perks.ini
|
||||
;PerksFile=sfall\Perks.ini
|
||||
|
||||
;To add additional books to the game, uncomment the next line and point to a file containing book information
|
||||
;See the Books.ini in the modders pack for an example file
|
||||
;BooksFile=Books.ini
|
||||
;BooksFile=sfall\Books.ini
|
||||
|
||||
;Allows you to change some parameters for drugs and their addictions
|
||||
;See the Drugs.ini in the modders pack for an example file
|
||||
;DrugsFile=Drugs.ini
|
||||
;DrugsFile=sfall\Drugs.ini
|
||||
|
||||
;Point to an ini file containing elevator data
|
||||
;ElevatorsFile=Elevators.ini
|
||||
;ElevatorsFile=sfall\Elevators.ini
|
||||
|
||||
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||||
[Scripts]
|
||||
|
||||
@@ -320,6 +320,7 @@
|
||||
#define spatial_radius(obj) sfall_func1("spatial_radius", obj)
|
||||
#define string_compare(str1, str2) sfall_func2("string_compare", str1, str2)
|
||||
#define string_compare_locale(str1, str2, codePage) sfall_func3("string_compare", str1, str2, codePage)
|
||||
#define string_format(format, a1, a2) sfall_func3("string_format", format, a1, a2)
|
||||
#define tile_refresh_display sfall_func0("tile_refresh_display")
|
||||
#define unjam_lock(obj) sfall_func1("unjam_lock", obj)
|
||||
#define unset_unique_id(obj) sfall_func2("set_unique_id", obj, -1)
|
||||
|
||||
@@ -262,7 +262,7 @@ Some utility/math functions are available:
|
||||
- you can use this to search for a substring in a string like this: strlen(get_array(string_split(haystack, needle), 0))
|
||||
|
||||
> string substr(string, start, length)
|
||||
- cuts a substring from a string starting at "start" up to "length" characters. The first character position starts with 0 (zero).
|
||||
- cuts a substring from a string starting at "start" up to "length" characters. The first character position is 0 (zero).
|
||||
- If start is negative - it indicates starting position from the end of the string (for example substr("test", -2, 2) will return last 2 charactes: "st").
|
||||
- If length is negative - it means so many characters will be omitted from the end of string (example: substr("test", 0, -2) will return string without last 2 characters: "te").
|
||||
- If length is zero - it will return a string from the starting position to the end of the string **New behavior** for sfall 4.2.2/3.8.22
|
||||
@@ -682,6 +682,10 @@ optional argument:
|
||||
- codePage: code page number to properly compare national characters in the range 128-255 of the ASCII code table
|
||||
available encodings: 1250-1252, 866
|
||||
|
||||
> string sfall_func3("string_format", string format, any val1, any val2, ...)
|
||||
- formats given value using standard syntax of C printf function (google "printf" for format details). However it is limited to formatting up to 4 values
|
||||
- formatting is only supported for %s and %d. The format string is limited to 1024 characters
|
||||
|
||||
------------------------
|
||||
------ MORE INFO -------
|
||||
------------------------
|
||||
|
||||
@@ -310,19 +310,18 @@ hide:
|
||||
}
|
||||
}
|
||||
|
||||
static char* artDbgMsg = "\nError: file not found: %s\n";
|
||||
static char* artDbgMsg = "\nERROR: File not found: %s\n";
|
||||
static void __declspec(naked) art_data_size_hook() {
|
||||
__asm {
|
||||
test edi, edi;
|
||||
jz artNotExist;
|
||||
retn;
|
||||
artNotExist:
|
||||
mov eax, [esp + 0x124 - 0x1C + 4]; // filename
|
||||
push eax;
|
||||
mov edx, [esp + 0x124 - 0x1C + 4]; // filename
|
||||
push edx;
|
||||
push artDbgMsg;
|
||||
call fo::funcoffs::debug_printf_;
|
||||
mov eax, [esp + 0x124 - 0x1C + 12]; // filename
|
||||
push eax;
|
||||
push edx; // filename
|
||||
push artDbgMsg;
|
||||
lea eax, [esp + 0x124 - 0x124 + 20]; // buf
|
||||
push eax;
|
||||
@@ -368,7 +367,7 @@ static void DebugModePatch() {
|
||||
if (iniGetInt("Debugging", "HideObjIsNullMsg", 0, ::sfall::ddrawIni)) {
|
||||
MakeJump(0x453FD2, dbg_error_hack);
|
||||
}
|
||||
// prints a debug message about missing art file for critters to debug.log and the message window
|
||||
// prints a debug message about missing art file for critters to both debug.log and the message window
|
||||
HookCall(0x419B65, art_data_size_hook);
|
||||
|
||||
dlogr(" Done", DL_INIT);
|
||||
|
||||
@@ -523,7 +523,7 @@ end:
|
||||
|
||||
static void __declspec(naked) DialogHook() {
|
||||
__asm {
|
||||
test inLoop, DIALOG; // check bit flag
|
||||
test inLoop, DIALOG; // check byte flag
|
||||
jz changeMode;
|
||||
jmp fo::funcoffs::gdProcess_;
|
||||
changeMode:
|
||||
|
||||
@@ -51,6 +51,8 @@ static DWORD _stdcall HandleMapUpdateForScripts(const DWORD procId);
|
||||
|
||||
static int idle;
|
||||
|
||||
char ScriptExtender::gTextBuffer[5120]; // used as global temp text buffer for script functions
|
||||
|
||||
std::string ScriptExtender::iniConfigFolder;
|
||||
|
||||
struct GlobalScript {
|
||||
|
||||
@@ -40,6 +40,11 @@ public:
|
||||
|
||||
static std::string iniConfigFolder;
|
||||
|
||||
static char gTextBuffer[5120];
|
||||
|
||||
// returns the size of the global text buffer
|
||||
inline static const long TextBufferSize() { return sizeof(gTextBuffer); }
|
||||
|
||||
static long GetScriptReturnValue();
|
||||
static long GetResetScriptReturnValue();
|
||||
|
||||
|
||||
@@ -132,6 +132,7 @@ static const SfallMetarule metarules[] = {
|
||||
{"show_window", sf_show_window, 0, 1, -1, {ARG_STRING}},
|
||||
{"spatial_radius", sf_spatial_radius, 1, 1, 0, {ARG_OBJECT}},
|
||||
{"string_compare", sf_string_compare, 2, 3, 0, {ARG_STRING, ARG_STRING, ARG_INT}},
|
||||
{"string_format", sf_string_format, 2, 5, 0, {ARG_STRING, ARG_ANY, ARG_ANY, ARG_ANY, ARG_ANY}},
|
||||
{"tile_refresh_display", sf_tile_refresh_display, 0, 0},
|
||||
{"unjam_lock", sf_unjam_lock, 1, 1, -1, {ARG_OBJECT}},
|
||||
{"unwield_slot", sf_unwield_slot, 2, 2, -1, {ARG_OBJECT, ARG_INT}},
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace sfall
|
||||
namespace script
|
||||
{
|
||||
|
||||
const char* stringTooLong = "%s() - the string length exceeds maximum of 64 characters.";
|
||||
const char* stringTooLong = "%s() - the string exceeds maximum length of 64 characters.";
|
||||
|
||||
void sf_set_dm_model(OpcodeContext& ctx) {
|
||||
auto model = ctx.arg(0).strValue();
|
||||
@@ -453,7 +453,6 @@ specialIni:
|
||||
return 1;
|
||||
}
|
||||
|
||||
static char IniStrBuffer[256];
|
||||
static DWORD GetIniSetting(const char* str, bool isString) {
|
||||
const char* key;
|
||||
char section[33], file[128];
|
||||
@@ -462,9 +461,9 @@ static DWORD GetIniSetting(const char* str, bool isString) {
|
||||
return -1;
|
||||
}
|
||||
if (isString) {
|
||||
IniStrBuffer[0] = 0;
|
||||
iniGetString(section, key, "", IniStrBuffer, 256, file);
|
||||
return (DWORD)&IniStrBuffer[0];
|
||||
ScriptExtender::gTextBuffer[0] = 0;
|
||||
iniGetString(section, key, "", ScriptExtender::gTextBuffer, 256, file);
|
||||
return (DWORD)&ScriptExtender::gTextBuffer[0];
|
||||
} else {
|
||||
return iniGetInt(section, key, -1, file);
|
||||
}
|
||||
@@ -1037,8 +1036,8 @@ void sf_set_ini_setting(OpcodeContext& ctx) {
|
||||
|
||||
const char* saveValue;
|
||||
if (argVal.isInt()) {
|
||||
_itoa_s(argVal.rawValue(), IniStrBuffer, 10);
|
||||
saveValue = IniStrBuffer;
|
||||
_itoa_s(argVal.rawValue(), ScriptExtender::gTextBuffer, 10);
|
||||
saveValue = ScriptExtender::gTextBuffer;
|
||||
} else {
|
||||
saveValue = argVal.strValue();
|
||||
}
|
||||
@@ -1075,12 +1074,10 @@ static std::string GetIniFilePath(const ScriptValue& arg) {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
char getIniSectionBuf[5120];
|
||||
|
||||
void sf_get_ini_sections(OpcodeContext& ctx) {
|
||||
GetPrivateProfileSectionNamesA(getIniSectionBuf, 5120, GetIniFilePath(ctx.arg(0)).data());
|
||||
GetPrivateProfileSectionNamesA(ScriptExtender::gTextBuffer, ScriptExtender::TextBufferSize(), GetIniFilePath(ctx.arg(0)).data());
|
||||
std::vector<char*> sections;
|
||||
char* section = getIniSectionBuf;
|
||||
char* section = ScriptExtender::gTextBuffer;
|
||||
while (*section != 0) {
|
||||
sections.push_back(section); // position
|
||||
section += std::strlen(section) + 1;
|
||||
@@ -1099,10 +1096,10 @@ void sf_get_ini_sections(OpcodeContext& ctx) {
|
||||
|
||||
void sf_get_ini_section(OpcodeContext& ctx) {
|
||||
auto section = ctx.arg(1).strValue();
|
||||
GetPrivateProfileSectionA(section, getIniSectionBuf, 5120, GetIniFilePath(ctx.arg(0)).data());
|
||||
GetPrivateProfileSectionA(section, ScriptExtender::gTextBuffer, ScriptExtender::TextBufferSize(), GetIniFilePath(ctx.arg(0)).data());
|
||||
int arrayId = TempArray(-1, 0); // associative
|
||||
auto& arr = arrays[arrayId];
|
||||
char *key = getIniSectionBuf, *val = nullptr;
|
||||
char *key = ScriptExtender::gTextBuffer, *val = nullptr;
|
||||
while (*key != 0) {
|
||||
char* val = std::strpbrk(key, "=");
|
||||
if (val != nullptr) {
|
||||
|
||||
@@ -190,8 +190,6 @@ void sf_string_split(OpcodeContext& ctx) {
|
||||
ctx.setReturn(StringSplit(ctx.arg(0).strValue(), ctx.arg(1).strValue()));
|
||||
}
|
||||
|
||||
static char* tempTextBuf = nullptr;
|
||||
|
||||
char* Substring(const char* str, int startPos, int length) {
|
||||
int len = strlen(str);
|
||||
|
||||
@@ -201,11 +199,8 @@ char* Substring(const char* str, int startPos, int length) {
|
||||
}
|
||||
if (length < 0) {
|
||||
length += len - startPos; // cutoff at end
|
||||
if (length == 0) {
|
||||
return "";
|
||||
} else if (length < 0) {
|
||||
length = -length; // length can't be negative
|
||||
}
|
||||
if (length == 0) return "";
|
||||
abs(length); // length can't be negative
|
||||
}
|
||||
// check position
|
||||
if (startPos >= len) return ""; // start position is out of string length, return empty string
|
||||
@@ -213,11 +208,12 @@ char* Substring(const char* str, int startPos, int length) {
|
||||
length = len - startPos; // set the correct length, the length of characters goes beyond the end of the string
|
||||
}
|
||||
|
||||
if (tempTextBuf) delete[] tempTextBuf;
|
||||
tempTextBuf = new char[length + 1];
|
||||
memcpy(tempTextBuf, &str[startPos], length);
|
||||
tempTextBuf[length] = '\0';
|
||||
return tempTextBuf;
|
||||
const int bufMax = ScriptExtender::TextBufferSize() - 1;
|
||||
if (length > bufMax) length = bufMax;
|
||||
|
||||
memcpy(ScriptExtender::gTextBuffer, &str[startPos], length);
|
||||
ScriptExtender::gTextBuffer[length] = '\0';
|
||||
return ScriptExtender::gTextBuffer;
|
||||
}
|
||||
|
||||
void sf_substr(OpcodeContext& ctx) {
|
||||
@@ -241,8 +237,7 @@ static char* _stdcall sprintf_lite(const char* format, ScriptValue value) {
|
||||
int fmtlen = strlen(format);
|
||||
int buflen = fmtlen + 1;
|
||||
for (int i = 0; i < fmtlen; i++) {
|
||||
if (format[i] == '%')
|
||||
buflen++; // will possibly be escaped, need space for that
|
||||
if (format[i] == '%') buflen++; // will possibly be escaped, need space for that
|
||||
}
|
||||
// parse format to make it safe
|
||||
char* newfmt = new char[buflen];
|
||||
@@ -290,7 +285,8 @@ static char* _stdcall sprintf_lite(const char* format, ScriptValue value) {
|
||||
newfmt[j++] = c;
|
||||
}
|
||||
newfmt[j] = '\0';
|
||||
// calculate required memory
|
||||
|
||||
// calculate required length
|
||||
if (hasDigits) {
|
||||
buflen = 254;
|
||||
} else if (specifier == 'c') {
|
||||
@@ -300,18 +296,18 @@ static char* _stdcall sprintf_lite(const char* format, ScriptValue value) {
|
||||
} else {
|
||||
buflen = j + 30; // numbers
|
||||
}
|
||||
if (tempTextBuf) {
|
||||
delete[] tempTextBuf;
|
||||
}
|
||||
tempTextBuf = new char[buflen + 1];
|
||||
|
||||
const long bufMaxLen = ScriptExtender::TextBufferSize() - 1;
|
||||
if (buflen > bufMaxLen - 1) buflen = bufMaxLen - 1;
|
||||
ScriptExtender::gTextBuffer[bufMaxLen] = '\0';
|
||||
|
||||
if (value.isFloat()) {
|
||||
_snprintf(tempTextBuf, buflen, newfmt, value.floatValue());
|
||||
_snprintf(ScriptExtender::gTextBuffer, buflen, newfmt, value.floatValue());
|
||||
} else {
|
||||
_snprintf(tempTextBuf, buflen, newfmt, value.rawValue());
|
||||
_snprintf(ScriptExtender::gTextBuffer, buflen, newfmt, value.rawValue());
|
||||
}
|
||||
tempTextBuf[buflen] = '\0'; // just in case
|
||||
delete[] newfmt;
|
||||
return tempTextBuf;
|
||||
return ScriptExtender::gTextBuffer;
|
||||
}
|
||||
|
||||
void sf_sprintf(OpcodeContext& ctx) {
|
||||
@@ -320,6 +316,53 @@ void sf_sprintf(OpcodeContext& ctx) {
|
||||
);
|
||||
}
|
||||
|
||||
void sf_string_format(OpcodeContext& ctx) {
|
||||
const char* format = ctx.arg(0).strValue();
|
||||
|
||||
int fmtLen = strlen(format);
|
||||
if (fmtLen == 0) {
|
||||
ctx.setReturn(format);
|
||||
return;
|
||||
}
|
||||
if (fmtLen > 1024) {
|
||||
ctx.printOpcodeError("%s() - the format string exceeds maximum length of 1024 characters.", ctx.getMetaruleName());
|
||||
ctx.setReturn("Error");
|
||||
} else {
|
||||
char* newFmt = new char[fmtLen + 1];
|
||||
newFmt[fmtLen] = '\0';
|
||||
// parse format to make it safe
|
||||
int i = 0;
|
||||
do {
|
||||
char c = format[i];
|
||||
if (c == '%') {
|
||||
char cf = format[i + 1];
|
||||
if (cf != 's' && cf != 'd' && cf != '%') c = ' '; // unsupported format
|
||||
}
|
||||
newFmt[i] = c;
|
||||
} while (++i < fmtLen);
|
||||
|
||||
const long bufMaxLen = ScriptExtender::TextBufferSize() - 1;
|
||||
|
||||
switch (ctx.numArgs()) {
|
||||
case 2 :
|
||||
_snprintf(ScriptExtender::gTextBuffer, bufMaxLen, newFmt, ctx.arg(1).rawValue());
|
||||
break;
|
||||
case 3 :
|
||||
_snprintf(ScriptExtender::gTextBuffer, bufMaxLen, newFmt, ctx.arg(1).rawValue(), ctx.arg(2).rawValue());
|
||||
break;
|
||||
case 4 :
|
||||
_snprintf(ScriptExtender::gTextBuffer, bufMaxLen, newFmt, ctx.arg(1).rawValue(), ctx.arg(2).rawValue(), ctx.arg(3).rawValue());
|
||||
break;
|
||||
case 5 :
|
||||
_snprintf(ScriptExtender::gTextBuffer, bufMaxLen, newFmt, ctx.arg(1).rawValue(), ctx.arg(2).rawValue(), ctx.arg(3).rawValue(), ctx.arg(4).rawValue());
|
||||
}
|
||||
ScriptExtender::gTextBuffer[bufMaxLen] = '\0'; // just in case
|
||||
|
||||
delete[] newFmt;
|
||||
ctx.setReturn(ScriptExtender::gTextBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
void sf_power(OpcodeContext& ctx) {
|
||||
const ScriptValue &base = ctx.arg(0),
|
||||
&power = ctx.arg(1);
|
||||
|
||||
@@ -53,6 +53,8 @@ void sf_string_compare(OpcodeContext&);
|
||||
|
||||
void sf_sprintf(OpcodeContext&);
|
||||
|
||||
void sf_string_format(OpcodeContext&);
|
||||
|
||||
void sf_ord(OpcodeContext&);
|
||||
|
||||
void sf_typeof(OpcodeContext&);
|
||||
|
||||
Reference in New Issue
Block a user