Added "string_format" script function

Rewrote ASM code of opcode handlers in MemoryOp.hpp.
This commit is contained in:
NovaRain
2019-12-03 10:52:56 +08:00
parent eeb97a4299
commit 7016690f99
10 changed files with 247 additions and 230 deletions
+1
View File
@@ -277,6 +277,7 @@
#define spatial_radius(obj) sfall_func1("spatial_radius", obj) #define spatial_radius(obj) sfall_func1("spatial_radius", obj)
#define string_compare(str1, str2) sfall_func2("string_compare", str1, str2) #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_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 tile_refresh_display sfall_func0("tile_refresh_display")
#define unjam_lock(obj) sfall_func1("unjam_lock", obj) #define unjam_lock(obj) sfall_func1("unjam_lock", obj)
#define unset_unique_id(obj) sfall_func2("set_unique_id", obj, -1) #define unset_unique_id(obj) sfall_func2("set_unique_id", obj, -1)
+5 -1
View File
@@ -248,7 +248,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)) - 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) > 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 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 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 - 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
@@ -588,6 +588,10 @@ optional arguments:
- codePage: code page number to properly compare national characters in the range 128-255 of the ASCII code table - codePage: code page number to properly compare national characters in the range 128-255 of the ASCII code table
available encodings: 1250-1252, 866 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 ------- ------ MORE INFO -------
------------------------ ------------------------
+2 -3
View File
@@ -979,11 +979,10 @@ DWORD __stdcall InterpretAddString(TProgram* scriptPtr, const char* strval) {
} }
} }
const char* __stdcall InterpretGetString(TProgram* scriptPtr, DWORD strId, DWORD dataType) { const char* __fastcall InterpretGetString(TProgram* scriptPtr, DWORD dataType, DWORD strId) {
__asm { __asm {
mov edx, dataType;
mov ebx, strId; mov ebx, strId;
mov eax, scriptPtr; mov eax, ecx;
call interpretGetString_; call interpretGetString_;
} }
} }
+1 -1
View File
@@ -1084,7 +1084,7 @@ void __stdcall InterpretPushLong(TProgram* scriptPtr, DWORD val);
// pushes value type to Data stack (must be preceded by InterpretPushLong) // pushes value type to Data stack (must be preceded by InterpretPushLong)
void __stdcall InterpretPushShort(TProgram* scriptPtr, DWORD valType); void __stdcall InterpretPushShort(TProgram* scriptPtr, DWORD valType);
const char* __stdcall InterpretGetString(TProgram* scriptPtr, DWORD strId, DWORD dataType); const char* __fastcall InterpretGetString(TProgram* scriptPtr, DWORD dataType, DWORD strId);
DWORD __stdcall InterpretAddString(TProgram* scriptPtr, const char* str); DWORD __stdcall InterpretAddString(TProgram* scriptPtr, const char* str);
+1 -1
View File
@@ -337,7 +337,7 @@ public:
// retrieve string argument // retrieve string argument
if (type == DATATYPE_STR) { if (type == DATATYPE_STR) {
_args[i] = InterpretGetString(program, rawValue, rawValueType); _args[i] = InterpretGetString(program, rawValueType, rawValue);
} else { } else {
_args[i] = ScriptValue(type, rawValue); _args[i] = ScriptValue(type, rawValue);
} }
+8
View File
@@ -42,6 +42,7 @@
Gets argument from stack to eax and puts its type to edx register Gets argument from stack to eax and puts its type to edx register
eax register must contain the script_ptr eax register must contain the script_ptr
jlabel - name of the jump label in case the value type is not INT jlabel - name of the jump label in case the value type is not INT
return: eax - arg value
*/ */
#define _GET_ARG_INT(jlabel) __asm { \ #define _GET_ARG_INT(jlabel) __asm { \
__asm mov edx, eax \ __asm mov edx, eax \
@@ -131,6 +132,13 @@ notstring##num:
__asm call interpretPushShort_ \ __asm call interpretPushShort_ \
} }
#define _J_RET_VAL_TYPE(type) __asm { \
__asm call interpretPushLong_ \
__asm mov edx, type \
__asm mov eax, ebx \
__asm jmp interpretPushShort_ \
}
/* /*
handle return value which may be of any type handle return value which may be of any type
num - any unique number num - any unique number
+151 -218
View File
@@ -1,6 +1,6 @@
/* /*
* sfall * sfall
* Copyright (C) 2008, 2009, 2010, 2012 The sfall team * Copyright (C) 2008-2016 The sfall team
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -19,303 +19,236 @@
#pragma once #pragma once
#include "main.h" #include "main.h"
#include "ScriptExtender.h" //#include "ScriptExtender.h"
#define START_VALID_ADDR 0x410000
#define END_VALID_ADDR 0x6B403F
// memory_reading_funcs // memory_reading_funcs
static void __declspec(naked) ReadByte() { static void __declspec(naked) ReadByte() {
__asm { __asm {
push ebx; _GET_ARG_INT(error);
push ecx; test eax, eax;
push edx; jz error;
mov ecx, eax; movzx edx, byte ptr ds:[eax]; // read memory
call interpretPopShort_;
mov edx, eax;
mov eax, ecx;
call interpretPopLong_;
cmp dx, VAR_TYPE_INT;
jnz error;
movzx edx, byte ptr ds:[eax];
jmp result;
error:
mov edx, 0;
result: result:
mov eax, ecx; mov eax, ebx;
call interpretPushLong_; _J_RET_VAL_TYPE(VAR_TYPE_INT);
mov edx, VAR_TYPE_INT; // retn;
mov eax, ecx; error:
call interpretPushShort_; xor edx, edx;
pop edx; jmp result;
pop ecx;
pop ebx;
retn;
} }
} }
static void __declspec(naked) ReadShort() { static void __declspec(naked) ReadShort() {
__asm { __asm {
push ebx; _GET_ARG_INT(error);
push ecx; test eax, eax;
push edx; jz error;
mov ecx, eax; movzx edx, word ptr ds:[eax]; // read memory
call interpretPopShort_;
mov edx, eax;
mov eax, ecx;
call interpretPopLong_;
cmp dx, VAR_TYPE_INT;
jnz error;
movzx edx, word ptr ds:[eax];
jmp result;
error:
mov edx, 0;
result: result:
mov eax, ecx; mov eax, ebx;
call interpretPushLong_; _J_RET_VAL_TYPE(VAR_TYPE_INT);
mov edx, VAR_TYPE_INT; // retn;
mov eax, ecx; error:
call interpretPushShort_; xor edx, edx;
pop edx; jmp result;
pop ecx;
pop ebx;
retn;
} }
} }
static void __declspec(naked) ReadInt() { static void __declspec(naked) ReadInt() {
__asm { __asm {
push ebx; _GET_ARG_INT(error);
push ecx; test eax, eax;
push edx; jz error;
mov ecx, eax; mov edx, dword ptr ds:[eax]; // read memory
call interpretPopShort_;
mov edx, eax;
mov eax, ecx;
call interpretPopLong_;
cmp dx, VAR_TYPE_INT;
jnz error;
mov edx, dword ptr ds:[eax];
jmp result;
error:
mov edx, 0;
result: result:
mov eax, ecx; mov eax, ebx;
call interpretPushLong_; _J_RET_VAL_TYPE(VAR_TYPE_INT);
mov edx, VAR_TYPE_INT; // retn;
mov eax, ecx; error:
call interpretPushShort_; xor edx, edx;
pop edx; jmp result;
pop ecx;
pop ebx;
retn;
} }
} }
static void __declspec(naked) ReadString() { static void __declspec(naked) ReadString() {
__asm { __asm {
push ebx; _GET_ARG_INT(error);
push ecx; test eax, eax;
push edx; jz error;
mov ecx, eax; mov edx, eax;
call interpretPopShort_;
mov edx, eax;
mov eax, ecx;
call interpretPopLong_;
cmp dx, VAR_TYPE_INT;
jnz error;
mov edx, eax;
jmp result;
error:
mov edx, 0;
result: result:
mov eax, ecx; mov eax, ebx;
call interpretPushLong_; _J_RET_VAL_TYPE(VAR_TYPE_STR);
mov edx, VAR_TYPE_STR; // retn;
mov eax, ecx; error:
call interpretPushShort_; xor edx, edx;
pop edx; jmp result;
pop ecx;
pop ebx;
retn;
} }
} }
static void __declspec(naked) WriteByte() { static void __declspec(naked) WriteByte() {
__asm { __asm {
pushad push ecx;
mov ecx, eax;
call interpretPopShort_; call interpretPopShort_;
mov esi, eax; mov ecx, eax; // type
mov eax, ecx; mov eax, ebx;
call interpretPopLong_; call interpretPopLong_;
mov edx, eax; mov esi, eax; // write value
mov eax, ecx; mov eax, ebx;
call interpretPopShort_; _GET_ARG_INT(end);
mov edi, eax; cmp cx, VAR_TYPE_INT;
mov eax, ecx; jnz end;
call interpretPopLong_; // check valid addr
cmp di, VAR_TYPE_INT; cmp eax, START_VALID_ADDR;
jnz end; jb end;
cmp si, VAR_TYPE_INT; cmp eax, END_VALID_ADDR;
jnz end; ja end;
//mov byte ptr ds:[eax], dl; and esi, 0xFF;
and edx, 0xff; push esi;
push edx;
push eax; push eax;
call SafeWrite8; call SafeWrite8;
end: end:
popad; pop ecx;
retn; retn;
} }
} }
static void __declspec(naked) WriteShort() { static void __declspec(naked) WriteShort() {
__asm { __asm {
pushad; push ecx;
mov ecx, eax;
call interpretPopShort_; call interpretPopShort_;
mov esi, eax; mov ecx, eax; // type
mov eax, ecx; mov eax, ebx;
call interpretPopLong_; call interpretPopLong_;
mov edx, eax; mov esi, eax; // write value
mov eax, ecx; mov eax, ebx;
call interpretPopShort_; _GET_ARG_INT(end);
mov edi, eax; cmp cx, VAR_TYPE_INT;
mov eax, ecx; jnz end;
call interpretPopLong_; // check valid addr
cmp di, VAR_TYPE_INT; cmp eax, START_VALID_ADDR;
jnz end; jb end;
cmp si, VAR_TYPE_INT; cmp eax, END_VALID_ADDR;
jnz end; ja end;
//mov word ptr ds:[eax], dx; and esi, 0xFFFF;
and edx, 0xffff; push esi;
push edx;
push eax; push eax;
call SafeWrite16; call SafeWrite16;
end: end:
popad; pop ecx;
retn; retn;
} }
} }
static void __declspec(naked) WriteInt() { static void __declspec(naked) WriteInt() {
__asm { __asm {
pushad push ecx;
mov ecx, eax;
call interpretPopShort_; call interpretPopShort_;
mov esi, eax; mov ecx, eax; // type
mov eax, ecx; mov eax, ebx;
call interpretPopLong_; call interpretPopLong_;
mov edx, eax; mov esi, eax; // write value
mov eax, ecx; mov eax, ebx;
call interpretPopShort_; _GET_ARG_INT(end);
mov edi, eax; cmp cx, VAR_TYPE_INT;
mov eax, ecx; jnz end;
call interpretPopLong_; // check valid addr
cmp di, VAR_TYPE_INT; cmp eax, START_VALID_ADDR;
jnz end; jb end;
cmp si, VAR_TYPE_INT; cmp eax, END_VALID_ADDR;
jnz end; ja end;
//mov dword ptr ds:[eax], edx; push esi;
push edx;
push eax; push eax;
call SafeWrite32; call SafeWrite32;
end: end:
popad pop ecx;
retn; retn;
} }
} }
static void _stdcall WriteStringInternal(const char* str, char* addr) {
bool hitnull = false; static void __fastcall WriteStringInternal(char* addr, long type, long strID, TProgram* script) {
const char* str = InterpretGetString(script, type, strID);
while (*str) { while (*str) {
if (!*addr) hitnull = true; if (!addr[0] && addr[1]) break; // addr[1] as *(addr + 1)
if (hitnull && addr[1]) break; *addr++ = *str++;
*addr = *str;
addr++;
str++;
} }
*addr = 0; *addr = 0;
} }
static void __declspec(naked) WriteString() { static void __declspec(naked) WriteString() {
__asm { __asm {
pushad; push ecx;
mov ecx, eax;
call interpretPopShort_; call interpretPopShort_;
mov esi, eax; mov ecx, eax; // type
mov eax, ecx; mov eax, ebx;
call interpretPopLong_; call interpretPopLong_;
mov edi, eax; mov esi, eax; // str value
mov eax, ecx; mov eax, ebx;
call interpretPopShort_; _GET_ARG_INT(end);
mov edx, eax; cmp cx, VAR_TYPE_STR2;
mov eax, ecx; je next;
call interpretPopLong_; cmp cx, VAR_TYPE_STR;
cmp dx, VAR_TYPE_INT; jnz end;
jnz end;
cmp si, VAR_TYPE_STR2;
jz next;
cmp si, VAR_TYPE_STR;
jnz end;
next: next:
mov ebx, edi; // ecx - type, esi - value
mov edx, esi; // edx - type, eax - addr
mov esi, eax; // check valid address
mov eax, ecx; cmp eax, START_VALID_ADDR;
call interpretGetString_; jb end;
push esi; cmp eax, END_VALID_ADDR;
push eax; ja end;
push ebx; // script
push esi; // str value
mov edx, ecx; // type
mov ecx, eax; // addr
call WriteStringInternal; call WriteStringInternal;
jmp end;
end: end:
popad; pop ecx;
retn; retn;
} }
} }
static void _stdcall CallOffsetInternal(DWORD func, DWORD script) {
func = (func >> 2) - 0x1d2;
bool ret = func >= 5;
int argcount = func % 5;
DWORD args[5];
DWORD illegalarg = 0;
for (int i = argcount * 4; i >= 0; i -= 4) {
__asm {
mov eax, script;
call interpretPopShort_;
cmp ax, VAR_TYPE_INT;
jz legal;
inc illegalarg;
legal:
mov eax, script;
call interpretPopLong_;
lea ecx, args;
add ecx, i;
mov [ecx], eax;
}
}
if (illegalarg) { static void __fastcall CallOffsetInternal(TProgram* script, DWORD func) {
func = (func >> 2) - 0x1d2;
DWORD args[5];
DWORD illegalArg = 0;
int argCount = func % 5;
for (int i = argCount; i >= 0; i--) {
if ((short)InterpretPopShort(script) != (short)VAR_TYPE_INT) illegalArg++;
args[i] = InterpretPopLong(script);
}
if (illegalArg || args[0] < 0x410010 || args[0] > 0x4FCE34) {
args[0] = 0; args[0] = 0;
} else { } else {
__asm { __asm {
mov eax, args[4]; // args[1] mov eax, args[4];
mov edx, args[8]; // args[2] mov edx, args[8];
mov ebx, args[12]; // args[3] mov ebx, args[12];
mov ecx, args[16]; // args[4] mov ecx, args[16];
mov edi, args[0]; // args[0] call args[0];
call edi; mov args[0], eax;
mov args[0], eax;
} }
} }
if (ret) { if (func >= 5) { // has return
__asm { __asm {
mov eax, script; mov eax, script;
mov edx, args[0]; mov edx, args[0];
call interpretPushLong_; mov ebx, eax;
mov eax, script; _RET_VAL_INT2;
mov edx, VAR_TYPE_INT;
call interpretPushShort_;
} }
} }
} }
static void __declspec(naked) CallOffset() { static void __declspec(naked) CallOffset() {
__asm { __asm {
pushad; push ecx;
push eax; mov ecx, eax;
push edx; call CallOffsetInternal; // edx - func
call CallOffsetInternal; pop ecx;
popad;
retn; retn;
} }
} }
+1
View File
@@ -169,6 +169,7 @@ static const SfallMetarule metaruleArray[] = {
{"show_window", sf_show_window, 0, 1}, {"show_window", sf_show_window, 0, 1},
{"spatial_radius", sf_spatial_radius, 1, 1}, {"spatial_radius", sf_spatial_radius, 1, 1},
{"string_compare", sf_string_compare, 2, 3}, {"string_compare", sf_string_compare, 2, 3},
{"string_format", sf_string_format, 2, 5},
{"tile_refresh_display", sf_tile_refresh_display, 0, 0}, {"tile_refresh_display", sf_tile_refresh_display, 0, 0},
{"unjam_lock", sf_unjam_lock, 1, 1}, {"unjam_lock", sf_unjam_lock, 1, 1},
{"unwield_slot", sf_unwield_slot, 2, 2}, {"unwield_slot", sf_unwield_slot, 2, 2},
+71 -1
View File
@@ -591,7 +591,7 @@ char* _stdcall Substring(const char* str, int startPos, int length) {
if (length < 0) { if (length < 0) {
length += len - startPos; // cutoff at end length += len - startPos; // cutoff at end
if (length == 0) return ""; if (length == 0) return "";
abs(length); // length can't be negative length = abs(length); // length can't be negative
} }
// check position // check position
if (startPos >= len) return ""; // start position is out of string length, return empty string if (startPos >= len) return ""; // start position is out of string length, return empty string
@@ -782,6 +782,76 @@ fail:
} }
} }
static void sf_string_format() {
const ScriptValue &fmtArg = opHandler.arg(0);
if (!fmtArg.isString()) {
OpcodeInvalidArgs("string_format");
opHandler.setReturn(0);
return;
}
const char* format = fmtArg.strValue();
int fmtLen = strlen(format);
if (fmtLen == 0) {
opHandler.setReturn(format);
return;
}
if (fmtLen > 1024) {
opHandler.printOpcodeError("string_format() - the format string exceeds maximum length of 1024 characters.");
opHandler.setReturn("Error");
} else {
char* newFmt = new char[fmtLen + 1];
newFmt[fmtLen] = '\0';
// parse format to make it safe
int i = 0, arg = 0, totalArg = opHandler.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 && !opHandler.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 = GlblTextBufferSize() - 1;
switch (totalArg) {
case 2 :
_snprintf(gTextBuffer, bufMaxLen, newFmt, opHandler.arg(1).rawValue());
break;
case 3 :
_snprintf(gTextBuffer, bufMaxLen, newFmt, opHandler.arg(1).rawValue(), opHandler.arg(2).rawValue());
break;
case 4 :
_snprintf(gTextBuffer, bufMaxLen, newFmt, opHandler.arg(1).rawValue(), opHandler.arg(2).rawValue(), opHandler.arg(3).rawValue());
break;
case 5 :
_snprintf(gTextBuffer, bufMaxLen, newFmt, opHandler.arg(1).rawValue(), opHandler.arg(2).rawValue(), opHandler.arg(3).rawValue(), opHandler.arg(4).rawValue());
}
gTextBuffer[bufMaxLen] = '\0'; // just in case
delete[] newFmt;
opHandler.setReturn(gTextBuffer);
}
}
static void funcPow2() { static void funcPow2() {
const ScriptValue &base = opHandler.arg(0), const ScriptValue &base = opHandler.arg(0),
&power = opHandler.arg(1); &power = opHandler.arg(1);
+1
View File
@@ -268,6 +268,7 @@ end:
retn; retn;
} }
} }
static void __declspec(naked) get_critter_skill_points() { static void __declspec(naked) get_critter_skill_points() {
__asm { __asm {
pushad; pushad;