Added "get_text_width" and "string_compare" script functions

Fixed the return value of charcode script function for characters in
the extended ASCII character set.

Rewrote set_dm/df_model, set_movie_path functions in C++.
This commit is contained in:
NovaRain
2019-11-23 20:18:06 +08:00
parent 0c42f6447d
commit 977e6a5ad7
12 changed files with 155 additions and 172 deletions
+4 -1
View File
@@ -280,6 +280,7 @@
#define get_outline(obj) sfall_func1("get_outline", obj)
#define get_sfall_arg_at(argNum) sfall_func1("get_sfall_arg_at", argNum)
#define get_string_pointer(text) sfall_func1("get_string_pointer", text)
#define get_text_width(text) sfall_func1("get_text_width", text)
#define has_fake_perk_npc(npc, perk) sfall_func2("has_fake_perk_npc", npc, perk)
#define has_fake_trait_npc(npc, trait) sfall_func2("has_fake_trait_npc", npc, trait)
#define hide_window(winName) sfall_func1("hide_window", winName)
@@ -313,13 +314,15 @@
#define set_rest_heal_time(time) sfall_func1("set_rest_heal_time", time)
#define set_rest_mode(mode) sfall_func1("set_rest_mode", mode)
#define set_unique_id(obj) sfall_func1("set_unique_id", obj)
#define unset_unique_id(obj) sfall_func2("set_unique_id", obj, -1)
#define set_unjam_locks_time(time) sfall_func1("set_unjam_locks_time", time)
#define set_window_flag(winID, flag, value) sfall_func3("set_window_flag", winID, flag, value)
#define show_window(winName) sfall_func1("show_window", winName)
#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 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)
#define unwield_slot(critter, slot) sfall_func2("unwield_slot", critter, slot)
#define set_fake_perk_npc(npc, perk, level, image, desc) sfall_func5("set_fake_perk_npc", npc, perk, level, image, desc)
+11 -2
View File
@@ -601,8 +601,8 @@ Some utility/math functions are available:
> void sfall_func5("draw_image", string/int pathFile/artId, int frame, int x, int y, bool noTransparent)
> void sfall_func6("draw_image_scaled", string/int pathFile/artId, int frame, int x, int y, int width, int height)
- displays the specified FRM image in the active window created by vanilla CreateWin or sfall's create_win script function
- pathFile/artId: path to the FRM file (e.g. "art\\inven\\5mmap.frm"), or its FRM ID number (e.g. 117440550, see specification of the FID format)
- displays the specified PCX or FRM image in the active window created by vanilla CreateWin or sfall's create_win script function
- pathFile/artId: path to the PCX/FRM file (e.g. "art\\inven\\5mmap.frm"), or its FRM ID number (e.g. 117440550, see specification of the FID format)
optional arguments:
- frame: frame number, the first frame starts from zero
- x/y: offset relative to the top-left corner of the window
@@ -669,6 +669,15 @@ optional argument:
- flag: the flag to change (see WIN_FLAG_* constants in define_extra.h)
- value: true - set the flag, false - unset the flag
> int sfall_func1("get_text_width", string text)
- returns the text width in pixels for the currently set font
> bool sfall_func2("string_compare", string str1, string str2)
> bool sfall_func3("string_compare", string str1, string str2, int codePage)
- compares two strings case-insensitive, and returns True if the two strings are matched
- codePage: code page number to properly compare national characters in the range 128-255 of the ASCII code table
available encodings: 1250-1252, 866
------------------------
------ MORE INFO -------
------------------------
+1 -4
View File
@@ -211,14 +211,11 @@ DWORD GetTextHeight() {
//---------------------------------------------------------
//gets the length of a string using the currently selected font
DWORD GetTextWidth(char *TextMsg) {
DWORD TxtWidth;
DWORD GetTextWidth(const char *TextMsg) {
__asm {
mov eax, TextMsg;
call dword ptr ds:[FO_VAR_text_width]; //get text width
mov TxtWidth, eax;
}
return TxtWidth;
}
//---------------------------------------------------------
+1 -1
View File
@@ -81,7 +81,7 @@ void PrintText(char *displayText, BYTE colorIndex, DWORD x, DWORD y, DWORD textW
// gets the height of the currently selected font
DWORD GetTextHeight();
// gets the length of a string using the currently selected font
DWORD GetTextWidth(char *textMsg);
DWORD GetTextWidth(const char *textMsg);
// get width of Char for current font
DWORD GetCharWidth(char charVal);
// get maximum string length for current font - if all characters were maximum width
@@ -90,6 +90,7 @@ static const SfallMetarule metarules[] = {
{"get_outline", sf_get_outline, 1, 1, {ARG_OBJECT}},
{"get_sfall_arg_at", sf_get_sfall_arg_at, 1, 1, {ARG_INT}},
{"get_string_pointer", sf_get_string_pointer, 1, 1, {ARG_STRING}},
{"get_text_width", sf_get_text_width, 1, 1},
{"has_fake_perk_npc", sf_has_fake_perk_npc, 2, 2, {ARG_OBJECT, ARG_STRING}},
{"has_fake_trait_npc", sf_has_fake_trait_npc, 2, 2, {ARG_OBJECT, ARG_STRING}},
{"hide_window", sf_hide_window, 0, 1, {ARG_STRING}},
@@ -129,6 +130,7 @@ static const SfallMetarule metarules[] = {
{"set_window_flag", sf_set_window_flag, 3, 3, {ARG_INTSTR, ARG_INT, ARG_INT}},
{"show_window", sf_show_window, 0, 1, {ARG_STRING}},
{"spatial_radius", sf_spatial_radius, 1, 1, {ARG_OBJECT}},
{"string_compare", sf_string_compare, 2, 3, {ARG_STRING, ARG_STRING, ARG_INT}},
{"tile_refresh_display", sf_tile_refresh_display, 0, 0},
{"unjam_lock", sf_unjam_lock, 1, 1, {ARG_OBJECT}},
{"unwield_slot", sf_unwield_slot, 2, 2, {ARG_OBJECT, ARG_INT}},
@@ -136,7 +138,7 @@ static const SfallMetarule metarules[] = {
{"validate_test", sf_test, 2, 5, {ARG_INT, ARG_NUMBER, ARG_STRING, ARG_OBJECT, ARG_ANY}},
#endif
};
//
// returns current contents of metarule table
static void sf_get_metarule_table(OpcodeContext& ctx) {
DWORD arrId = TempArray(metaruleTable.size(), 0);
+23 -132
View File
@@ -42,129 +42,35 @@ namespace sfall
namespace script
{
static DWORD defaultMaleModelNamePtr = (DWORD)defaultMaleModelName;
static DWORD defaultFemaleModelNamePtr = (DWORD)defaultFemaleModelName;
static DWORD movieNamesPtr = (DWORD)MoviePaths;
const char* stringTooLong = "%s() - the string length exceeds maximum of 64 characters.";
//// *** End Helios *** ///
static void _stdcall strcpy_p(char* to, const char* from) {
strcpy_s(to, 64, from);
void sf_set_dm_model(OpcodeContext& ctx) {
auto model = ctx.arg(0).strValue();
if (strlen(model) > 64) {
ctx.printOpcodeError(stringTooLong, ctx.getOpcodeName());
return;
}
strcpy(defaultMaleModelName, model);
}
/************************************************************************/
/* TODO: Rewrite these raw handlers using OpcodeContext */
/************************************************************************/
void __declspec(naked) op_set_dm_model() {
__asm {
push ebx;
push ecx;
push edx;
push edi;
mov edi, eax;
call fo::funcoffs::interpretPopShort_;
mov edx, eax;
mov eax, edi;
call fo::funcoffs::interpretPopLong_;
cmp dx, VAR_TYPE_STR2;
jz next;
cmp dx, VAR_TYPE_STR;
jnz end;
next:
mov ebx, eax;
mov eax, edi;
call fo::funcoffs::interpretGetString_;
push eax;
push defaultMaleModelNamePtr;
call strcpy_p;
end:
pop edi;
pop edx;
pop ecx;
pop ebx;
retn;
void sf_set_df_model(OpcodeContext& ctx) {
auto model = ctx.arg(0).strValue();
if (strlen(model) > 64) {
ctx.printOpcodeError(stringTooLong, ctx.getOpcodeName());
return;
}
strcpy(defaultFemaleModelName, model);
}
void __declspec(naked) op_set_df_model() {
__asm {
push ebx;
push ecx;
push edx;
push edi;
mov edi, eax;
call fo::funcoffs::interpretPopShort_;
mov edx, eax;
mov eax, edi;
call fo::funcoffs::interpretPopLong_;
cmp dx, VAR_TYPE_STR2;
jz next;
cmp dx, VAR_TYPE_STR;
jnz end;
next:
mov ebx, eax;
mov eax, edi;
call fo::funcoffs::interpretGetString_;
push eax;
push defaultFemaleModelNamePtr;
call strcpy_p;
end:
pop edi;
pop edx;
pop ecx;
pop ebx;
retn;
}
}
void __declspec(naked) op_set_movie_path() {
__asm {
push ebx;
push ecx;
push edx;
push edi;
push esi;
mov edi, eax;
call fo::funcoffs::interpretPopShort_;
mov ebx, eax;
mov eax, edi;
call fo::funcoffs::interpretPopLong_;
mov esi, eax;
mov eax, edi;
call fo::funcoffs::interpretPopShort_;
mov edx, eax;
mov eax, edi;
call fo::funcoffs::interpretPopLong_;
cmp dx, VAR_TYPE_STR2;
jz next;
cmp dx, VAR_TYPE_STR;
jnz end;
next:
cmp bx, VAR_TYPE_INT;
jnz end;
cmp esi, 0;
jl end;
cmp esi, MaxMovies;
jge end;
mov ebx, eax;
mov eax, edi;
call fo::funcoffs::interpretGetString_;
push eax;
mov eax, esi;
mov esi, 65;
mul si;
add eax, movieNamesPtr;
push eax;
call strcpy_p;
end:
pop esi;
pop edi;
pop edx;
pop ecx;
pop ebx;
retn;
void sf_set_movie_path(OpcodeContext& ctx) {
long movieID = ctx.arg(1).rawValue();
if (movieID < 0 || movieID >= MaxMovies) return;
auto fileName = ctx.arg(0).strValue();
if (strlen(fileName) > 64) {
ctx.printOpcodeError(stringTooLong, ctx.getOpcodeName());
return;
}
strcpy(&MoviePaths[movieID * 65], fileName);
}
void sf_get_year(OpcodeContext& ctx) {
@@ -181,14 +87,12 @@ void sf_get_year(OpcodeContext& ctx) {
void __declspec(naked) op_game_loaded() {
__asm {
push ecx;
push edx;
push eax;
push eax; // script
call ScriptHasLoaded;
movzx edx, al;
pop eax;
_RET_VAL_INT(ecx);
pop edx;
pop ecx;
retn;
}
@@ -197,15 +101,13 @@ void __declspec(naked) op_game_loaded() {
void __declspec(naked) op_set_pipboy_available() {
__asm {
push ecx;
push edx;
_GET_ARG_INT(end);
cmp eax, 0;
jl end;
cmp eax, 1;
jg end;
mov byte ptr ds:[FO_VAR_gmovie_played_list + 0x3], al;
mov byte ptr ds:[FO_VAR_gmovie_played_list][0x3], al;
end:
pop edx;
pop ecx;
retn;
}
@@ -795,17 +697,6 @@ end:
//numbers subgame functions
void __declspec(naked) op_nb_create_char() {
__asm {
/*pushad;
push eax;
call NumbersCreateChar;
mov edx, eax;
pop eax;
mov ecx, eax;
call fo::funcoffs::interpretPushLong_;
mov eax, ecx;
mov edx, VAR_TYPE_INT;
call fo::funcoffs::interpretPushShort_;
popad;*/
retn;
}
}
+3 -5
View File
@@ -29,13 +29,11 @@ namespace script
class OpcodeContext;
// TODO: rewrite all op_* functions using OpcodeContext
void sf_set_dm_model(OpcodeContext&);
void __declspec() op_set_dm_model();
void sf_set_df_model(OpcodeContext&);
void __declspec() op_set_df_model();
void __declspec() op_set_movie_path();
void sf_set_movie_path(OpcodeContext&);
void sf_get_year(OpcodeContext&);
+88 -10
View File
@@ -19,6 +19,7 @@
#include <cmath>
#include "..\..\..\FalloutEngine\Fallout2.h"
#include "..\..\..\FalloutEngine\EngineUtils.h"
#include "..\..\ScriptExtender.h"
#include "..\..\FileSystem.h"
#include "..\..\Message.h"
@@ -33,6 +34,69 @@ namespace sfall
namespace script
{
// compares strings case-insensitive with specifics for Fallout
static bool FalloutStringCompare(const char* str1, const char* str2, long codePage) {
while (true) {
unsigned char c1 = *str1;
unsigned char c2 = *str2;
if (c1 == 0 && c2 == 0) return true; // end - strings are equal
if (c1 == 0 || c2 == 0) return false; // strings are not equal
str1++;
str2++;
if (c1 == c2) continue;
if (codePage == 866) {
// replace Russian 'x' to English (Fallout specific)
if (c1 == 229) c1 -= 229 - 'x';
if (c2 == 229) c2 -= 229 - 'x';
}
// 0 - 127 (standard ASCII)
// upper to lower case
if (c1 >= 'A' && c1 <= 'Z') c1 |= 32;
if (c2 >= 'A' && c2 <= 'Z') c2 |= 32;
if (c1 == c2) continue;
if (c1 < 128 || c2 < 128) return false;
// 128 - 255 (international/extended)
switch (codePage) {
case 866:
if (c1 != 149 && c2 != 149) {
// upper to lower case
if (c1 >= 0x80 && c1 <= 0x9F) {
c1 |= 32;
} else if (c1 >= 224 && c1 <= 239) {
c1 -= 48; // shift lower range
} else if (c1 == 240) {
c1++;
}
if (c2 >= 0x80 && c2 <= 0x9F) {
c2 |= 32;
} else if (c2 >= 224 && c2 <= 239) {
c2 -= 48; // shift lower range
} else if (c2 == 240) {
c2++;
}
}
break;
case 1251:
// upper to lower case
if (c1 >= 0xC0 && c1 <= 0xDF) c1 |= 32;
if (c2 >= 0xC0 && c2 <= 0xDF) c2 |= 32;
if (c1 == 0xA8) c1 += 16;
if (c2 == 0xA8) c2 += 16;
break;
case 1250:
case 1252:
if (c1 != 0xD7 && c1 != 0xF7 && c2 != 0xD7 && c2 != 0xF7) {
if (c1 >= 0xC0 && c1 <= 0xDE) c1 |= 32;
if (c2 >= 0xC0 && c2 <= 0xDE) c2 |= 32;
}
break;
}
if (c1 != c2) return false; // strings are not equal
}
}
void sf_sqrt(OpcodeContext& ctx) {
ctx.setReturn(sqrt(ctx.arg(0).asFloat()));
}
@@ -68,22 +132,22 @@ void sf_strlen(OpcodeContext& ctx) {
}
void sf_atoi(OpcodeContext& ctx) {
auto str = ctx.arg(0).asString();
auto str = ctx.arg(0).strValue();
ctx.setReturn(
static_cast<int>(strtol(str, (char**)nullptr, 0)) // auto-determine radix
);
}
void sf_atof(OpcodeContext& ctx) {
auto str = ctx.arg(0).asString();
auto str = ctx.arg(0).strValue();
ctx.setReturn(
static_cast<float>(atof(str))
);
}
void sf_ord(OpcodeContext& ctx) {
char firstChar = ctx.arg(0).asString()[0];
ctx.setReturn(static_cast<int>(firstChar));
unsigned char firstChar = ctx.arg(0).strValue()[0];
ctx.setReturn(static_cast<unsigned long>(firstChar));
}
void sf_typeof(OpcodeContext& ctx) {
@@ -123,10 +187,10 @@ static int _stdcall StringSplit(const char* str, const char* split) {
}
void sf_string_split(OpcodeContext& ctx) {
ctx.setReturn(StringSplit(ctx.arg(0).asString(), ctx.arg(1).asString()));
ctx.setReturn(StringSplit(ctx.arg(0).strValue(), ctx.arg(1).strValue()));
}
char* _stdcall Substring(const char* str, int pos, int length) {
char* Substring(const char* str, int pos, int length) {
char* newstr;
int srclen;
srclen = strlen(str);
@@ -138,7 +202,7 @@ char* _stdcall Substring(const char* str, int pos, int length) {
length = 0;
else if (length + pos > srclen)
length = srclen - pos;
newstr = new char[length + 1];
newstr = new char[length + 1]; // memory leak!!!
if (length > 0)
memcpy(newstr, &str[pos], length);
newstr[length] = '\0';
@@ -147,10 +211,20 @@ char* _stdcall Substring(const char* str, int pos, int length) {
void sf_substr(OpcodeContext& ctx) {
ctx.setReturn(
Substring(ctx.arg(0).asString(), ctx.arg(1).asInt(), ctx.arg(2).asInt())
Substring(ctx.arg(0).strValue(), ctx.arg(1).rawValue(), ctx.arg(2).rawValue())
);
}
void sf_string_compare(OpcodeContext& ctx) {
if (ctx.numArgs() < 3) {
ctx.setReturn(
(_stricmp(ctx.arg(0).strValue(), ctx.arg(1).strValue()) ? 0 : 1)
);
} else {
ctx.setReturn(FalloutStringCompare(ctx.arg(0).strValue(), ctx.arg(1).strValue(), ctx.arg(2).rawValue()));
}
}
static char* sprintfbuf = nullptr;
// A safer version of sprintf for using in user scripts.
static char* _stdcall sprintf_lite(const char* format, ScriptValue value) {
@@ -232,7 +306,7 @@ static char* _stdcall sprintf_lite(const char* format, ScriptValue value) {
void sf_sprintf(OpcodeContext& ctx) {
ctx.setReturn(
sprintf_lite(ctx.arg(0).asString(), ctx.arg(1))
sprintf_lite(ctx.arg(0).strValue(), ctx.arg(1))
);
}
@@ -315,7 +389,11 @@ void sf_floor2(OpcodeContext& ctx) {
}
void sf_get_string_pointer(OpcodeContext& ctx) {
ctx.setReturn(reinterpret_cast<long>(ctx.arg(0).asString()), DataType::INT);
ctx.setReturn(reinterpret_cast<long>(ctx.arg(0).strValue()), DataType::INT);
}
void sf_get_text_width(OpcodeContext& ctx) {
ctx.setReturn(fo::GetTextWidth(ctx.arg(0).asString()));
}
}
+5 -1
View File
@@ -23,6 +23,8 @@ namespace sfall
namespace script
{
char* Substring(const char* str, int pos, int length);
class OpcodeContext;
void sf_sqrt(OpcodeContext&);
@@ -47,6 +49,8 @@ void sf_substr(OpcodeContext&);
void sf_strlen(OpcodeContext&);
void sf_string_compare(OpcodeContext&);
void sf_sprintf(OpcodeContext&);
void sf_ord(OpcodeContext&);
@@ -71,7 +75,7 @@ void sf_floor2(OpcodeContext&);
void sf_get_string_pointer(OpcodeContext&);
char* _stdcall Substring(const char* str, int pos, int length);
void sf_get_text_width(OpcodeContext&);
}
}
+3 -3
View File
@@ -33,10 +33,10 @@ OpcodeContext::OpcodeContext(fo::Program* program, DWORD opcode, int argNum, boo
assert(argNum < OP_MAX_ARGUMENTS);
}
OpcodeContext::OpcodeContext(fo::Program* program, DWORD opcode, int argNum, bool hasReturn, const char* opcodeName)
: OpcodeContext::OpcodeContext(program, opcode, argNum, hasReturn)
OpcodeContext::OpcodeContext(fo::Program* program, const SfallOpcodeInfo* info)
: _program(program), _opcode(info->opcode), _numArgs(info->argNum), _hasReturn(info->hasReturn), _opcodeName(info->name), _argShift(0)
{
_opcodeName = opcodeName;
assert(_numArgs < OP_MAX_ARGUMENTS);
}
const char* OpcodeContext::getOpcodeName() const {
+1 -1
View File
@@ -92,7 +92,7 @@ public:
// hasReturn - true if opcode has return value (is expression)
// opcodeName - name of a function (for logging)
OpcodeContext(fo::Program* program, DWORD opcode, int argNum, bool hasReturn);
OpcodeContext(fo::Program* program, DWORD opcode, int argNum, bool hasReturn, const char* opcodeName);
OpcodeContext(fo::Program* program, const SfallOpcodeInfo* info);
const char* getOpcodeName() const;
const char* getMetaruleName() const;
+6 -5
View File
@@ -74,6 +74,9 @@ static SfallOpcodeInfo opcodeInfoArray[] = {
{0x163, "get_year", sf_get_year, 0, true},
{0x16c, "key_pressed", sf_key_pressed, 1, true, {ARG_INT}},
{0x171, "force_encounter", sf_force_encounter, 1, false, {ARG_INT}},
{0x175, "set_dm_model", sf_set_dm_model, 1, false, {ARG_STRING}},
{0x176, "set_df_model", sf_set_df_model, 1, false, {ARG_STRING}},
{0x177, "set_movie_path", sf_set_movie_path, 2, false, {ARG_STRING, ARG_INT}},
{0x190, "get_perk_available", sf_get_perk_available, 1, true, {ARG_INT}},
{0x195, "set_weapon_knockback", sf_set_object_knockback, 3, false, {ARG_OBJECT, ARG_INT, ARG_NUMBER}},
@@ -165,7 +168,7 @@ static SfallOpcodeInfo opcodeInfoArray[] = {
{0x241, "get_npc_level", sf_get_npc_level, 1, true, {ARG_INTSTR}},
{0x24e, "substr", sf_substr, 3, true, {ARG_STRING, ARG_INT, ARG_INT}},
{0x24f, "strlen", sf_strlen, 1, true, {ARG_STRING}},
{0x24f, "strlen", sf_strlen, 1, true},
{0x250, "sprintf", sf_sprintf, 2, true, {ARG_STRING, ARG_ANY}},
{0x251, "charcode", sf_ord, 1, true, {ARG_STRING}},
// 0x252 // RESERVED
@@ -238,7 +241,7 @@ void __fastcall defaultOpcodeHandlerCall(fo::Program* program, DWORD opcodeOffse
auto iter = opcodeInfoMap.find(opcode);
if (iter != opcodeInfoMap.end()) {
auto info = iter->second;
OpcodeContext ctx(program, opcode, info->argNum, info->hasReturn, info->name);
OpcodeContext ctx(program, info);
ctx.handleOpcode(info->handler, info->argValidation);
} else {
fo::func::interpretError("Unknown opcode: %d", opcode);
@@ -298,9 +301,7 @@ void InitNewOpcodes() {
opcodes[0x172] = op_set_world_map_pos;
opcodes[0x173] = op_get_world_map_x_pos;
opcodes[0x174] = op_get_world_map_y_pos;
opcodes[0x175] = op_set_dm_model;
opcodes[0x176] = op_set_df_model;
opcodes[0x177] = op_set_movie_path;
for (int i = 0x178; i < 0x189; i++) {
opcodes[i] = op_set_perk_value;
}