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 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)
+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))
> 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
@@ -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
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 -------
------------------------
+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 {
mov edx, dataType;
mov ebx, strId;
mov eax, scriptPtr;
mov eax, ecx;
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)
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);
+1 -1
View File
@@ -337,7 +337,7 @@ public:
// retrieve string argument
if (type == DATATYPE_STR) {
_args[i] = InterpretGetString(program, rawValue, rawValueType);
_args[i] = InterpretGetString(program, rawValueType, rawValue);
} else {
_args[i] = ScriptValue(type, rawValue);
}
+13 -5
View File
@@ -5,7 +5,7 @@
Notes:
- DO NOT add any comments within macros
- every macro should contain __asm {} block
- every macro should contain __asm {} block
- every assembly line should start with __asm and should NOT have semicolon in the end!
- use this macros outside of other __asm {} blocks (obviously)
*/
@@ -42,6 +42,7 @@
Gets argument from stack to eax and puts its type to edx register
eax register must contain the script_ptr
jlabel - name of the jump label in case the value type is not INT
return: eax - arg value
*/
#define _GET_ARG_INT(jlabel) __asm { \
__asm mov edx, eax \
@@ -71,7 +72,7 @@
checks argument (which may be any type) if it's a string and retrieves it (overwrites value in rval)
num - any number, but it must be unique (used for label names)
r16type - 16bit register where value type is stored
rval - r32 where value is stored
rval - r32 where value is stored
*/
#define _CHECK_PARSE_STR(num, rscript, r16type, rval) __asm { \
__asm cmp r16type, VAR_TYPE_STR2 \
@@ -131,7 +132,14 @@ notstring##num:
__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
num - any unique number
type - register or other expression (like memory address) where value type is stored (usually [esp])
@@ -154,8 +162,8 @@ __asm resultnotstr##num: \
__asm call interpretPushShort_ \
}
/*
better way of handling new opcodes:
/*
better way of handling new opcodes:
- no ASM code required
- all type checks should be done in the wrapped C-function
- use opArgs, opArgTypes to access arguments
+151 -218
View File
@@ -1,6 +1,6 @@
/*
* 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
* it under the terms of the GNU General Public License as published by
@@ -19,303 +19,236 @@
#pragma once
#include "main.h"
#include "ScriptExtender.h"
//#include "ScriptExtender.h"
#define START_VALID_ADDR 0x410000
#define END_VALID_ADDR 0x6B403F
// memory_reading_funcs
static void __declspec(naked) ReadByte() {
__asm {
push ebx;
push ecx;
push edx;
mov ecx, eax;
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;
_GET_ARG_INT(error);
test eax, eax;
jz error;
movzx edx, byte ptr ds:[eax]; // read memory
result:
mov eax, ecx;
call interpretPushLong_;
mov edx, VAR_TYPE_INT;
mov eax, ecx;
call interpretPushShort_;
pop edx;
pop ecx;
pop ebx;
retn;
mov eax, ebx;
_J_RET_VAL_TYPE(VAR_TYPE_INT);
// retn;
error:
xor edx, edx;
jmp result;
}
}
static void __declspec(naked) ReadShort() {
__asm {
push ebx;
push ecx;
push edx;
mov ecx, eax;
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;
_GET_ARG_INT(error);
test eax, eax;
jz error;
movzx edx, word ptr ds:[eax]; // read memory
result:
mov eax, ecx;
call interpretPushLong_;
mov edx, VAR_TYPE_INT;
mov eax, ecx;
call interpretPushShort_;
pop edx;
pop ecx;
pop ebx;
retn;
mov eax, ebx;
_J_RET_VAL_TYPE(VAR_TYPE_INT);
// retn;
error:
xor edx, edx;
jmp result;
}
}
static void __declspec(naked) ReadInt() {
__asm {
push ebx;
push ecx;
push edx;
mov ecx, eax;
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;
_GET_ARG_INT(error);
test eax, eax;
jz error;
mov edx, dword ptr ds:[eax]; // read memory
result:
mov eax, ecx;
call interpretPushLong_;
mov edx, VAR_TYPE_INT;
mov eax, ecx;
call interpretPushShort_;
pop edx;
pop ecx;
pop ebx;
retn;
mov eax, ebx;
_J_RET_VAL_TYPE(VAR_TYPE_INT);
// retn;
error:
xor edx, edx;
jmp result;
}
}
static void __declspec(naked) ReadString() {
__asm {
push ebx;
push ecx;
push edx;
mov ecx, 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;
_GET_ARG_INT(error);
test eax, eax;
jz error;
mov edx, eax;
result:
mov eax, ecx;
call interpretPushLong_;
mov edx, VAR_TYPE_STR;
mov eax, ecx;
call interpretPushShort_;
pop edx;
pop ecx;
pop ebx;
retn;
mov eax, ebx;
_J_RET_VAL_TYPE(VAR_TYPE_STR);
// retn;
error:
xor edx, edx;
jmp result;
}
}
static void __declspec(naked) WriteByte() {
__asm {
pushad
mov ecx, eax;
push ecx;
call interpretPopShort_;
mov esi, eax;
mov eax, ecx;
mov ecx, eax; // type
mov eax, ebx;
call interpretPopLong_;
mov edx, eax;
mov eax, ecx;
call interpretPopShort_;
mov edi, eax;
mov eax, ecx;
call interpretPopLong_;
cmp di, VAR_TYPE_INT;
jnz end;
cmp si, VAR_TYPE_INT;
jnz end;
//mov byte ptr ds:[eax], dl;
and edx, 0xff;
push edx;
mov esi, eax; // write value
mov eax, ebx;
_GET_ARG_INT(end);
cmp cx, VAR_TYPE_INT;
jnz end;
// check valid addr
cmp eax, START_VALID_ADDR;
jb end;
cmp eax, END_VALID_ADDR;
ja end;
and esi, 0xFF;
push esi;
push eax;
call SafeWrite8;
end:
popad;
pop ecx;
retn;
}
}
static void __declspec(naked) WriteShort() {
__asm {
pushad;
mov ecx, eax;
push ecx;
call interpretPopShort_;
mov esi, eax;
mov eax, ecx;
mov ecx, eax; // type
mov eax, ebx;
call interpretPopLong_;
mov edx, eax;
mov eax, ecx;
call interpretPopShort_;
mov edi, eax;
mov eax, ecx;
call interpretPopLong_;
cmp di, VAR_TYPE_INT;
jnz end;
cmp si, VAR_TYPE_INT;
jnz end;
//mov word ptr ds:[eax], dx;
and edx, 0xffff;
push edx;
mov esi, eax; // write value
mov eax, ebx;
_GET_ARG_INT(end);
cmp cx, VAR_TYPE_INT;
jnz end;
// check valid addr
cmp eax, START_VALID_ADDR;
jb end;
cmp eax, END_VALID_ADDR;
ja end;
and esi, 0xFFFF;
push esi;
push eax;
call SafeWrite16;
end:
popad;
pop ecx;
retn;
}
}
static void __declspec(naked) WriteInt() {
__asm {
pushad
mov ecx, eax;
push ecx;
call interpretPopShort_;
mov esi, eax;
mov eax, ecx;
mov ecx, eax; // type
mov eax, ebx;
call interpretPopLong_;
mov edx, eax;
mov eax, ecx;
call interpretPopShort_;
mov edi, eax;
mov eax, ecx;
call interpretPopLong_;
cmp di, VAR_TYPE_INT;
jnz end;
cmp si, VAR_TYPE_INT;
jnz end;
//mov dword ptr ds:[eax], edx;
push edx;
mov esi, eax; // write value
mov eax, ebx;
_GET_ARG_INT(end);
cmp cx, VAR_TYPE_INT;
jnz end;
// check valid addr
cmp eax, START_VALID_ADDR;
jb end;
cmp eax, END_VALID_ADDR;
ja end;
push esi;
push eax;
call SafeWrite32;
end:
popad
pop ecx;
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) {
if (!*addr) hitnull = true;
if (hitnull && addr[1]) break;
*addr = *str;
addr++;
str++;
if (!addr[0] && addr[1]) break; // addr[1] as *(addr + 1)
*addr++ = *str++;
}
*addr = 0;
}
static void __declspec(naked) WriteString() {
__asm {
pushad;
mov ecx, eax;
push ecx;
call interpretPopShort_;
mov esi, eax;
mov eax, ecx;
mov ecx, eax; // type
mov eax, ebx;
call interpretPopLong_;
mov edi, eax;
mov eax, ecx;
call interpretPopShort_;
mov edx, eax;
mov eax, ecx;
call interpretPopLong_;
cmp dx, VAR_TYPE_INT;
jnz end;
cmp si, VAR_TYPE_STR2;
jz next;
cmp si, VAR_TYPE_STR;
jnz end;
mov esi, eax; // str value
mov eax, ebx;
_GET_ARG_INT(end);
cmp cx, VAR_TYPE_STR2;
je next;
cmp cx, VAR_TYPE_STR;
jnz end;
next:
mov ebx, edi;
mov edx, esi;
mov esi, eax;
mov eax, ecx;
call interpretGetString_;
push esi;
push eax;
// ecx - type, esi - value
// edx - type, eax - addr
// check valid address
cmp eax, START_VALID_ADDR;
jb end;
cmp eax, END_VALID_ADDR;
ja end;
push ebx; // script
push esi; // str value
mov edx, ecx; // type
mov ecx, eax; // addr
call WriteStringInternal;
jmp end;
end:
popad;
pop ecx;
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;
} else {
__asm {
mov eax, args[4]; // args[1]
mov edx, args[8]; // args[2]
mov ebx, args[12]; // args[3]
mov ecx, args[16]; // args[4]
mov edi, args[0]; // args[0]
call edi;
mov args[0], eax;
mov eax, args[4];
mov edx, args[8];
mov ebx, args[12];
mov ecx, args[16];
call args[0];
mov args[0], eax;
}
}
if (ret) {
if (func >= 5) { // has return
__asm {
mov eax, script;
mov edx, args[0];
call interpretPushLong_;
mov eax, script;
mov edx, VAR_TYPE_INT;
call interpretPushShort_;
mov ebx, eax;
_RET_VAL_INT2;
}
}
}
static void __declspec(naked) CallOffset() {
__asm {
pushad;
push eax;
push edx;
call CallOffsetInternal;
popad;
push ecx;
mov ecx, eax;
call CallOffsetInternal; // edx - func
pop ecx;
retn;
}
}
+1
View File
@@ -169,6 +169,7 @@ static const SfallMetarule metaruleArray[] = {
{"show_window", sf_show_window, 0, 1},
{"spatial_radius", sf_spatial_radius, 1, 1},
{"string_compare", sf_string_compare, 2, 3},
{"string_format", sf_string_format, 2, 5},
{"tile_refresh_display", sf_tile_refresh_display, 0, 0},
{"unjam_lock", sf_unjam_lock, 1, 1},
{"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) {
length += len - startPos; // cutoff at end
if (length == 0) return "";
abs(length); // length can't be negative
length = abs(length); // length can't be negative
}
// check position
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() {
const ScriptValue &base = opHandler.arg(0),
&power = opHandler.arg(1);
+1
View File
@@ -268,6 +268,7 @@ end:
retn;
}
}
static void __declspec(naked) get_critter_skill_points() {
__asm {
pushad;