From bbe9a2d26d5ee7d16e465dcd1b5ce538379cdea5 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Sat, 23 Nov 2019 20:32:57 +0800 Subject: [PATCH] Added "get_text_width" and "string_compare" script functions Rewrote set_dm/df_model, set_movie_path functions with WRAP_OPCODE. --- artifacts/scripting/headers/sfall.h | 5 +- artifacts/scripting/sfall function notes.txt | 13 +- sfall/FalloutEngine.cpp | 1 + sfall/FalloutEngine.h | 1 + sfall/HeroAppearance.cpp | 20 +-- sfall/HeroAppearance.h | 2 +- sfall/ScriptExtender.cpp | 4 +- sfall/ScriptExtender.h | 2 +- sfall/ScriptOps/MetaruleOp.hpp | 2 + sfall/ScriptOps/MiscOps.hpp | 175 ++++++------------- sfall/ScriptOps/ScriptUtils.hpp | 95 +++++++++- 11 files changed, 168 insertions(+), 152 deletions(-) diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h index 705725d9..d02cb956 100644 --- a/artifacts/scripting/headers/sfall.h +++ b/artifacts/scripting/headers/sfall.h @@ -246,6 +246,7 @@ #define get_object_data(obj, offset) sfall_func2("get_object_data", obj, offset) #define get_outline(obj) sfall_func1("get_outline", obj) #define get_sfall_arg_at(argNum) sfall_func1("get_sfall_arg_at", argNum) +#define get_text_width(text) sfall_func1("get_text_width", text) #define hide_window(winName) sfall_func1("hide_window", winName) #define intface_hide sfall_func0("intface_hide") #define intface_is_hidden sfall_func0("intface_is_hidden") @@ -270,11 +271,13 @@ #define set_object_data(obj, offset, value) sfall_func3("set_object_data", obj, offset, value) #define set_outline(obj, color) sfall_func2("set_outline", obj, color) #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) diff --git a/artifacts/scripting/sfall function notes.txt b/artifacts/scripting/sfall function notes.txt index 8ab05406..df511166 100644 --- a/artifacts/scripting/sfall function notes.txt +++ b/artifacts/scripting/sfall function notes.txt @@ -524,8 +524,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 @@ -576,6 +576,15 @@ optional arguments: - 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 ------- ------------------------ diff --git a/sfall/FalloutEngine.cpp b/sfall/FalloutEngine.cpp index a39f46ac..49830e98 100644 --- a/sfall/FalloutEngine.cpp +++ b/sfall/FalloutEngine.cpp @@ -93,6 +93,7 @@ TGameObj** ptr_i_lhand = reinterpret_cast(_i_lhand); TGameObj** ptr_i_rhand = reinterpret_cast(_i_rhand); DWORD* ptr_i_wid = reinterpret_cast(_i_wid); TGameObj** ptr_i_worn = reinterpret_cast(_i_worn); +DWORD* ptr_idle_func = reinterpret_cast(_idle_func); DWORD* ptr_In_WorldMap = reinterpret_cast(_In_WorldMap); DWORD* ptr_info_line = reinterpret_cast(_info_line); DWORD* ptr_interfaceWindow = reinterpret_cast(_interfaceWindow); diff --git a/sfall/FalloutEngine.h b/sfall/FalloutEngine.h index 72a557c6..46f17858 100644 --- a/sfall/FalloutEngine.h +++ b/sfall/FalloutEngine.h @@ -346,6 +346,7 @@ extern TGameObj** ptr_i_lhand; extern TGameObj** ptr_i_rhand; extern DWORD* ptr_i_wid; extern TGameObj** ptr_i_worn; +extern DWORD* ptr_idle_func; extern DWORD* ptr_In_WorldMap; extern DWORD* ptr_info_line; extern DWORD* ptr_interfaceWindow; diff --git a/sfall/HeroAppearance.cpp b/sfall/HeroAppearance.cpp index 45392847..b8568fdb 100644 --- a/sfall/HeroAppearance.cpp +++ b/sfall/HeroAppearance.cpp @@ -506,21 +506,6 @@ static long GetFont() { return *ptr_curr_font_num; } -/* -int WordWrap(char *TextMsg, DWORD lineLength, WORD *lineNum, WORD *lineOffsets) { - int retVal; - __asm { - mov ebx, lineOffsets - mov ecx, lineNum - mov edx, lineLength - mov eax, TextMsg - call _word_wrap_ - mov retVal, eax - } - return retVal; -} -*/ - // print text to surface void PrintText(char *DisplayText, BYTE ColourIndex, DWORD Xpos, DWORD Ypos, DWORD TxtWidth, DWORD ToWidth, BYTE *ToSurface) { DWORD posOffset = Ypos * ToWidth + Xpos; @@ -548,14 +533,11 @@ DWORD GetTextHeight() { } // gets the length of a string using the currently selected font -DWORD GetTextWidth(char *TextMsg) { - DWORD TxtWidth; +DWORD __stdcall GetTextWidth(const char *TextMsg) { __asm { mov eax, TextMsg; call dword ptr ds:[_text_width]; // get text width - mov TxtWidth, eax; } - return TxtWidth; } // get width of Char for current font diff --git a/sfall/HeroAppearance.h b/sfall/HeroAppearance.h index 38846e41..a572cef7 100644 --- a/sfall/HeroAppearance.h +++ b/sfall/HeroAppearance.h @@ -20,7 +20,7 @@ WINinfo* __stdcall GetWinStruct(long winRef); void PrintText(char *DisplayText, BYTE ColourIndex, DWORD Xpos, DWORD Ypos, DWORD TxtWidth, DWORD ToWidth, BYTE *ToSurface); -DWORD GetTextWidth(char *TextMsg); +DWORD __stdcall GetTextWidth(const char *TextMsg); DWORD GetMaxCharWidth(); long __stdcall check_buttons(); diff --git a/sfall/ScriptExtender.cpp b/sfall/ScriptExtender.cpp index 00b94828..e98a8215 100644 --- a/sfall/ScriptExtender.cpp +++ b/sfall/ScriptExtender.cpp @@ -400,6 +400,7 @@ static const SfallOpcodeMetadata opcodeMetaArray[] = { {sf_critter_inven_obj2, "critter_inven_obj2", {DATATYPE_MASK_VALID_OBJ, DATATYPE_MASK_INT}}, {sf_draw_image, "draw_image", {DATATYPE_MASK_INT | DATATYPE_MASK_STR, DATATYPE_MASK_INT, DATATYPE_MASK_INT, DATATYPE_MASK_INT, DATATYPE_MASK_INT}}, {sf_draw_image_scaled, "draw_image_scaled", {DATATYPE_MASK_INT | DATATYPE_MASK_STR, DATATYPE_MASK_INT, DATATYPE_MASK_INT, DATATYPE_MASK_INT, DATATYPE_MASK_INT, DATATYPE_MASK_INT}}, + {sf_floor2, "floor2", {DATATYPE_MASK_INT | DATATYPE_MASK_FLOAT}}, {sf_get_current_inven_size, "get_current_inven_size", {DATATYPE_MASK_VALID_OBJ}}, {sf_get_flags, "get_flags", {DATATYPE_MASK_VALID_OBJ}}, {sf_get_object_data, "get_object_data", {DATATYPE_MASK_VALID_OBJ, DATATYPE_MASK_INT}}, @@ -423,6 +424,7 @@ static const SfallOpcodeMetadata opcodeMetaArray[] = { {sf_set_window_flag, "set_window_flag", {DATATYPE_MASK_INT | DATATYPE_MASK_STR, DATATYPE_MASK_INT, DATATYPE_MASK_INT}}, {sf_show_window, "show_window", {DATATYPE_MASK_STR}}, {sf_spatial_radius, "spatial_radius", {DATATYPE_MASK_VALID_OBJ}}, + {sf_string_compare, "string_compare", {DATATYPE_MASK_STR, DATATYPE_MASK_STR, DATATYPE_MASK_INT}}, {sf_unjam_lock, "unjam_lock", {DATATYPE_MASK_VALID_OBJ}}, {sf_unwield_slot, "unwield_slot", {DATATYPE_MASK_VALID_OBJ, DATATYPE_MASK_INT}}, #ifndef NDEBUG @@ -1287,7 +1289,7 @@ void ScriptExtenderInit() { idle = GetConfigInt("Misc", "ProcessorIdle", -1); if (idle > -1) { if (idle > 127) idle = 127; - SafeWrite32(_idle_func, reinterpret_cast(Sleep)); + *ptr_idle_func = reinterpret_cast(Sleep); SafeWrite8(0x4C9F12, 0x6A); // push idle SafeWrite8(0x4C9F13, idle); } diff --git a/sfall/ScriptExtender.h b/sfall/ScriptExtender.h index 86bfc889..5984a543 100644 --- a/sfall/ScriptExtender.h +++ b/sfall/ScriptExtender.h @@ -94,7 +94,7 @@ void _stdcall AddTimerEventScripts(DWORD script, long time, long param); void _stdcall RemoveTimerEventScripts(DWORD script, long param); void _stdcall RemoveTimerEventScripts(DWORD script); -char* _stdcall mysubstr(char* str, int pos, int length); +char* _stdcall mysubstr(const char* str, int pos, int length); // variables static char reg_anim_combat_check = 1; diff --git a/sfall/ScriptOps/MetaruleOp.hpp b/sfall/ScriptOps/MetaruleOp.hpp index be41b459..bf931bd0 100644 --- a/sfall/ScriptOps/MetaruleOp.hpp +++ b/sfall/ScriptOps/MetaruleOp.hpp @@ -140,6 +140,7 @@ static const SfallMetarule metaruleArray[] = { {"get_object_data", sf_get_object_data, 2, 2}, {"get_outline", sf_get_outline, 1, 1}, {"get_sfall_arg_at", sf_get_sfall_arg_at, 1, 1}, + {"get_text_width", sf_get_text_width, 1, 1}, {"hide_window", sf_hide_window, 0, 1}, {"intface_hide", sf_intface_hide, 0, 0}, {"intface_is_hidden", sf_intface_is_hidden, 0, 0}, @@ -167,6 +168,7 @@ static const SfallMetarule metaruleArray[] = { {"set_window_flag", sf_set_window_flag, 3, 3}, {"show_window", sf_show_window, 0, 1}, {"spatial_radius", sf_spatial_radius, 1, 1}, + {"string_compare", sf_string_compare, 2, 3}, {"tile_refresh_display", sf_tile_refresh_display, 0, 0}, {"unjam_lock", sf_unjam_lock, 1, 1}, {"unwield_slot", sf_unwield_slot, 2, 2}, diff --git a/sfall/ScriptOps/MiscOps.hpp b/sfall/ScriptOps/MiscOps.hpp index f352f515..0358edfb 100644 --- a/sfall/ScriptOps/MiscOps.hpp +++ b/sfall/ScriptOps/MiscOps.hpp @@ -32,122 +32,66 @@ * Misc operators */ -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."; +static void _stdcall SetDMModel2() { + const ScriptValue &modelArg = opHandler.arg(0); -//// *** End Helios *** /// -static void _stdcall strcpy_p(char* to, const char* from) { - strcpy_s(to, 64, from); + if (modelArg.isString()) { + const char* model = modelArg.strValue(); + if (strlen(model) > 64) { + opHandler.printOpcodeError(stringTooLong, "set_dm_model"); + return; + } + strcpy(defaultMaleModelName, model); + } else { + OpcodeInvalidArgs("set_dm_model"); + } } + static void __declspec(naked) SetDMModel() { - __asm { - push ebx; - push ecx; - push edx; - push edi; - mov edi, eax; - call interpretPopShort_; - mov edx, eax; - mov eax, edi; - call interpretPopLong_; - cmp dx, VAR_TYPE_STR2; - jz next; - cmp dx, VAR_TYPE_STR; - jnz end; -next: - mov ebx, eax; - mov eax, edi; - call interpretGetString_; - push eax; - push defaultMaleModelNamePtr; - call strcpy_p; -end: - pop edi; - pop edx; - pop ecx; - pop ebx; - retn; + _WRAP_OPCODE(SetDMModel2, 1, 0) +} + +static void _stdcall SetDFModel2() { + const ScriptValue &modelArg = opHandler.arg(0); + + if (modelArg.isString()) { + const char* model = modelArg.strValue(); + if (strlen(model) > 64) { + opHandler.printOpcodeError(stringTooLong, "set_df_model"); + return; + } + strcpy(defaultFemaleModelName, model); + } else { + OpcodeInvalidArgs("set_df_model"); } } + static void __declspec(naked) SetDFModel() { - __asm { - push ebx; - push ecx; - push edx; - push edi; - mov edi, eax; - call interpretPopShort_; - mov edx, eax; - mov eax, edi; - call interpretPopLong_; - cmp dx, VAR_TYPE_STR2; - jz next; - cmp dx, VAR_TYPE_STR; - jnz end; -next: - mov ebx, eax; - mov eax, edi; - call interpretGetString_; - push eax; - push defaultFemaleModelNamePtr; - call strcpy_p; -end: - pop edi; - pop edx; - pop ecx; - pop ebx; - retn; + _WRAP_OPCODE(SetDFModel2, 1, 0) +} + +static void _stdcall SetMoviePath2() { + const ScriptValue &fileNameArg = opHandler.arg(0), + &movieIdArg = opHandler.arg(1); + + if (fileNameArg.isString() && movieIdArg.isInt()) { + long movieID = movieIdArg.rawValue(); + if (movieID < 0 || movieID >= MaxMovies) return; + const char* fileName = fileNameArg.strValue(); + if (strlen(fileName) > 64) { + opHandler.printOpcodeError(stringTooLong, "set_movie_path"); + return; + } + strcpy(&MoviePaths[movieID * 65], fileName); + } else { + OpcodeInvalidArgs("set_movie_path"); } } + static void __declspec(naked) SetMoviePath() { - __asm { - push ebx; - push ecx; - push edx; - push edi; - push esi; - mov edi, eax; - call interpretPopShort_; - mov ebx, eax; - mov eax, edi; - call interpretPopLong_; - mov esi, eax; - mov eax, edi; - call interpretPopShort_; - mov edx, eax; - mov eax, edi; - call 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 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; - } + _WRAP_OPCODE(SetMoviePath2, 2, 0) } static void __declspec(naked) GetYear() { @@ -180,14 +124,12 @@ static void __declspec(naked) GetYear() { static void __declspec(naked) GameLoaded() { __asm { push ecx; - push edx; push eax; push eax; // script call ScriptHasLoaded; movzx edx, al; pop eax; _RET_VAL_INT2(ecx); - pop edx; pop ecx; retn; } @@ -196,15 +138,13 @@ static void __declspec(naked) GameLoaded() { static void __declspec(naked) SetPipBoyAvailable() { __asm { push ecx; - push edx; _GET_ARG_INT(end); cmp eax, 0; jl end; cmp eax, 1; jg end; - mov byte ptr ds:[_gmovie_played_list + 0x3], al; + mov byte ptr ds:[_gmovie_played_list][0x3], al; end: - pop edx; pop ecx; retn; } @@ -938,17 +878,6 @@ end: //numbers subgame functions static void __declspec(naked) NBCreateChar() { __asm { - /*pushad; - push eax; - call NumbersCreateChar; - mov edx, eax; - pop eax; - mov ecx, eax; - call interpretPushLong_; - mov eax, ecx; - mov edx, VAR_TYPE_INT; - call interpretPushShort_; - popad;*/ retn; } } @@ -1141,7 +1070,7 @@ end: static void __declspec(naked) modified_ini() { __asm { push ecx; - mov edx, modifiedIni; + mov edx, modifiedIni; _RET_VAL_INT2(ecx); pop ecx; retn; diff --git a/sfall/ScriptOps/ScriptUtils.hpp b/sfall/ScriptOps/ScriptUtils.hpp index 7f692ec9..3de967fa 100644 --- a/sfall/ScriptOps/ScriptUtils.hpp +++ b/sfall/ScriptOps/ScriptUtils.hpp @@ -24,10 +24,83 @@ #include "ScriptExtender.h" #include "ScriptArrays.hpp" -#include "FileSystem.h" #include "Arrays.h" +#include "HeroAppearance.h" #include "Message.h" +// compares strings case-insensitive with specifics for Fallout +static bool _stdcall 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 + } +} + +static void sf_string_compare() { + if (opHandler.numArgs() < 3) { + opHandler.setReturn( + (_stricmp(opHandler.arg(0).strValue(), opHandler.arg(1).strValue()) ? 0 : 1) + ); + } else { + opHandler.setReturn(FalloutStringCompare(opHandler.arg(0).strValue(), opHandler.arg(1).strValue(), opHandler.arg(2).rawValue())); + } +} + static void __declspec(naked) funcSqrt() { __asm { pushad; @@ -120,6 +193,7 @@ end: retn; } } + static void __declspec(naked) funcCos() { __asm { pushad; @@ -159,6 +233,7 @@ end: retn; } } + static void __declspec(naked) funcTan() { __asm { pushad; @@ -199,6 +274,7 @@ end: retn; } } + static void __declspec(naked) funcATan() { __asm { pushad; @@ -289,6 +365,7 @@ static int _stdcall StringSplit(const char* str, const char* split) { } return id; } + static void __declspec(naked) string_split() { __asm { pushad; @@ -340,13 +417,16 @@ end: retn; } } + static int _stdcall str_to_int_internal(const char* str) { return static_cast(strtol(str, (char**)nullptr, 0)); // auto-determine radix } + static DWORD _stdcall str_to_flt_internal(const char* str) { float f = static_cast(atof(str)); return *(DWORD*)&f; } + static void __declspec(naked) str_to_int() { __asm { pushad; @@ -380,6 +460,7 @@ end: retn; } } + static void __declspec(naked) str_to_flt() { __asm { pushad; @@ -413,7 +494,8 @@ end: retn; } } -char* _stdcall mysubstr(char* str, int pos, int length) { + +char* _stdcall mysubstr(const char* str, int pos, int length) { char* newstr; int srclen; srclen = strlen(str); @@ -432,11 +514,12 @@ char* _stdcall mysubstr(char* str, int pos, int length) { return newstr; } -static DWORD _stdcall mystrlen(char* str) { +static DWORD _stdcall mystrlen(const char* str) { return strlen(str); } + static char* sprintfbuf = nullptr; -static char* _stdcall mysprintf(char* format, DWORD value, DWORD valueType) { +static char* _stdcall mysprintf(const char* format, DWORD value, DWORD valueType) { valueType = valueType & 0xFFFF; // use lower 2 bytes int fmtlen = strlen(format); int buflen = fmtlen + 1; @@ -848,3 +931,7 @@ static void __declspec(naked) op_message_str_game() { static void sf_floor2() { opHandler.setReturn(static_cast(floor(opHandler.arg(0).asFloat()))); } + +static void sf_get_text_width() { + opHandler.setReturn(GetTextWidth(opHandler.arg(0).asString()), DATATYPE_INT); +}