Load order: more informative logging

Code fixes to PartyControl.cpp and ConsoleWindow.cpp.

Updated version number in documents.

Updated changelog in sslc document, and removed the mention about
empty statements (it never worked).
This commit is contained in:
NovaRain
2023-06-21 22:39:59 +08:00
parent 34092900a6
commit 135c26ab70
9 changed files with 64 additions and 48 deletions
+3 -3
View File
@@ -1,5 +1,5 @@
;sfall configuration settings ;sfall configuration settings
;v3.8.39 ;v3.8.40
[Main] [Main]
;Set to 1 if you want to use command line arguments to tell sfall to use another ini file ;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 ;This section allows you to set multiple paths to folders containing mods or patches
;Paths to folders and Fallout .dat files are supported ;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) ;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 <GameRoot>\mods\mods_order.txt ;Starting from 4.4/3.8.40, the game will load custom .dat files and folders from <GameRoot>\mods\mods_order.txt
;The files and folders in mods_order.txt will have a higher priority than the PatchFileXX options ;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: ;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 ;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 PipboyTimeAnimDelay=50
;Set to 1 to let you use the command cursor to specify targets for party members to attack in combat ;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 ;See the example mods in the modders pack for a script-based NPC combat control mod
PartyOrderToAttack=0 PartyOrderToAttack=0
+8 -11
View File
@@ -220,16 +220,6 @@ Syntax which requires sfall for compiled scripts to be interpreted is marked by
end 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. - 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: - old:
``` ```
@@ -360,6 +350,13 @@ There are several changes in this version of sslc which may result in problems f
### Changelog ### 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:** **sfall 4.2.9:**
- added support for additional universal opcodes `sfall_func7` and `sfall_func8` - added support for additional universal opcodes `sfall_func7` and `sfall_func8`
- fixed a compilation error when the script has a UTF-8 BOM - 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 - added ability to declare local variables anywhere in the procedure body
**sfall 4.2.3:** **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 new logical operators `AndAlso`, `OrElse` for short-circuit evaluation of logical expressions
- added an alternative (C/Java-style) assignment operator `=` - added an alternative (C/Java-style) assignment operator `=`
- added support for new `div` operator (unsigned integer division) - added support for new `div` operator (unsigned integer division)
+1 -1
View File
@@ -722,7 +722,7 @@ sfall_funcX metarule functions
#### get_string_pointer #### get_string_pointer
`int sfall_func1("get_string_pointer", string text)` `int sfall_func1("get_string_pointer", string text)`
- (DEPRECATED) Returns a pointer to a string variable or to a 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 #### dialog_message
+1 -1
View File
@@ -90,7 +90,7 @@ static void __declspec(naked) debug_printf_hook() {
} }
void ConsoleWindow::OnBeforeGameClose() { void ConsoleWindow::OnBeforeGameClose() {
instance().savePosition(); if (instance()._mode) instance().savePosition();
} }
void ConsoleWindow::init() { void ConsoleWindow::init() {
+45 -26
View File
@@ -262,26 +262,21 @@ static bool NormalizePath(std::string &path) {
if (pos != std::string::npos) { if (pos != std::string::npos) {
path.erase(pos); 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. // Trim whitespaces.
path.erase(0, path.find_first_not_of(whiteSpaces)); // trim left path.erase(0, path.find_first_not_of(whiteSpaces)); // trim left
path.erase(path.find_last_not_of(whiteSpaces) + 1); // trim right path.erase(path.find_last_not_of(whiteSpaces) + 1); // trim right
// Normalize directory separators.
std::replace(path.begin(), path.end(), '/', '\\');
// Remove leading slashes. // Remove leading slashes.
path.erase(0, path.find_first_not_of('\\')); path.erase(0, path.find_first_not_of('\\'));
return !path.empty();
}
static bool FileOrFolderExists(const std::string& path) { // Disallow paths trying to "escape" game folder:
return GetFileAttributesA(path.c_str()) != INVALID_FILE_ATTRIBUTES; 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) { 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)); 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 // Patches placed at the back of the vector will have priority in the chain over the front(previous) patches
static void GetExtraPatches() { static void GetExtraPatches() {
char patchFile[12] = "PatchFile"; char patchFile[12] = "PatchFile";
for (int i = 0; i < 100; i++) { for (int i = 0; i < 100; i++) {
_itoa(i, &patchFile[9], 10); _itoa(i, &patchFile[9], 10);
std::string patch = IniReader::GetConfigString("ExtraPatches", patchFile, "", MAX_PATH); 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); patchFiles.push_back(patch);
} }
const std::string modsPath = ".\\mods\\"; const std::string modsPath = ".\\mods\\";
const std::string loadOrderFilePath = modsPath + "mods_order.txt"; const std::string loadOrderFileName = "mods_order.txt";
const std::string loadOrderFilePath = modsPath + loadOrderFileName;
dlogr("Loading custom patches:", DL_MAIN);
// If the mods folder does not exist, create it. // If the mods folder does not exist, create it.
if (!FolderExists(modsPath)) { if (!FolderExists(modsPath)) {
dlog_f("Creating Mods folder: %s\n", DL_INIT, modsPath.c_str() + 2);
CreateDirectoryA(modsPath.c_str(), 0); CreateDirectoryA(modsPath.c_str(), 0);
} }
// If load order file does not exist, initialize it automatically with mods already in the mods folder. // If load order file does not exist, initialize it automatically with mods already in the mods folder.
if (!FileExists(loadOrderFilePath)) { 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); std::ofstream loadOrderFile(loadOrderFilePath, std::ios::out | std::ios::trunc);
if (loadOrderFile.is_open()) { if (loadOrderFile.is_open()) {
// Search all .dat files and folders in the mods folder. // Search all .dat files and folders in the mods folder.
@@ -333,27 +350,24 @@ static void GetExtraPatches() {
// Sort the search result. // Sort the search result.
std::sort(autoLoadedPatchFiles.begin(), autoLoadedPatchFiles.end()); std::sort(autoLoadedPatchFiles.begin(), autoLoadedPatchFiles.end());
// Write found files into load order file. // Write found files into load order file.
for (std::vector<std::string>::iterator it = autoLoadedPatchFiles.begin(); it != autoLoadedPatchFiles.end(); ++it) { for (std::vector<std::string>::const_iterator it = autoLoadedPatchFiles.begin(); it != autoLoadedPatchFiles.end(); ++it) {
loadOrderFile << *it << '\n'; loadOrderFile << *it << '\n';
} }
} else { } 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. // Add mods from load order file.
std::ifstream loadOrderFile(loadOrderFilePath, std::ios::in); std::ifstream loadOrderFile(loadOrderFilePath, std::ios::in);
if (loadOrderFile.is_open()) { if (loadOrderFile.is_open()) {
std::string patch; std::string patch;
while (std::getline(loadOrderFile, patch)) { while (std::getline(loadOrderFile, patch)) {
if (patch.empty() || !NormalizePath(patch)) continue; if (!ValidateExtraPatch(patch, modsPath.c_str(), loadOrderFileName.c_str())) continue;
patch = modsPath + patch;
if (!FileOrFolderExists(patch)) continue;
dlog_f("> %s\n", DL_MAIN, patch.c_str() + 2);
patchFiles.push_back(patch); patchFiles.push_back(patch);
} }
} else { } 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 // Remove first duplicates
@@ -365,6 +379,11 @@ static void GetExtraPatches() {
} }
} }
} }
dlogr("Loading extra patches:", DL_INIT);
for (std::vector<std::string>::const_iterator it = patchFiles.begin(); it != patchFiles.end(); ++it) {
dlog_f("> %s\n", DL_INIT, (*it).c_str() + 2);
}
} }
static void MultiPatchesPatch() { static void MultiPatchesPatch() {
+2 -2
View File
@@ -113,8 +113,8 @@ static void SetCurrentDude(fo::GameObject* npc) {
// reset traits // reset traits
fo::ptr::pc_trait[0] = fo::ptr::pc_trait[1] = -1; fo::ptr::pc_trait[0] = fo::ptr::pc_trait[1] = -1;
// reset perks (except Awareness) // reset perks
std::memset(&(*fo::ptr::perkLevelDataList)[0].perkData[1], 0, sizeof(DWORD) * (fo::PERK_count - 1)); std::memset(*fo::ptr::perkLevelDataList, 0, sizeof(DWORD) * fo::PERK_count);
// change level // change level
int level = fo::func::isPartyMember(npc) int level = fo::func::isPartyMember(npc)
+1 -1
View File
@@ -293,7 +293,7 @@ void op_get_tile_fid(OpcodeContext& ctx) {
default: default:
// Vanilla uses 12 bits for Tile FID, which means 4096 possible values, the mask was 0x0FFF // 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 // 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); ctx.setReturn(result);
} }
+1 -1
View File
@@ -373,7 +373,7 @@ void mf_outlined_object(OpcodeContext& ctx) {
} }
void mf_set_dude_obj(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 (obj == nullptr || obj->IsCritter()) {
//if (!InCombat && obj && obj != PartyControl::RealDudeObject()) { //if (!InCombat && obj && obj != PartyControl::RealDudeObject()) {
// ctx.printOpcodeError("%s() - controlling of the critter is only allowed in combat mode.", ctx.getMetaruleName()); // ctx.printOpcodeError("%s() - controlling of the critter is only allowed in combat mode.", ctx.getMetaruleName());
+2 -2
View File
@@ -24,7 +24,7 @@
#define VERSION_MAJOR 3 #define VERSION_MAJOR 3
#define VERSION_MINOR 8 #define VERSION_MINOR 8
#define VERSION_BUILD 39 #define VERSION_BUILD 40
#define VERSION_REV 0 #define VERSION_REV 0
#define VERSION_STRING "3.8.39" #define VERSION_STRING "3.8.40"