diff --git a/CHANGELOG.md b/CHANGELOG.md index 258761a6..9fc877dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -131,6 +131,8 @@ - CLEO_ResolvePath - CLEO_ListDirectory - CLEO_ListDirectoryFree + - CLEO_GetGameDirectory + - CLEO_GetUserDirectory - CLEO_GetScriptByName - CLEO_GetScriptByFilename - CLEO_GetScriptDebugMode diff --git a/cleo_plugins/DebugUtils/DebugUtils.cpp b/cleo_plugins/DebugUtils/DebugUtils.cpp index 5e49836f..13ef5f08 100644 --- a/cleo_plugins/DebugUtils/DebugUtils.cpp +++ b/cleo_plugins/DebugUtils/DebugUtils.cpp @@ -1,13 +1,15 @@ -#include "ScreenLog.h" -#include "CLEO.h" -#include "CLEO_Utils.h" -#include "CTimer.h" #include // keyboard #include #include #include #include +#include "CTimer.h" + +#include "CLEO.h" +#include "CLEO_Utils.h" +#include "ScreenLog.h" + using namespace CLEO; class DebugUtils diff --git a/cleo_plugins/Text/CTextManager.cpp b/cleo_plugins/Text/CTextManager.cpp index 91b524d6..5a6587b7 100644 --- a/cleo_plugins/Text/CTextManager.cpp +++ b/cleo_plugins/Text/CTextManager.cpp @@ -96,7 +96,7 @@ namespace CLEO TRACE("Loading CLEO text files..."); // create FXT directory if not present yet - FS::create_directory(FS::path(Gta_Root_Dir_Path).append("cleo\\cleo_text")); + FS::create_directory(std::string(CLEO_GetGameDirectory()) + "\\cleo\\cleo_text"); // load whole FXT files directory auto list = CLEO::CLEO_ListDirectory(nullptr, "cleo\\cleo_text\\*.fxt", false, true); diff --git a/cleo_sdk/CLEO.h b/cleo_sdk/CLEO.h index 823e92ad..1a1d3c83 100644 --- a/cleo_sdk/CLEO.h +++ b/cleo_sdk/CLEO.h @@ -538,6 +538,8 @@ void WINAPI CLEO_StringListFree(StringList list); // releases resources used by // Should be always used when working with files. Provides ModLoader compatibility void WINAPI CLEO_ResolvePath(CRunningScript* thread, char* inOutPath, DWORD pathMaxLen); // convert to absolute (file system) path StringList WINAPI CLEO_ListDirectory(CRunningScript* thread, const char* searchPath, BOOL listDirs, BOOL listFiles); // thread can be null, searchPath can contain wildcards. After use CLEO_StringListFree must be called on returned StringList to free its resources +LPCSTR WINAPI CLEO_GetGameDirectory(); // absolute game directory filepath without trailling path separator +LPCSTR WINAPI CLEO_GetUserDirectory(); // absolute game user files directory filepath without trailling path separator void WINAPI CLEO_Log(eLogLevel level, const char* msg); // add message to log diff --git a/cleo_sdk/CLEO_Utils.h b/cleo_sdk/CLEO_Utils.h index 35d0ca0f..ec373658 100644 --- a/cleo_sdk/CLEO_Utils.h +++ b/cleo_sdk/CLEO_Utils.h @@ -15,8 +15,10 @@ #include "CLEO.h" #include "CPools.h" // from GTA Plugin SDK #include "shellapi.h" // game window minimize/maximize support +#include #include #include +#include #include #include @@ -76,25 +78,11 @@ namespace CLEO OPCODE_WRITE_PARAM_PTR(value) // memory address */ - static const char* Gta_Root_Dir_Path = (char*)0x00B71AE0; - static const char* Gta_User_Dir_Path = (char*)0x00C92368; - static bool IsLegacyScript(CLEO::CRunningScript* thread) { return CLEO_GetScriptVersion(thread) < CLEO_VER_5; } - // this plugin's config file - static std::string GetConfigFilename() - { - std::string configFile = Gta_Root_Dir_Path; - if (!configFile.empty() && configFile.back() != '\\') configFile.push_back('\\'); - - configFile += "cleo\\cleo_plugins\\" TARGET_NAME ".ini"; - - return configFile; - } - static std::string StringPrintf(const char* format, ...) { va_list args; @@ -112,6 +100,23 @@ namespace CLEO return result; } + static bool StringStartsWith(const std::string_view str, const std::string_view prefix, bool caseSensitive = true) + { + if (str.length() < prefix.length()) + { + return false; + } + + if (caseSensitive) + { + return strncmp(str.data(), prefix.data(), prefix.length()) == 0; + } + else + { + return _strnicmp(str.data(), prefix.data(), prefix.length()) == 0; + } + } + static std::string ScriptInfoStr(CLEO::CRunningScript* thread) { std::string info(1024, '\0'); @@ -119,27 +124,79 @@ namespace CLEO return std::move(info); } - // does file path points inside game directories? (game root or user files) - static bool IsFilepathSafe(CLEO::CRunningScript* thread, const char* path) + // Normalize filepath, collapse all parent directory references. Input should be absolute path without expandable %variables% + static void FilepathNormalize(std::string& path, bool normalizeCase = true) + { + if (path.empty()) return; + + std::replace(path.begin(), path.end(), '/', '\\'); + if (normalizeCase) std::transform(path.begin(), path.end(), path.begin(), [](unsigned char c) { return tolower(c); }); // to lower case + + // collapse references to parent directory + const auto ParentRef = "\\..\\"; + const auto ParentRefLen = 4; + + size_t refPos = path.find(ParentRef); + while (refPos != std::string::npos && refPos > 0) + { + size_t parentPos = path.rfind('\\', refPos - 1); // find start of parent name + + if (parentPos == std::string::npos) + return; // parent must be root of the path then. We want to keep absolute path, let it be as is (even if "C:\..\" makes no sense) + + path.replace(parentPos, (refPos - parentPos) + ParentRefLen - 1, ""); // remove parent and parent reference + + refPos = path.find(ParentRef); // find next + } + + while(path.back() == '\\') path.pop_back(); // remove trailing path separator(s) + } + + // strip parent prefix from filepath if present + static void FilepathRemoveParent(std::string& path, const std::string_view base) + { + if (path.length() < base.length()) return; // can not hold that prefix + if (!StringStartsWith(path, base, false)) return; + if (path.length() > base.length() && path[base.length()] != '\\') return; // just similar base + + path.replace(0, base.length() + 1, ""); // remove path separator too if present + } + + // this plugin's config file + static std::string GetConfigFilename() { - auto IsSubpath = [](std::filesystem::path path, std::filesystem::path base) + std::string path = CLEO_GetGameDirectory(); + path += "\\cleo\\cleo_plugins\\"; + path += TARGET_NAME; + path += ".ini"; + return path; + } + + // does normalized file path points inside game directories? (game root or user files) + static bool FilepathIsSafe(CLEO::CRunningScript* thread, const char* path) + { + if (strchr(path, '%') != nullptr) { - auto relative = std::filesystem::relative(path, base); - return !relative.empty() && *relative.begin() != ".."; - }; + return false; // do not allow paths containing expandable variables + } - auto fsPath = std::filesystem::path(path); - if (!fsPath.is_absolute()) + std::string absolute; + if (!std::filesystem::path(path).is_absolute()) { - fsPath = CLEO_GetScriptWorkDir(thread) / fsPath; + absolute = CLEO_GetScriptWorkDir(thread); + absolute += '\\'; + absolute += path; + FilepathNormalize(absolute, false); + path = absolute.c_str(); } - if (IsSubpath(fsPath, Gta_Root_Dir_Path) || IsSubpath(fsPath, Gta_User_Dir_Path)) + if (!StringStartsWith(path, CLEO_GetGameDirectory(), false) && + !StringStartsWith(path, CLEO_GetUserDirectory(), false)) { - return true; + return false; } - return false; + return true; } static bool IsObjectHandleValid(DWORD handle) @@ -604,7 +661,7 @@ namespace CLEO #define OPCODE_READ_PARAMS_FORMATTED(_format, _varName) char _varName[2 * MAX_STR_LEN + 1]; char* _varName##Ok = CLEO_ReadParamsFormatted(thread, _format, _varName, sizeof(_varName)); #define OPCODE_READ_PARAM_FILEPATH(_varName) char _buff_##_varName[512]; const char* ##_varName = _readParamText(thread, _buff_##_varName, 512); if(##_varName != nullptr) ##_varName = _buff_##_varName; if(_paramWasString()) CLEO_ResolvePath(thread, _buff_##_varName, 512); else return OpcodeResult::OR_INTERRUPT; \ - if(!IsFilepathSafe(thread, ##_varName)) { SHOW_ERROR("Forbidden file path '%s' outside game directories in script %s \nScript suspended.", ##_varName, ScriptInfoStr(thread).c_str()); return thread->Suspend(); } + if(!FilepathIsSafe(thread, ##_varName)) { SHOW_ERROR("Forbidden file path '%s' outside game directories in script %s \nScript suspended.", ##_varName, ScriptInfoStr(thread).c_str()); return thread->Suspend(); } #define OPCODE_READ_PARAM_PTR() _readParam(thread).pParam; \ if (!_paramWasInt()) { SHOW_ERROR("Input argument %s expected to be integer, got %s in script %s\nScript suspended.", GetParamInfo().c_str(), CLEO::ToKindStr(_lastParamType, _lastParamArrayType), CLEO::ScriptInfoStr(thread).c_str()); return thread->Suspend(); } \ diff --git a/source/CCustomOpcodeSystem.cpp b/source/CCustomOpcodeSystem.cpp index 830f32d3..d8617a35 100644 --- a/source/CCustomOpcodeSystem.cpp +++ b/source/CCustomOpcodeSystem.cpp @@ -79,7 +79,6 @@ namespace CLEO void(__thiscall * ProcessScript)(CRunningScript*); - const char * (__cdecl * GetUserDirectory)(); void(__cdecl * ChangeToUserDir)(); void(__cdecl * ChangeToProgramDir)(const char *); @@ -215,7 +214,6 @@ namespace CLEO MemWrite(gvm.TranslateMemoryAddress(MA_OPCODE_HANDLER_REF), &customOpcodeHandlers); MemWrite(0x00469EF0, &customOpcodeHandlers); // TODO: game version translation - GetUserDirectory = gvm.TranslateMemoryAddress(MA_GET_USER_DIR_FUNCTION); ChangeToUserDir = gvm.TranslateMemoryAddress(MA_CHANGE_TO_USER_DIR_FUNCTION); ChangeToProgramDir = gvm.TranslateMemoryAddress(MA_CHANGE_TO_PROGRAM_DIR_FUNCTION); FindGroundZ = gvm.TranslateMemoryAddress(MA_FIND_GROUND_Z_FUNCTION); diff --git a/source/CCustomOpcodeSystem.h b/source/CCustomOpcodeSystem.h index fd450c7c..919c8e41 100644 --- a/source/CCustomOpcodeSystem.h +++ b/source/CCustomOpcodeSystem.h @@ -8,7 +8,6 @@ namespace CLEO { typedef OpcodeResult(__stdcall * CustomOpcodeHandler)(CRunningScript*); - extern const char* (__cdecl* GetUserDirectory)(); extern void(__cdecl* ChangeToUserDir)(); extern void(__cdecl* ChangeToProgramDir)(const char*); diff --git a/source/CGameVersionManager.cpp b/source/CGameVersionManager.cpp index e7da16de..2227cfaa 100644 --- a/source/CGameVersionManager.cpp +++ b/source/CGameVersionManager.cpp @@ -78,7 +78,6 @@ namespace CLEO { 0x00B74490, memory_und, 0x00B74490, 0x00B76B10, 0x00C01038 }, // MA_PED_POOL, { 0x00B74494, memory_und, 0x00B74494, 0x00B76B14, 0x00C0103C }, // MA_VEHICLE_POOL, { 0x00B7449C, memory_und, 0x00B7449C, 0x00B76B18, 0x00C01044 }, // MA_OBJECT_POOL, - { 0x00744FB0, memory_und, 0x00744FB0, 0x007457E0, 0x0077EDC0 }, // MA_GET_USER_DIR_FUNCTION, { 0x00538860, memory_und, 0x00538860, 0x00538D00, 0x0054A730 }, // MA_CHANGE_TO_USER_DIR_FUNCTION, { 0x005387D0, memory_und, 0x005387D0, 0x00538C70, 0x0054A680 }, // MA_CHANGE_TO_PROGRAM_DIR_FUNCTION, { 0x00569660, memory_und, 0x00569660, 0x00569B00, 0x00583CB0 }, // MA_FIND_GROUND_Z_FUNCTION, diff --git a/source/CGameVersionManager.h b/source/CGameVersionManager.h index 34867776..9d356bc5 100644 --- a/source/CGameVersionManager.h +++ b/source/CGameVersionManager.h @@ -94,7 +94,6 @@ namespace CLEO MA_PED_POOL, MA_VEHICLE_POOL, MA_OBJECT_POOL, - MA_GET_USER_DIR_FUNCTION, MA_CHANGE_TO_USER_DIR_FUNCTION, MA_CHANGE_TO_PROGRAM_DIR_FUNCTION, MA_FIND_GROUND_Z_FUNCTION, diff --git a/source/CPluginSystem.cpp b/source/CPluginSystem.cpp index 1a6a6dcc..c18f6da6 100644 --- a/source/CPluginSystem.cpp +++ b/source/CPluginSystem.cpp @@ -68,14 +68,18 @@ void CPluginSystem::LoadPlugins() { for (auto it = paths.crbegin(); it != paths.crend(); it++) { - const auto filename = it->c_str(); + std::string filename = *it; + + // ModLoader support: keep game dir relative paths relative + FilepathRemoveParent(filename, Filepath_Game); + TRACE(""); // separator - TRACE("Loading plugin '%s'", filename); + TRACE("Loading plugin '%s'", filename.c_str()); - HMODULE hlib = LoadLibrary(filename); + HMODULE hlib = LoadLibrary(filename.c_str()); if (!hlib) { - LOG_WARNING(0, "Error loading plugin '%s'", filename); + LOG_WARNING(0, "Error loading plugin '%s'", filename.c_str()); continue; } diff --git a/source/CScriptEngine.cpp b/source/CScriptEngine.cpp index 5fd105ff..666cfc1b 100644 --- a/source/CScriptEngine.cpp +++ b/source/CScriptEngine.cpp @@ -684,79 +684,61 @@ namespace CLEO return {}; } - try + auto fsPath = FS::path(path); + + // check for virtual path root + enum class VPref{ None, Game, User, Script, Cleo, Modules } virtualPrefix = VPref::None; + if(!fsPath.empty()) { - auto fsPath = FS::path(path); + const auto root = fsPath.begin()->string(); // first path element + const auto r = root.c_str(); - // check for virtual path root - enum class VPref{ None, Game, User, Script, Cleo, Modules } virtualPrefix = VPref::None; - auto root = fsPath.begin(); - if(root != fsPath.end()) - { - if(*root == DIR_GAME) virtualPrefix = VPref::Game; - else if (*root == DIR_USER) virtualPrefix = VPref::User; - else if (*root == DIR_SCRIPT) virtualPrefix = VPref::Script; - else if (*root == DIR_CLEO) virtualPrefix = VPref::Cleo; - else if (*root == DIR_MODULES) virtualPrefix = VPref::Modules; - } + if(_strcmpi(r, DIR_GAME) == 0) virtualPrefix = VPref::Game; + else if (_strcmpi(r, DIR_USER) == 0) virtualPrefix = VPref::User; + else if (_strcmpi(r, DIR_SCRIPT) == 0) virtualPrefix = VPref::Script; + else if (_strcmpi(r, DIR_CLEO) == 0) virtualPrefix = VPref::Cleo; + else if (_strcmpi(r, DIR_MODULES) == 0) virtualPrefix = VPref::Modules; + } - // not virtual - if(virtualPrefix == VPref::None) + // not virtual + if(virtualPrefix == VPref::None) + { + if(fsPath.is_relative()) { - if(fsPath.is_relative()) - { - if(customWorkDir != nullptr) - fsPath = ResolvePath(customWorkDir) / fsPath; - else - fsPath = GetWorkDir() / fsPath; - - auto resolved = FS::weakly_canonical(fsPath).string(); - - // ModLoader support: do not expand game dir relative paths - if (resolved.find(Filepath_Root) == 0) - return FS::relative(resolved, Filepath_Root).string(); - else - return resolved; - } - - return FS::weakly_canonical(fsPath).string(); + if(customWorkDir != nullptr) + fsPath = ResolvePath(customWorkDir) / fsPath; + else + fsPath = GetWorkDir() / fsPath; } - // expand virtual paths - FS::path resolved; + auto result = fsPath.string(); + FilepathNormalize(result, false); - if (virtualPrefix == VPref::User) // user files location - { - resolved = GetUserDirectory(); - } - else - if (virtualPrefix == VPref::Script) // this script's source file location - { - resolved = GetScriptFileDir(); - } - else - { - // all remaing variants starts with game root - resolved = Filepath_Root; - - switch(virtualPrefix) - { - case(VPref::Cleo): resolved /= "cleo"; break; - case(VPref::Modules): resolved /= "cleo\\cleo_modules"; break; - } - } + // ModLoader support: make paths withing game directory relative to it + FilepathRemoveParent(result, Filepath_Game); - // append all but virtual prefix from original path - for(auto it = ++fsPath.begin(); it != fsPath.end(); it++) - resolved /= *it; - - return FS::weakly_canonical(resolved).string(); // collapse "..\" uses + return std::move(result); } - catch (const std::exception& ex) + + // expand virtual paths + FS::path resolved; + switch(virtualPrefix) { - TRACE("Error while resolving path: %s", ex.what()); - return {}; + case VPref::User: resolved = Filepath_User; break; + case VPref::Script: resolved = GetScriptFileDir(); break; + case VPref::Game: resolved = Filepath_Game; break; + case VPref::Cleo: resolved = Filepath_Cleo; break; + case VPref::Modules: resolved = Filepath_Cleo + "\\modules"; break; + default : resolved = ""; break; // should never happen } + + // append all but virtual prefix from original path + for (auto it = ++fsPath.begin(); it != fsPath.end(); it++) + resolved /= *it; + + auto result = resolved.string(); + FilepathNormalize(result, false); + return std::move(result); } std::string CCustomScript::GetInfoStr(bool currLineInfo) const @@ -968,17 +950,17 @@ namespace CLEO if (CGame::bMissionPackGame == 0) // regular main game { - MainScriptFileDir = FS::path(Filepath_Root).append("data\\script").string(); + MainScriptFileDir = Filepath_Game + "\\data\\script"; MainScriptFileName = "main.scm"; } else // mission pack { - MainScriptFileDir = FS::path(GetUserDirectory()).append(stringPrintf("MPACK\\MPACK%d", CGame::bMissionPackGame)).string(); + MainScriptFileDir = Filepath_User + stringPrintf("\\MPACK\\MPACK%d", CGame::bMissionPackGame); MainScriptFileName = "scr.scm"; } NativeScriptsDebugMode = GetPrivateProfileInt("General", "DebugMode", 0, Filepath_Config.c_str()) != 0; - MainScriptCurWorkDir = Filepath_Root; + MainScriptCurWorkDir = Filepath_Game; GetInstance().ModuleSystem.LoadCleoModules(); LoadState(GetInstance().saveSlot); @@ -1596,7 +1578,7 @@ namespace CLEO else { bDebugMode = GetInstance().ScriptEngine.NativeScriptsDebugMode; // global setting - workDir = Filepath_Root; // game root + workDir = Filepath_Game; // game root } using std::ios; diff --git a/source/CleoBase.cpp b/source/CleoBase.cpp index 96bf2752..418b601c 100644 --- a/source/CleoBase.cpp +++ b/source/CleoBase.cpp @@ -116,12 +116,12 @@ namespace CLEO if (m_bStarted) return; // already started m_bStarted = true; - FS::create_directory(FS::path(Filepath_Root).append("cleo")); - FS::create_directory(FS::path(Filepath_Root).append("cleo\\cleo_modules")); - FS::create_directory(FS::path(Filepath_Root).append("cleo\\cleo_plugins")); - FS::create_directory(FS::path(Filepath_Root).append("cleo\\cleo_saves")); + FS::create_directory(Filepath_Cleo); + FS::create_directory(Filepath_Cleo + "\\cleo_modules"); + FS::create_directory(Filepath_Cleo + "\\cleo_plugins"); + FS::create_directory(Filepath_Cleo + "\\cleo_saves"); - OpcodeInfoDb.Load(FS::path(Filepath_Root).append("cleo\\.config\\sa.json").generic_string().c_str()); + OpcodeInfoDb.Load((Filepath_Cleo + "\\.config\\sa.json").c_str()); CodeInjector.OpenReadWriteAccess(); // must do this earlier to ensure plugins write access on init GameMenu.Inject(CodeInjector); @@ -276,14 +276,14 @@ namespace CLEO if (!listDirs && !listFiles) return {}; // nothing to list, done + // make absolute auto fsSearchPath = FS::path(searchPath); if (!fsSearchPath.is_absolute()) { - auto workDir = (thread != nullptr) ? - ((CCustomScript*)thread)->GetWorkDir() : - Filepath_Root.c_str(); - - fsSearchPath = workDir / fsSearchPath; + if (thread != nullptr) + fsSearchPath = ((CCustomScript*)thread)->GetWorkDir() / fsSearchPath; + else + fsSearchPath = Filepath_Game / fsSearchPath; } WIN32_FIND_DATA wfd = { 0 }; @@ -311,5 +311,15 @@ namespace CLEO return CreateStringList(found); } + + LPCSTR WINAPI CLEO_GetGameDirectory() + { + return Filepath_Game.c_str(); + } + + LPCSTR WINAPI CLEO_GetUserDirectory() + { + return Filepath_User.c_str(); + } } diff --git a/source/cleo.def b/source/cleo.def index 59112e0c..00da8e63 100644 --- a/source/cleo.def +++ b/source/cleo.def @@ -55,3 +55,5 @@ EXPORTS _CLEO_RegisterCommand@8 @52 _CLEO_IsScriptRunning@4 @53 _CLEO_TerminateScript@4 @54 + _CLEO_GetGameDirectory@0 @55 + _CLEO_GetUserDirectory@0 @56 diff --git a/source/stdafx.h b/source/stdafx.h index 96603147..266e9974 100644 --- a/source/stdafx.h +++ b/source/stdafx.h @@ -19,36 +19,57 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "..\cleo_sdk\CLEO.h" +#include "..\cleo_sdk\CLEO_Utils.h" // global constant paths. Initialize before anything else namespace FS = std::filesystem; -static std::string GetApplicationDirectory() +static std::string GetGameDirectory() // already stored in Filepath_Game { - char buffer[512]; - GetModuleFileNameA(NULL, buffer, sizeof(buffer) - 1); // game exe absolute path - return FS::path(buffer).parent_path().string(); + static const auto GTA_GetCWD = (char* (__cdecl*)(char*, int))0x00836E91; // SA 1.0 US ingame function + + std::string path; + + path.resize(MAX_PATH); + GTA_GetCWD(path.data(), path.size()); // assume work dir is game location when initialized + path.resize(strlen(path.data())); + + CLEO::FilepathNormalize(path); + + return std::move(path); } -static const std::string Filepath_Root = GetApplicationDirectory(); -//static const std::string Filepath_Cleo = FS::path(Filepath_Root).append("cleo").string(); // absolute path -static const std::string Filepath_Cleo = "cleo"; // relative path - allow mod loaders to affect it +static std::string GetUserDirectory() // already stored in Filepath_User +{ + static const char* GTA_User_Dir_Path = (char*)0x00C92368; // SA 1.0 US + static const auto GTA_InitUserDirectories = (char* (__cdecl*)())0x00744FB0; // SA 1.0 US -static const std::string Filepath_Config = FS::path(Filepath_Cleo).append(".cleo_config.ini").string(); -static const std::string Filepath_Log = FS::path(Filepath_Cleo).append(".cleo.log").string(); + if (strlen(GTA_User_Dir_Path) == 0) + { + GTA_InitUserDirectories(); + } + std::string path = GTA_User_Dir_Path; + CLEO::FilepathNormalize(path); -#include -#include -#include -#include -#include -#include -#include + return std::move(path); +} -#include "..\cleo_sdk\CLEO.h" -#include "..\cleo_sdk\CLEO_Utils.h" -#include "CTheScripts.h" +inline const std::string Filepath_Game = GetGameDirectory(); +inline const std::string Filepath_User = GetUserDirectory(); +inline const std::string Filepath_Cleo = Filepath_Game + "\\cleo"; +inline const std::string Filepath_Config = Filepath_Cleo + "\\.cleo_config.ini"; +inline const std::string Filepath_Log = Filepath_Cleo + "\\.cleo.log"; #define NUM_SCAN_ENTITIES 16