diff --git a/src/plugins/gta3/std.asi/args_translator/translator_basic.hpp b/src/plugins/gta3/std.asi/args_translator/translator_basic.hpp index 28756256..9645273a 100644 --- a/src/plugins/gta3/std.asi/args_translator/translator_basic.hpp +++ b/src/plugins/gta3/std.asi/args_translator/translator_basic.hpp @@ -401,14 +401,17 @@ struct path_translator_base // Gets the current working directory relative to the the game path // If working directory is not anywhere near the game path, return null // This is the manual version, where you send the current working directory (@fullpath) and game path (@gamepath) - static const char* GetCurrentDir(const char* fullpath, const char* gamePath, size_t max) + // + // Both strings MUST have the same character type, otherwise their comparison fails + template + static const T* GetCurrentDir(const T* fullpath, const T* gamePath, size_t max) { - const char* currdir = 0; + const T* currdir = 0; // Iterate on the game path comparing it with the current working dir for(size_t i = 0; i < max; ++i) { - if(gamePath[i] == 0) // End of game path? + if(gamePath[i] == T(0)) // End of game path? { // Then here starts the relative part currdir = &fullpath[i]; @@ -417,7 +420,7 @@ struct path_translator_base else if(gamePath[i] != fullpath[i]) // Piece of gamepath not equal to the working dir? wow { // Let's calm down, if working directory ended and game path is ending, we're still 'equal' - if(fullpath[i] == 0 && gamePath[i] == '\\' && gamePath[i+1] == 0) + if(fullpath[i] == T(0) && gamePath[i] == T('\\') && gamePath[i+1] == T(0)) { // Point current directory to "\0" part of fullpath currdir = &fullpath[i]; @@ -429,6 +432,34 @@ struct path_translator_base return currdir; } + // The game path in the same character type as the translated argument. + // Mod Loader stores the game path as a narrow (ANSI) string, so wide arguments (which is what + // C++/STL based modules pass to GetFileAttributesW/CreateFileW/... ) need a converted copy; + // comparing them against the narrow game path can never match. + static const char* GetGamePathFor(const char*) + { + return plugin_ptr->loader->gamepath; + } + + static const wchar_t* GetGamePathFor(const wchar_t*) + { + static const std::wstring path = []() -> std::wstring + { + const char* narrow = plugin_ptr->loader->gamepath; + if(narrow == nullptr || narrow[0] == 0) return std::wstring(); + + // CP_ACP: the same conversion the ANSI Win32 APIs perform on this path + int length = MultiByteToWideChar(CP_ACP, 0, narrow, -1, nullptr, 0); // count includes the terminator + if(length <= 1) return std::wstring(); + + std::wstring wide(size_t(length) - 1, L'\0'); + MultiByteToWideChar(CP_ACP, 0, narrow, -1, &wide[0], length); // rewrites the terminator + return wide; + }(); + + return path.c_str(); + } + }; 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..0c07bdd9 100644 --- a/src/plugins/gta3/std.asi/args_translator/xtranslator_path.hpp +++ b/src/plugins/gta3/std.asi/args_translator/xtranslator_path.hpp @@ -53,13 +53,14 @@ inline bool path_translator_base::CallInfo::GetCurrentDir(const T*& arg, char t if(this->bAbsolutePath) // This argument is an absolute path? { // Get current directory assuming argument is the fullpath - currdir = GetCurrentDir( (const char*) arg, plugin_ptr->loader->gamepath, -1); + // + const T* relative = GetCurrentDir(arg, GetGamePathFor(arg), -1); // If could get the currdir, set up some stuff and go ahead on the translation // For SetDir, if currdir is empty, that means it should chdir into base dir, so don't touch it - if(currdir && (!bSetDir || currdir[0] != 0)) + if(relative && (!bSetDir || relative[0] != 0)) { - arg = (T*) currdir; + arg = relative; currdir = 0; bCheckModules = true; }