mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Fixed position jumping for console window
Improved "string_format" script function. Edited error messages for a few functions. Updated documents.
This commit is contained in:
@@ -391,6 +391,11 @@
|
||||
#define string_format2(format, a1, a2) sfall_func3("string_format", format, a1, a2)
|
||||
#define string_format3(format, a1, a2, a3) sfall_func4("string_format", format, a1, a2, a3)
|
||||
#define string_format4(format, a1, a2, a3, a4) sfall_func5("string_format", format, a1, a2, a3, a4)
|
||||
#define string_format5(format, a1, a2, a3, a4, a5) sfall_func6("string_format", format, a1, a2, a3, a4, a5)
|
||||
#define string_format6(format, a1, a2, a3, a4, a5, a6) sfall_func7("string_format", format, a1, a2, a3, a4, a5, a6)
|
||||
#define string_format7(format, a1, a2, a3, a4, a5, a6, a7) sfall_func8("string_format", format, a1, a2, a3, a4, a5, a6, a7)
|
||||
#define string_format_MACRO(_0,_1,_2,_3,_4,_5,_6,FUNC,...) FUNC
|
||||
#define string_format(...) string_format_MACRO(__VA_ARGS__, string_format6, string_format5, string_format4, string_format3, string_format2, string_format1)(__VA_ARGS__)
|
||||
#define string_tolower(text) sfall_func2("string_to_case", text, 0)
|
||||
#define string_toupper(text) sfall_func2("string_to_case", text, 1)
|
||||
#define tile_by_position(x, y) sfall_func2("tile_by_position", x, y)
|
||||
|
||||
@@ -339,7 +339,7 @@ FUNCTION REFERENCE
|
||||
|
||||
-----
|
||||
##### `string sprintf(string format, any value)`
|
||||
- Formats given value using standard syntax of C `printf` function (google "printf" for format details). However it is limited to formatting only 1 value.
|
||||
- Formats given value using standard syntax of C `printf` function (google "printf" for format details). However, it is limited to formatting only 1 value.
|
||||
- Can be used to get character by ASCII code (`%c`).
|
||||
|
||||
-----
|
||||
@@ -907,9 +907,10 @@ sfall_funcX metarule functions
|
||||
|
||||
----
|
||||
#### string_format
|
||||
`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`, and the format string is limited to 1024 characters
|
||||
`string sfall_func2("string_format", string format, any val1)`
|
||||
`string sfall_funcX("string_format", string format, any val1, any val2, ...)`
|
||||
- Formats given values using standard syntax of C `printf` function (google "printf" for format details). However, it is limited to formatting up to 7 values
|
||||
- The format string is limited to 1024 characters
|
||||
|
||||
----
|
||||
#### objects_in_radius
|
||||
|
||||
@@ -55,10 +55,14 @@ void ConsoleWindow::loadPosition() {
|
||||
screenHeight = GetSystemMetrics(SM_CYSCREEN);
|
||||
int w = min(max(windowData[2], 640), screenWidth),
|
||||
h = min(max(windowData[3], 480), screenHeight),
|
||||
x = min(max(windowData[0], 0), screenWidth - w),
|
||||
y = min(max(windowData[1], 0), screenHeight - h);
|
||||
x = min(max(windowData[0], 0), screenWidth - w/2),
|
||||
y = min(max(windowData[1], 0), screenHeight - h/2);
|
||||
|
||||
if (!SetWindowPos(wnd, HWND_TOP, x, y, w, h, 0)) {
|
||||
dlog_f("Setting console window position: (%d, %d), size: %dx%d\n", DL_MAIN, x, y, w, h);
|
||||
if (!SetWindowPos(wnd, HWND_TOP, 0, 0, w, h, SWP_NOMOVE)) {
|
||||
dlog_f("Error resizing console window: 0x%x\n", DL_MAIN, GetLastError());
|
||||
}
|
||||
if (!SetWindowPos(wnd, HWND_TOP, x, y, 0, 0, SWP_NOSIZE)) {
|
||||
dlog_f("Error repositioning console window: 0x%x\n", DL_MAIN, GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ static MetaruleTableType metaruleTable;
|
||||
{ name, handler, minArgs, maxArgs, error, {arg1, arg2, ...} }
|
||||
- name - name of function that will be used in scripts,
|
||||
- handler - pointer to handler function (see examples below),
|
||||
- minArgs/maxArgs - minimum and maximum number of arguments allowed for this function (max 6)
|
||||
- minArgs/maxArgs - minimum and maximum number of arguments allowed for this function (max 8)
|
||||
- returned error value for argument validation,
|
||||
- arg1, arg2, ... - argument types for automatic validation
|
||||
*/
|
||||
@@ -153,7 +153,7 @@ static const SfallMetarule metarules[] = {
|
||||
{"show_window", mf_show_window, 0, 1, -1, {ARG_STRING}},
|
||||
{"spatial_radius", mf_spatial_radius, 1, 1, 0, {ARG_OBJECT}},
|
||||
{"string_compare", mf_string_compare, 2, 3, 0, {ARG_STRING, ARG_STRING, ARG_INT}},
|
||||
{"string_format", mf_string_format, 2, 5, 0, {ARG_STRING, ARG_ANY, ARG_ANY, ARG_ANY, ARG_ANY}},
|
||||
{"string_format", mf_string_format, 2, 8, 0, {ARG_STRING, ARG_ANY, ARG_ANY, ARG_ANY, ARG_ANY, ARG_ANY, ARG_ANY, ARG_ANY}},
|
||||
{"string_to_case", mf_string_to_case, 2, 2, -1, {ARG_STRING, ARG_INT}},
|
||||
{"tile_by_position", mf_tile_by_position, 2, 2, -1, {ARG_INT, ARG_INT}},
|
||||
{"tile_refresh_display", mf_tile_refresh_display, 0, 0},
|
||||
|
||||
@@ -156,7 +156,7 @@ void op_set_script(OpcodeContext& ctx) {
|
||||
|
||||
long scriptIndex = valArg & ~0xF0000000;
|
||||
if (scriptIndex == 0 || valArg > 0x8FFFFFFF) { // negative values are not allowed
|
||||
ctx.printOpcodeError("%s() - the script index number is incorrect.", ctx.getOpcodeName());
|
||||
ctx.printOpcodeError("%s() - invalid script index number.", ctx.getOpcodeName());
|
||||
return;
|
||||
}
|
||||
scriptIndex--;
|
||||
@@ -429,7 +429,7 @@ void mf_item_make_explosive(OpcodeContext& ctx) {
|
||||
if (pid > 0 && pidActive > 0) {
|
||||
Explosions::AddToExplosives(pid, pidActive, min, max);
|
||||
} else {
|
||||
ctx.printOpcodeError("%s() - invalid PID number, must be greater than 0.", ctx.getMetaruleName());
|
||||
ctx.printOpcodeError("%s() - invalid PID number.", ctx.getMetaruleName());
|
||||
ctx.setReturn(-1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,148 +199,92 @@ void mf_string_compare(OpcodeContext& ctx) {
|
||||
}
|
||||
|
||||
// A safer version of sprintf for using in user scripts.
|
||||
static char* sprintf_lite(const char* format, const ScriptValue& value) {
|
||||
int fmtlen = strlen(format);
|
||||
int buflen = fmtlen + 1;
|
||||
static const char* sprintf_lite(OpcodeContext& ctx, const char* opcodeName) {
|
||||
const char* format = ctx.arg(0).strValue();
|
||||
int fmtLen = strlen(format);
|
||||
if (fmtLen == 0) {
|
||||
return format;
|
||||
}
|
||||
if (fmtLen > 1024) {
|
||||
ctx.printOpcodeError("%s() - format string exceeds maximum length of 1024 characters.", opcodeName);
|
||||
return "Error";
|
||||
}
|
||||
int newFmtLen = fmtLen;
|
||||
|
||||
for (int i = 0; i < fmtlen; i++) {
|
||||
if (format[i] == '%') buflen++; // will possibly be escaped, need space for that
|
||||
for (int i = 0; i < fmtLen; i++) {
|
||||
if (format[i] == '%') newFmtLen++; // will possibly be escaped, need space for that
|
||||
}
|
||||
|
||||
// parse format to make it safe
|
||||
char* newfmt = new char[buflen];
|
||||
unsigned char mode = 0;
|
||||
char specifier = 0;
|
||||
bool hasDigits = false;
|
||||
char* newFmt = new char[newFmtLen + 1];
|
||||
bool conversion = false;
|
||||
int j = 0;
|
||||
int valIdx = 0;
|
||||
char* outBuf = ScriptExtender::gTextBuffer;
|
||||
long bufCount = ScriptExtender::TextBufferSize() - 1;
|
||||
int numArgs = ctx.numArgs();
|
||||
|
||||
for (int i = 0; i < fmtlen; i++) {
|
||||
for (int i = 0; i < fmtLen; i++) {
|
||||
char c = format[i];
|
||||
switch (mode) {
|
||||
case 0: // prefix
|
||||
if (!conversion) {
|
||||
// Start conversion.
|
||||
if (c == '%') conversion = true;
|
||||
} else {
|
||||
if (c == '%') {
|
||||
mode = 1;
|
||||
}
|
||||
break;
|
||||
case 1: // definition
|
||||
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
|
||||
if (c == 'h' || c == 'l' || c == 'j' || c == 'z' || c == 't' || c == 'L') continue; // ignore sub-specifiers
|
||||
|
||||
if (c == 's' && !value.isString() || // don't allow to treat non-string values as string pointers
|
||||
c == 'n') // don't allow "n" specifier
|
||||
conversion = false; // escaped % sign, just skip
|
||||
} else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) {
|
||||
// ignore size prefixes
|
||||
if (c == 'h' || c == 'l' || c == 'j' || c == 'z' || c == 't' || c == 'w' || c == 'L' || c == 'I') continue;
|
||||
// Type specifier, perform conversion.
|
||||
if (++valIdx == numArgs) {
|
||||
ctx.printOpcodeError("%s() - format string contains more conversions than passed arguments (%d): %s", opcodeName, numArgs - 1, format);
|
||||
}
|
||||
const ScriptValue& arg = ctx.arg(valIdx < numArgs ? valIdx : numArgs - 1);
|
||||
if (c == 'S' || c == 'Z') {
|
||||
c = 's'; // don't allow wide strings
|
||||
}
|
||||
if (c == 's' && !arg.isString() || // don't allow treating non-string values as string pointers
|
||||
c == 'n') // don't allow "n" specifier
|
||||
{
|
||||
c = 'd';
|
||||
}
|
||||
specifier = c;
|
||||
mode = 2;
|
||||
} else if (c == '%') {
|
||||
mode = 0;
|
||||
hasDigits = false;
|
||||
} else if (c >= '0' && c <= '9') {
|
||||
hasDigits = true;
|
||||
newFmt[j++] = c;
|
||||
newFmt[j] = '\0';
|
||||
int partLen = arg.isFloat()
|
||||
? _snprintf(outBuf, bufCount, newFmt, arg.floatValue())
|
||||
: _snprintf(outBuf, bufCount, newFmt, arg.rawValue());
|
||||
outBuf += partLen;
|
||||
bufCount -= partLen;
|
||||
conversion = false;
|
||||
j = 0;
|
||||
if (bufCount <= 0) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case 2: // postfix
|
||||
if (c == '%') { // don't allow more than one specifier
|
||||
newfmt[j++] = '%'; // escape it
|
||||
if (format[i + 1] == '%') i++; // skip already escaped
|
||||
}
|
||||
break;
|
||||
}
|
||||
newfmt[j++] = c;
|
||||
newFmt[j++] = c;
|
||||
}
|
||||
newfmt[j] = '\0';
|
||||
|
||||
// calculate required length
|
||||
if (hasDigits) {
|
||||
buflen = 254;
|
||||
} else if (specifier == 'c') {
|
||||
buflen = j;
|
||||
} else if (specifier == 's') {
|
||||
buflen = j + strlen(value.strValue());
|
||||
} else {
|
||||
buflen = j + 30; // numbers
|
||||
// Copy the remainder of the string.
|
||||
if (bufCount > 0) {
|
||||
newFmt[j] = '\0';
|
||||
strcpy_s(outBuf, bufCount, newFmt);
|
||||
}
|
||||
|
||||
const long bufMaxLen = ScriptExtender::TextBufferSize() - 1;
|
||||
if (buflen > bufMaxLen - 1) buflen = bufMaxLen - 1;
|
||||
ScriptExtender::gTextBuffer[bufMaxLen] = '\0';
|
||||
|
||||
if (value.isFloat()) {
|
||||
_snprintf(ScriptExtender::gTextBuffer, buflen, newfmt, value.floatValue());
|
||||
} else {
|
||||
_snprintf(ScriptExtender::gTextBuffer, buflen, newfmt, value.rawValue());
|
||||
}
|
||||
delete[] newfmt;
|
||||
delete[] newFmt;
|
||||
return ScriptExtender::gTextBuffer;
|
||||
}
|
||||
|
||||
void op_sprintf(OpcodeContext& ctx) {
|
||||
ctx.setReturn(
|
||||
sprintf_lite(ctx.arg(0).strValue(), ctx.arg(1))
|
||||
sprintf_lite(ctx, ctx.getOpcodeName())
|
||||
);
|
||||
}
|
||||
|
||||
void mf_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, arg = 0, totalArg = ctx.numArgs(); // total passed args
|
||||
do {
|
||||
char c = format[i];
|
||||
if (c == '%') {
|
||||
char cf = format[i + 1];
|
||||
if (cf != '%') {
|
||||
if (arg >= 0) {
|
||||
arg++;
|
||||
if (arg == totalArg) arg = -1; // format '%' prefixes in the format string exceed the number of passed value args
|
||||
}
|
||||
if (arg < 0) { // have % more than passed value args
|
||||
c = '^'; // delete %
|
||||
}
|
||||
// check string is valid or replace unsupported format
|
||||
else if ((cf == 's' && (arg > 0 && !ctx.arg(arg).isString())) || (cf != 's' && cf != 'd')) {
|
||||
newFmt[i++] = c;
|
||||
c = 'd'; // replace with %d
|
||||
}
|
||||
} else {
|
||||
newFmt[i++] = cf; // skip %%
|
||||
}
|
||||
}
|
||||
newFmt[i] = c;
|
||||
} while (++i < fmtLen);
|
||||
|
||||
const long bufMaxLen = ScriptExtender::TextBufferSize() - 1;
|
||||
|
||||
switch (totalArg) {
|
||||
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);
|
||||
}
|
||||
ctx.setReturn(
|
||||
sprintf_lite(ctx, ctx.getMetaruleName())
|
||||
);
|
||||
}
|
||||
|
||||
void op_message_str_game(OpcodeContext& ctx) {
|
||||
|
||||
Reference in New Issue
Block a user