From eb7afc3c8171d0a32d622ca89312facc07b6d082 Mon Sep 17 00:00:00 2001 From: Seemann Date: Mon, 21 Sep 2026 21:37:43 -0400 Subject: [PATCH 1/2] Improve compatibility with CLEO5 --- src/plugins/gta3/std.asi/ModuleInfo.cpp | 17 ++++++++++++++-- .../args_translator/hacks/FindCleoScripts.hpp | 6 ++++++ .../args_translator/xtranslator_path.hpp | 20 +++++++++++++++++++ src/plugins/gta3/std.asi/asi.h | 10 ++++++++-- 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/plugins/gta3/std.asi/ModuleInfo.cpp b/src/plugins/gta3/std.asi/ModuleInfo.cpp index 5190b1b6..a8d3e3f8 100644 --- a/src/plugins/gta3/std.asi/ModuleInfo.cpp +++ b/src/plugins/gta3/std.asi/ModuleInfo.cpp @@ -243,10 +243,23 @@ void ThePlugin::LocateCleo() this->iCleoVersion = CLEO_GetVersion? CLEO_GetVersion() : 0; Log("CLEO library version %X found at \"%s\"", iCleoVersion, p); - if(this->bHasNoCleoFolder = !IsPath((std::string(loader->gamepath) + "./CLEO/").c_str())) - Log("Warning: No CLEO folder found, may cause problems"); this->asiList.emplace_back(p, nullptr, hCleo); + + // assume CLEO directory is located in root by default + this->cleoFolder = std::string(loader->gamepath) + "cleo"; + + // Try asking newer CLEO 5 where its folder it located + auto CLEO_GetCleoDirectory = (const char* (__stdcall*)()) GetProcAddress(hCleo, "_CLEO_GetCleoDirectory@0"); + if(CLEO_GetCleoDirectory) + { + if(const char* cleoDir = CLEO_GetCleoDirectory()) + this->cleoFolder = cleoDir; + } + + if(this->bHasNoCleoFolder = !IsPath((this->cleoFolder + "\\").c_str())) + Log("Warning: No CLEO folder found, may cause problems"); + this->asiList.back().PatchImports(); } } diff --git a/src/plugins/gta3/std.asi/args_translator/hacks/FindCleoScripts.hpp b/src/plugins/gta3/std.asi/args_translator/hacks/FindCleoScripts.hpp index 893cc4c5..3bc5adc5 100644 --- a/src/plugins/gta3/std.asi/args_translator/hacks/FindCleoScripts.hpp +++ b/src/plugins/gta3/std.asi/args_translator/hacks/FindCleoScripts.hpp @@ -171,6 +171,12 @@ namespace hacks // Get path in iterator compatible with what CLEO.asi expects to receive char* GetCleoCompatiblePath(char* buf) { + // CLEO 5 allows relocating the CLEO folder out of the game root + // (e.g. to scripts\cleo), which breaks the "..\" form, + // so always prefer the absolute path. + if(plugin_ptr->cast().IsCLEO5()) + return GetFullPath(buf); + // CLEO 4.3 needs a existing CLEO folder for the path relativity to work if(plugin_ptr->cast().iCleoVersion > 0x401011E && plugin_ptr->cast().bHasNoCleoFolder) return GetFullPath(buf); diff --git a/src/plugins/gta3/std.asi/args_translator/xtranslator_path.hpp b/src/plugins/gta3/std.asi/args_translator/xtranslator_path.hpp index b8625f3a..a8b24d21 100644 --- a/src/plugins/gta3/std.asi/args_translator/xtranslator_path.hpp +++ b/src/plugins/gta3/std.asi/args_translator/xtranslator_path.hpp @@ -142,12 +142,32 @@ inline void path_translator_base::CallInfo::TranslatePathForMainExecutable(const } }; + auto CheckCleoFolder = [&]() + { + auto& cleoDir = plugin_ptr->cast().cleoFolder; + + if(!bSet && !cleoDir.empty()) + { + struct PathBase { std::string translationPath; } cleo; + cleo.translationPath = cleoDir; + bSet = CxBuildPath(p, cleo, currdir, arg, build_path, false); + } + }; + // Run the tries by priority based on what is the caller if(asi->bIsCleo) { // Cleo scripts first CheckCleoScripts(); CheckASI(); + + // File operations worked only by accident in some legacy CLEO scripts, + // because modloader added CLEO folder containing .cleo plugins to search path. + // As CLEO5 moved them to cleo_plugins, we need to keep + // searching there to preserve backward compatibility. + // https://github.com/cleolibrary/CLEO5/issues/589 + if(plugin_ptr->cast().IsCLEO5()) + CheckCleoFolder(); } else { diff --git a/src/plugins/gta3/std.asi/asi.h b/src/plugins/gta3/std.asi/asi.h index 9035c348..6fc39350 100644 --- a/src/plugins/gta3/std.asi/asi.h +++ b/src/plugins/gta3/std.asi/asi.h @@ -122,8 +122,14 @@ class ThePlugin : public modloader::basic_plugin typedef std::list CsInfoList; // CLEO.ASI version - int iCleoVersion; - bool bHasNoCleoFolder; + int iCleoVersion = 0; + bool bHasNoCleoFolder = false; + + // CLEO data folder. Can be root\cleo or relative to cleo.asi (CLEO 5) + std::string cleoFolder; + + // CLEO 5.0 or newer (support for relocatable CLEO folder and cleo_plugins) + bool IsCLEO5() const { return iCleoVersion >= 0x05000000; } // Set to true when loading - do not translate relative paths, as we're chdir'd into modloader already // Set by the DLL Load Notification From e64811ce015fd10a84f40eb2856bd3861220ec77 Mon Sep 17 00:00:00 2001 From: Seemann Date: Tue, 22 Sep 2026 19:54:01 -0400 Subject: [PATCH 2/2] clarify cleoFolder path --- src/plugins/gta3/std.asi/asi.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/gta3/std.asi/asi.h b/src/plugins/gta3/std.asi/asi.h index 6fc39350..b9563f1a 100644 --- a/src/plugins/gta3/std.asi/asi.h +++ b/src/plugins/gta3/std.asi/asi.h @@ -125,7 +125,8 @@ class ThePlugin : public modloader::basic_plugin int iCleoVersion = 0; bool bHasNoCleoFolder = false; - // CLEO data folder. Can be root\cleo or relative to cleo.asi (CLEO 5) + // Absolute path to the CLEO data folder, without a trailing path separator. + // Can be ("\cleo") or relative to cleo.asi ("\scripts\cleo") std::string cleoFolder; // CLEO 5.0 or newer (support for relocatable CLEO folder and cleo_plugins)