From b2e60ba62eca42e7e0cbdaec247cce6a5399daf5 Mon Sep 17 00:00:00 2001 From: NovaRain Date: Wed, 28 Jun 2023 08:52:28 +0800 Subject: [PATCH] Backported IniConfigFolder option * using C-string instead of std::string. --- artifacts/ddraw.ini | 7 +++ sfall/Modules/ScriptExtender.cpp | 15 ++++++ sfall/Modules/ScriptExtender.h | 1 + sfall/Modules/Scripting/Handlers/IniFiles.cpp | 52 ++++++++++++++++--- 4 files changed, 67 insertions(+), 8 deletions(-) diff --git a/artifacts/ddraw.ini b/artifacts/ddraw.ini index cfa062cb..b5d62db4 100644 --- a/artifacts/ddraw.ini +++ b/artifacts/ddraw.ini @@ -822,6 +822,13 @@ DisableSpecialAreas=0 ;To change some engine parameters for the game mechanics, uncomment the next line ;TweaksFile=sfall\Tweaks.ini +;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +[Scripts] +;Uncomment the option to specify an additional directory for ini files used by scripts +;The game will search for ini files first relative to this directory and then relative to the root directory if not found +;The path length is limited to 61 characters +;IniConfigFolder=mods\iniConfigs + ;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Debugging] ;Extra sfall configuration settings that can be used by modders diff --git a/sfall/Modules/ScriptExtender.cpp b/sfall/Modules/ScriptExtender.cpp index 1f9e1e32..1dcf4fb5 100644 --- a/sfall/Modules/ScriptExtender.cpp +++ b/sfall/Modules/ScriptExtender.cpp @@ -58,6 +58,8 @@ static char highlightFail2[128]; char ScriptExtender::gTextBuffer[5120]; // used as global temp text buffer for script functions +char ScriptExtender::iniConfigFolder[64]; + bool ScriptExtender::OnMapLeave; static std::vector scriptsIndexList; @@ -1069,6 +1071,19 @@ void ScriptExtender::init() { HookCalls(obj_remove_outline_hook, objRemoveOutlineAddr); } + size_t len = IniReader::GetConfigString("Scripts", "IniConfigFolder", "", iniConfigFolder, 64); + if (len) { + char c = iniConfigFolder[len - 1]; + bool pathSeparator = (c == '\\' || c == '/'); + if (len > 62 || (len == 62 && !pathSeparator)) { + iniConfigFolder[0] = '\0'; + dlogr("Error: IniConfigFolder path is too long.", DL_MAIN); + } else if (!pathSeparator) { + iniConfigFolder[len++] = '\\'; + iniConfigFolder[len] = '\0'; + } + } + alwaysFindScripts = isDebug && (IniReader::GetIntDefaultConfig("Debugging", "AlwaysFindScripts", 0) != 0); if (alwaysFindScripts) dlogr("Always searching for global/hook scripts behavior enabled.", DL_SCRIPT); diff --git a/sfall/Modules/ScriptExtender.h b/sfall/Modules/ScriptExtender.h index 49d94488..416353b0 100644 --- a/sfall/Modules/ScriptExtender.h +++ b/sfall/Modules/ScriptExtender.h @@ -39,6 +39,7 @@ public: static void OnGameReset(); + static char iniConfigFolder[64]; static bool OnMapLeave; static char gTextBuffer[5120]; diff --git a/sfall/Modules/Scripting/Handlers/IniFiles.cpp b/sfall/Modules/Scripting/Handlers/IniFiles.cpp index f812f79d..a917c54c 100644 --- a/sfall/Modules/Scripting/Handlers/IniFiles.cpp +++ b/sfall/Modules/Scripting/Handlers/IniFiles.cpp @@ -38,12 +38,21 @@ void ResetIniCache() { ConfigArrayCacheDat.clear(); } +static bool IsSpecialIni(const char* str, const char* end) { + const char* pos = strfind(str, &IniReader::instance().getConfigFile()[2]); // TODO test + if (pos && pos < end) return true; + pos = strfind(str, "f2_res.ini"); + if (pos && pos < end) return true; + return false; +} + static int ParseIniSetting(const char* iniString, const char* &key, char section[], char file[]) { key = strstr(iniString, "|"); if (!key) return -1; DWORD filelen = (DWORD)key - (DWORD)iniString; - if (filelen >= 64) return -1; + if (ScriptExtender::iniConfigFolder[0] == '\0' && filelen >= 64) return -1; + const char* fileEnd = key; key = strstr(key + 1, "|"); if (!key) return -1; @@ -53,9 +62,18 @@ static int ParseIniSetting(const char* iniString, const char* &key, char section file[0] = '.'; file[1] = '\\'; - memcpy(&file[2], iniString, filelen); - file[2 + filelen] = 0; + if (ScriptExtender::iniConfigFolder[0] != '\0' && !IsSpecialIni(iniString, fileEnd)) { + size_t len = strlen(ScriptExtender::iniConfigFolder); // limit up to 62 characters + memcpy(&file[2], ScriptExtender::iniConfigFolder, len); + memcpy(&file[2 + len], iniString, filelen); // copy path and file + file[2 + len + filelen] = 0; + if (GetFileAttributesA(file) & FILE_ATTRIBUTE_DIRECTORY) goto defRoot; // also file not found + } else { +defRoot: + memcpy(&file[2], iniString, filelen); + file[2 + filelen] = 0; + } memcpy(section, &iniString[filelen + 1], seclen); section[seclen] = 0; @@ -140,9 +158,28 @@ static std::string GetSanitizedDBPath(const char* pathArg) { return std::move(path); } +static std::string GetIniFilePathFromArg(const ScriptValue& arg) { + const char* pathArg = arg.strValue(); + std::string fileName(".\\"); + if (ScriptExtender::iniConfigFolder[0] == '\0') { + fileName += pathArg; + } else { + fileName += ScriptExtender::iniConfigFolder; + fileName += pathArg; + if (GetFileAttributesA(fileName.c_str()) & FILE_ATTRIBUTE_DIRECTORY) { + const char* str = pathArg; + for (size_t i = 2; ; i++, str++) { + //if (*str == '.') str += (str[1] == '.') ? 3 : 2; // skip '.\' or '..\' + fileName[i] = *str; + if (!*str) break; + } + } + } + return std::move(fileName); +} + void mf_get_ini_sections(OpcodeContext& ctx) { - std::string fileName = std::string(".\\") + ctx.arg(0).strValue(); - Config* config = IniReader::instance().getIniConfig(fileName.c_str()); + Config* config = IniReader::instance().getIniConfig(GetIniFilePathFromArg(ctx.arg(0)).c_str()); if (config == nullptr) { ctx.setReturn(CreateTempArray(0, 0)); return; @@ -169,8 +206,7 @@ void mf_get_ini_section(OpcodeContext& ctx) { DWORD arrayId = CreateTempArray(-1, 0); // associative ctx.setReturn(arrayId); - std::string fileName = std::string(".\\") + ctx.arg(0).strValue(); - Config* config = IniReader::instance().getIniConfig(fileName.c_str()); + Config* config = IniReader::instance().getIniConfig(GetIniFilePathFromArg(ctx.arg(0)).c_str()); if (config == nullptr) return; // ini file not found const Config::Data& data = config->data(); @@ -184,7 +220,7 @@ void mf_get_ini_config(OpcodeContext& ctx) { bool isDb = ctx.arg(1).asBool(); std::string filePath(isDb ? GetSanitizedDBPath(ctx.arg(0).strValue()) - : std::string(".\\") + ctx.arg(0).strValue()); + : GetIniFilePathFromArg(ctx.arg(0))); if (filePath.size() == 0) { ctx.printOpcodeError("%s() - invalid config file path: %s", ctx.getMetaruleName(), ctx.arg(0).strValue());