diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index 99eb6a78..cfa062cb 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -1,5 +1,5 @@ ;sfall configuration settings -;v3.8.39 +;v3.8.40 [Main] ;Set to 1 if you want to use command line arguments to tell sfall to use another ini file @@ -15,7 +15,7 @@ UseCommandLine=0 ;This section allows you to set multiple paths to folders containing mods or patches ;Paths to folders and Fallout .dat files are supported ;The PatchFileXX options are available from 0 to 99. Larger numbers take precedence over smaller numbers (same as patchXXX.dat) -;Starting from 4.3.9/3.8.39, the game will load custom .dat files and folders from \mods\mods_order.txt +;Starting from 4.4/3.8.40, the game will load custom .dat files and folders from \mods\mods_order.txt ;The files and folders in mods_order.txt will have a higher priority than the PatchFileXX options ;The complete order of how the engine loads game data is: ;master_patches > critter_patches > mods_order.txt > PatchFileXX > patchXXX.dat > sfall.dat > critter_dat > f2_res_patches > f2_res_dat > master_dat @@ -649,7 +649,7 @@ DialogPanelAnimDelay=33 PipboyTimeAnimDelay=50 ;Set to 1 to let you use the command cursor to specify targets for party members to attack in combat -;Note: The old built-in ControlCombat option is removed in 3.8.39 +;Note: The old built-in ControlCombat option is removed in 3.8.40 ;See the example mods in the modders pack for a script-based NPC combat control mod PartyOrderToAttack=0 diff --git a/artifacts/scripting/compiler/sslc_readme.md b/artifacts/scripting/compiler/sslc_readme.md index a8d32006..c549efe3 100644 --- a/artifacts/scripting/compiler/sslc_readme.md +++ b/artifacts/scripting/compiler/sslc_readme.md @@ -220,16 +220,6 @@ Syntax which requires sfall for compiled scripts to be interpreted is marked by end ``` -- Empty statements in blocks are allowed: This is just a convenience to save scripters a bit of memory. Some of the macros in the Fallout headers include their own semicolons while others do not. With the original compiler you had to remember which was which, and if you got it wrong the script would not compile. Now it's always safe to include your own semicolon, even if the macro already had its own. For example, this would not compile with the original sslc, but will with the sfall edition: - ``` - #define my_macro display_msg("foo"); - - procedure start begin - my_macro; - end - ``` - __NOTE:__ **Does not work currently.** - - Procedure stringify operator `@`: Designed to make callback-procedures a better option and allow for basic functional programming. Basically it replaces procedure names preceded by `@` by a string constant. - old: ``` @@ -360,6 +350,13 @@ There are several changes in this version of sslc which may result in problems f ### Changelog +**sfall 4.4:** +- fixed compiler crash when trying to define an exported procedure with variables +- fixed compiler giving "symbol or string expected" error when trying to call procedure using a string literal +- fixed optimizer not treating `call string_variable` as variable use +- fixed unused arguments in a procedure being removed incorrectly by the optimizer +- fixed unused string literals in an optimized-out procedure not being removed by the optimizer + **sfall 4.2.9:** - added support for additional universal opcodes `sfall_func7` and `sfall_func8` - fixed a compilation error when the script has a UTF-8 BOM @@ -368,7 +365,7 @@ There are several changes in this version of sslc which may result in problems f - added ability to declare local variables anywhere in the procedure body **sfall 4.2.3:** -- fixed compiler giving "assignment operator expected" error when a variable-like macro is not being defined properly +- fixed compiler giving "assignment operator expected" error when a variable-like macro is not defined properly - added new logical operators `AndAlso`, `OrElse` for short-circuit evaluation of logical expressions - added an alternative (C/Java-style) assignment operator `=` - added support for new `div` operator (unsigned integer division) diff --git a/artifacts/scripting/sfall function notes.md b/artifacts/scripting/sfall function notes.md index d9a0dfd8..b05f0bec 100644 --- a/artifacts/scripting/sfall function notes.md +++ b/artifacts/scripting/sfall function notes.md @@ -722,7 +722,7 @@ sfall_funcX metarule functions #### get_string_pointer `int sfall_func1("get_string_pointer", string text)` - (DEPRECATED) Returns a pointer to a string variable or to a text -- __NOTE:__ this function is intended for use only in `HOOK_DESCRIPTIONOBJ`. Starting from sfall 4.3.9/3.8.39, you can return normal strings directly in the hook without calling the function +- __NOTE:__ this function is intended for use only in `HOOK_DESCRIPTIONOBJ`. Starting from sfall 4.4/3.8.40, you can return normal strings directly in the hook without calling the function ---- #### dialog_message diff --git a/sfall/ConsoleWindow.cpp b/sfall/ConsoleWindow.cpp index 1c4d9d17..56880ef5 100644 --- a/sfall/ConsoleWindow.cpp +++ b/sfall/ConsoleWindow.cpp @@ -90,7 +90,7 @@ static void __declspec(naked) debug_printf_hook() { } void ConsoleWindow::OnBeforeGameClose() { - instance().savePosition(); + if (instance()._mode) instance().savePosition(); } void ConsoleWindow::init() { diff --git a/sfall/Modules/LoadOrder.cpp b/sfall/Modules/LoadOrder.cpp index 0e0cfbb9..713787ab 100644 --- a/sfall/Modules/LoadOrder.cpp +++ b/sfall/Modules/LoadOrder.cpp @@ -262,26 +262,21 @@ static bool NormalizePath(std::string &path) { if (pos != std::string::npos) { path.erase(pos); } - // Skip paths with colons. - if (path.find(':') != std::string::npos) return false; - - // Normalize directory separators. - std::replace(path.begin(), path.end(), '/', '\\'); - - // Disallow paths going outside of root folder. - if (path.find(".\\") != std::string::npos || path.find("..\\") != std::string::npos) return false; - // Trim whitespaces. path.erase(0, path.find_first_not_of(whiteSpaces)); // trim left path.erase(path.find_last_not_of(whiteSpaces) + 1); // trim right - + // Normalize directory separators. + std::replace(path.begin(), path.end(), '/', '\\'); // Remove leading slashes. path.erase(0, path.find_first_not_of('\\')); - return !path.empty(); -} -static bool FileOrFolderExists(const std::string& path) { - return GetFileAttributesA(path.c_str()) != INVALID_FILE_ATTRIBUTES; + // Disallow paths trying to "escape" game folder: + if (path.find(':') != std::string::npos || + path.find(".\\") != std::string::npos || + path.find("..\\") != std::string::npos) { + return false; + } + return !path.empty(); } static bool FileExists(const std::string& path) { @@ -294,26 +289,48 @@ static bool FolderExists(const std::string& path) { return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY)); } +static bool FileOrFolderExists(const std::string& path) { + return GetFileAttributesA(path.c_str()) != INVALID_FILE_ATTRIBUTES; +} + +static bool ValidateExtraPatch(std::string& path, const char* basePath, const char* entryName) { + if (!NormalizePath(path)) { + if (!path.empty()) { + dlog_f("Error: %s invalid entry: \"%s\"\n", DL_INIT, entryName, path.c_str()); + } + return false; + } + path.insert(0, basePath); + if (!FileOrFolderExists(path)) { + const char* entry = path.c_str(); + if (path.find(".\\") == 0) entry += 2; + dlog_f("Error: %s entry not found: %s\n", DL_INIT, entryName, entry); + return false; + } + return true; +} + // Patches placed at the back of the vector will have priority in the chain over the front(previous) patches static void GetExtraPatches() { char patchFile[12] = "PatchFile"; for (int i = 0; i < 100; i++) { _itoa(i, &patchFile[9], 10); std::string patch = IniReader::GetConfigString("ExtraPatches", patchFile, "", MAX_PATH); - if (patch.empty() || !NormalizePath(patch) || !FileOrFolderExists(patch)) continue; + if (!ValidateExtraPatch(patch, "", patchFile)) continue; patchFiles.push_back(patch); } const std::string modsPath = ".\\mods\\"; - const std::string loadOrderFilePath = modsPath + "mods_order.txt"; - - dlogr("Loading custom patches:", DL_MAIN); + const std::string loadOrderFileName = "mods_order.txt"; + const std::string loadOrderFilePath = modsPath + loadOrderFileName; // If the mods folder does not exist, create it. if (!FolderExists(modsPath)) { + dlog_f("Creating Mods folder: %s\n", DL_INIT, modsPath.c_str() + 2); CreateDirectoryA(modsPath.c_str(), 0); } // If load order file does not exist, initialize it automatically with mods already in the mods folder. if (!FileExists(loadOrderFilePath)) { + dlog_f("Generating Mods Order file based on the contents of Mods folder: %s\n", DL_INIT, loadOrderFilePath.c_str() + 2); std::ofstream loadOrderFile(loadOrderFilePath, std::ios::out | std::ios::trunc); if (loadOrderFile.is_open()) { // Search all .dat files and folders in the mods folder. @@ -333,27 +350,24 @@ static void GetExtraPatches() { // Sort the search result. std::sort(autoLoadedPatchFiles.begin(), autoLoadedPatchFiles.end()); // Write found files into load order file. - for (std::vector::iterator it = autoLoadedPatchFiles.begin(); it != autoLoadedPatchFiles.end(); ++it) { + for (std::vector::const_iterator it = autoLoadedPatchFiles.begin(); it != autoLoadedPatchFiles.end(); ++it) { loadOrderFile << *it << '\n'; } } else { - dlog_f("Error creating load order file %s.\n", DL_MAIN, loadOrderFilePath.c_str() + 2); + dlog_f("Error creating load order file %s.\n", DL_INIT, loadOrderFilePath.c_str() + 2); } } + // Add mods from load order file. std::ifstream loadOrderFile(loadOrderFilePath, std::ios::in); if (loadOrderFile.is_open()) { std::string patch; while (std::getline(loadOrderFile, patch)) { - if (patch.empty() || !NormalizePath(patch)) continue; - patch = modsPath + patch; - if (!FileOrFolderExists(patch)) continue; - - dlog_f("> %s\n", DL_MAIN, patch.c_str() + 2); + if (!ValidateExtraPatch(patch, modsPath.c_str(), loadOrderFileName.c_str())) continue; patchFiles.push_back(patch); } } else { - dlog_f("Error opening %s for read: 0x%x\n", DL_MAIN, loadOrderFilePath.c_str() + 2, GetLastError()); + dlog_f("Error opening %s for read: 0x%x\n", DL_INIT, loadOrderFilePath.c_str() + 2, GetLastError()); } // Remove first duplicates @@ -365,6 +379,11 @@ static void GetExtraPatches() { } } } + + dlogr("Loading extra patches:", DL_INIT); + for (std::vector::const_iterator it = patchFiles.begin(); it != patchFiles.end(); ++it) { + dlog_f("> %s\n", DL_INIT, (*it).c_str() + 2); + } } static void MultiPatchesPatch() { diff --git a/sfall/Modules/PartyControl.cpp b/sfall/Modules/PartyControl.cpp index 5491ecdb..d8e00abe 100644 --- a/sfall/Modules/PartyControl.cpp +++ b/sfall/Modules/PartyControl.cpp @@ -113,8 +113,8 @@ static void SetCurrentDude(fo::GameObject* npc) { // reset traits fo::ptr::pc_trait[0] = fo::ptr::pc_trait[1] = -1; - // reset perks (except Awareness) - std::memset(&(*fo::ptr::perkLevelDataList)[0].perkData[1], 0, sizeof(DWORD) * (fo::PERK_count - 1)); + // reset perks + std::memset(*fo::ptr::perkLevelDataList, 0, sizeof(DWORD) * fo::PERK_count); // change level int level = fo::func::isPartyMember(npc) diff --git a/sfall/Modules/Scripting/Handlers/Misc.cpp b/sfall/Modules/Scripting/Handlers/Misc.cpp index cc801966..32c5703f 100644 --- a/sfall/Modules/Scripting/Handlers/Misc.cpp +++ b/sfall/Modules/Scripting/Handlers/Misc.cpp @@ -293,7 +293,7 @@ void op_get_tile_fid(OpcodeContext& ctx) { default: // Vanilla uses 12 bits for Tile FID, which means 4096 possible values, the mask was 0x0FFF // BUT sfall's FRM Limit patch extended it to 14 bits, so we need to use mask 0x3FFF - result = squareData & 0x3FFF; // this is how opcode worked prior to 4.3.9 + result = squareData & 0x3FFF; // this is how opcode worked up to 4.3.8 } ctx.setReturn(result); } diff --git a/sfall/Modules/Scripting/Handlers/Objects.cpp b/sfall/Modules/Scripting/Handlers/Objects.cpp index a0d73482..1d6dcfac 100644 --- a/sfall/Modules/Scripting/Handlers/Objects.cpp +++ b/sfall/Modules/Scripting/Handlers/Objects.cpp @@ -373,7 +373,7 @@ void mf_outlined_object(OpcodeContext& ctx) { } void mf_set_dude_obj(OpcodeContext& ctx) { - auto obj = ctx.arg(0).object(); + fo::GameObject* obj = ctx.arg(0).object(); if (obj == nullptr || obj->IsCritter()) { //if (!InCombat && obj && obj != PartyControl::RealDudeObject()) { // ctx.printOpcodeError("%s() - controlling of the critter is only allowed in combat mode.", ctx.getMetaruleName()); diff --git a/sfall/version.h b/sfall/version.h index 3383b013..d0de4ac4 100644 --- a/sfall/version.h +++ b/sfall/version.h @@ -24,7 +24,7 @@ #define VERSION_MAJOR 3 #define VERSION_MINOR 8 -#define VERSION_BUILD 39 +#define VERSION_BUILD 40 #define VERSION_REV 0 -#define VERSION_STRING "3.8.39" +#define VERSION_STRING "3.8.40"