diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index cb25dc9ebf3..79fab508826 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -67,14 +67,6 @@ String ProjectSettings::get_resource_path() const { return resource_path; } -String ProjectSettings::get_safe_project_name() const { - String safe_name = OS::get_singleton()->get_safe_dir_name(get("application/config/name")); - if (safe_name.is_empty()) { - safe_name = "UnnamedProject"; - } - return safe_name; -} - String ProjectSettings::get_imported_files_path() const { return get_project_data_path().path_join("imported"); } @@ -930,10 +922,26 @@ Error ProjectSettings::_save_settings_text(const String &p_file, const RBMap dir = DirAccess::open(p_root_dir); + + dir->list_dir_begin(); + String file_name = dir->_get_next(); + while (file_name != "") { + if (!dir->current_is_dir() && file_name.get_extension() == "csproj") { + return true; + } + file_name = dir->_get_next(); + } + + return false; +} +#endif // TOOLS_ENABLED + Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_custom, const Vector &p_custom_features, bool p_merge_with_current) { ERR_FAIL_COND_V_MSG(p_path.is_empty(), ERR_INVALID_PARAMETER, "Project settings save path cannot be empty."); @@ -952,7 +960,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust } } // Check for the existence of a csproj file. - if (FileAccess::exists(get_resource_path().path_join(get_safe_project_name() + ".csproj"))) { + if (_csproj_exists(get_resource_path())) { // If there is a csproj file, add the C# feature if it doesn't already exist. if (!project_features.has("C#")) { project_features.append("C#"); diff --git a/core/config/project_settings.h b/core/config/project_settings.h index a0249ef2673..b89e6694b0c 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -166,7 +166,6 @@ public: String get_project_data_dir_name() const; String get_project_data_path() const; String get_resource_path() const; - String get_safe_project_name() const; String get_imported_files_path() const; static ProjectSettings *get_singleton(); diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 05595e7e452..28331288d30 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -62,6 +62,7 @@ #include "signal_awaiter_utils.h" #include "utils/macros.h" #include "utils/naming_utils.h" +#include "utils/path_utils.h" #include "utils/string_utils.h" #define CACHED_STRING_NAME(m_var) (CSharpLanguage::get_singleton()->get_string_names().m_var) @@ -740,11 +741,7 @@ bool CSharpLanguage::is_assembly_reloading_needed() { return false; // Already up to date } } else { - String assembly_name = GLOBAL_GET("dotnet/project/assembly_name"); - - if (assembly_name.is_empty()) { - assembly_name = ProjectSettings::get_singleton()->get_safe_project_name(); - } + String assembly_name = path::get_csharp_project_name(); assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir() .path_join(assembly_name + ".dll"); diff --git a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs index 7c5502814f6..d86a77d222d 100644 --- a/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs +++ b/modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs @@ -57,27 +57,5 @@ namespace GodotTools.Core path.StartsWith("\\", StringComparison.Ordinal) || path.StartsWith(_driveRoot, StringComparison.Ordinal); } - - public static string ToSafeDirName(this string dirName, bool allowDirSeparator = false) - { - var invalidChars = new List { ":", "*", "?", "\"", "<", ">", "|" }; - - if (allowDirSeparator) - { - // Directory separators are allowed, but disallow ".." to avoid going up the filesystem - invalidChars.Add(".."); - } - else - { - invalidChars.Add("/"); - } - - string safeDirName = dirName.Replace("\\", "/").Trim(); - - foreach (string invalidChar in invalidChars) - safeDirName = safeDirName.Replace(invalidChar, "-"); - - return safeDirName; - } } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs index 27c2676dd00..ea2d14958b3 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs @@ -151,7 +151,7 @@ namespace GodotTools.Export string ridOS = DetermineRuntimeIdentifierOS(platform); string ridArch = DetermineRuntimeIdentifierArch(arch); string runtimeIdentifier = $"{ridOS}-{ridArch}"; - string projectDataDirName = $"{DetermineDataDirNameForProject()}_{arch}"; + string projectDataDirName = $"data_{GodotSharpDirs.CSharpProjectName}_{arch}"; if (platform == OS.Platforms.MacOS) { projectDataDirName = Path.Combine("Contents", "Resources", projectDataDirName); @@ -258,12 +258,5 @@ namespace GodotTools.Export platform = null; return false; } - - private static string DetermineDataDirNameForProject() - { - string appName = (string)ProjectSettings.GetSetting("application/config/name"); - string appNameSafe = appName.ToSafeDirName(); - return $"data_{appNameSafe}"; - } } } diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs index fb68fcbae69..55b413453d5 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs @@ -48,21 +48,23 @@ namespace GodotTools.Internals } } + + public static string CSharpProjectName + { + get + { + Internal.godot_icall_GodotSharpDirs_CSharpProjectName(out godot_string dest); + using (dest) + return Marshaling.ConvertStringToManaged(dest); + } + } + public static void DetermineProjectLocation() { - static string DetermineProjectName() - { - string projectAssemblyName = (string)ProjectSettings.GetSetting("application/config/name"); - projectAssemblyName = projectAssemblyName.ToSafeDirName(); - if (string.IsNullOrEmpty(projectAssemblyName)) - projectAssemblyName = "UnnamedProject"; - return projectAssemblyName; - } - _projectAssemblyName = (string)ProjectSettings.GetSetting("dotnet/project/assembly_name"); if (string.IsNullOrEmpty(_projectAssemblyName)) { - _projectAssemblyName = DetermineProjectName(); + _projectAssemblyName = CSharpProjectName; ProjectSettings.SetSetting("dotnet/project/assembly_name", _projectAssemblyName); } diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs index fd810996f7b..3ea11750b78 100644 --- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs +++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs @@ -101,6 +101,8 @@ namespace GodotTools.Internals public static partial void godot_icall_GodotSharpDirs_DataEditorToolsDir(out godot_string r_dest); + public static partial void godot_icall_GodotSharpDirs_CSharpProjectName(out godot_string r_dest); + public static partial void godot_icall_EditorProgress_Create(in godot_string task, in godot_string label, int amount, bool canCancel); diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp index 34b9974d102..527543bb5ef 100644 --- a/modules/mono/editor/editor_internal_calls.cpp +++ b/modules/mono/editor/editor_internal_calls.cpp @@ -49,6 +49,7 @@ #include "../csharp_script.h" #include "../godotsharp_dirs.h" #include "../utils/macos_utils.h" +#include "../utils/path_utils.h" #include "code_completion.h" #include "../interop_types.h" @@ -81,6 +82,10 @@ void godot_icall_GodotSharpDirs_DataEditorToolsDir(godot_string *r_dest) { #endif } +void godot_icall_GodotSharpDirs_CSharpProjectName(godot_string *r_dest) { + memnew_placement(r_dest, String(path::get_csharp_project_name())); +} + void godot_icall_EditorProgress_Create(const godot_string *p_task, const godot_string *p_label, int32_t p_amount, bool p_can_cancel) { String task = *reinterpret_cast(p_task); String label = *reinterpret_cast(p_label); @@ -231,6 +236,7 @@ static const void *unmanaged_callbacks[]{ (void *)godot_icall_GodotSharpDirs_MonoUserDir, (void *)godot_icall_GodotSharpDirs_BuildLogsDirs, (void *)godot_icall_GodotSharpDirs_DataEditorToolsDir, + (void *)godot_icall_GodotSharpDirs_CSharpProjectName, (void *)godot_icall_EditorProgress_Create, (void *)godot_icall_EditorProgress_Dispose, (void *)godot_icall_EditorProgress_Step, diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index bff7d04b551..9b487992b5c 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -44,6 +44,7 @@ #endif #include "mono_gd/gd_mono.h" +#include "utils/path_utils.h" namespace GodotSharpDirs { @@ -139,8 +140,7 @@ private: api_assemblies_dir = api_assemblies_base_dir.path_join(GDMono::get_expected_api_build_config()); #else // TOOLS_ENABLED String arch = Engine::get_singleton()->get_architecture_name(); - String appname = GLOBAL_GET("application/config/name"); - String appname_safe = OS::get_singleton()->get_safe_dir_name(appname); + String appname_safe = path::get_csharp_project_name(); String data_dir_root = exe_dir.path_join("data_" + appname_safe + "_" + arch); if (!DirAccess::exists(data_dir_root)) { data_dir_root = exe_dir.path_join("data_Godot_" + arch); diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp index 97bf49271af..0aef45c128e 100644 --- a/modules/mono/mono_gd/gd_mono.cpp +++ b/modules/mono/mono_gd/gd_mono.cpp @@ -293,20 +293,10 @@ godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime return godot_plugins_initialize; } #else -static String get_assembly_name() { - String assembly_name = GLOBAL_GET("dotnet/project/assembly_name"); - - if (assembly_name.is_empty()) { - assembly_name = ProjectSettings::get_singleton()->get_safe_project_name(); - } - - return assembly_name; -} - godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime_initialized) { godot_plugins_initialize_fn godot_plugins_initialize = nullptr; - String assembly_name = get_assembly_name(); + String assembly_name = path::get_csharp_project_name(); HostFxrCharString assembly_path = str_to_hostfxr(GodotSharpDirs::get_api_assemblies_dir() .path_join(assembly_name + ".dll")); @@ -331,7 +321,7 @@ godot_plugins_initialize_fn initialize_hostfxr_and_godot_plugins(bool &r_runtime } godot_plugins_initialize_fn try_load_native_aot_library(void *&r_aot_dll_handle) { - String assembly_name = get_assembly_name(); + String assembly_name = path::get_csharp_project_name(); #if defined(WINDOWS_ENABLED) String native_aot_so_path = GodotSharpDirs::get_api_assemblies_dir().path_join(assembly_name + ".dll"); @@ -476,11 +466,7 @@ void GDMono::_init_godot_api_hashes() { #ifdef TOOLS_ENABLED bool GDMono::_load_project_assembly() { - String assembly_name = GLOBAL_GET("dotnet/project/assembly_name"); - - if (assembly_name.is_empty()) { - assembly_name = ProjectSettings::get_singleton()->get_safe_project_name(); - } + String assembly_name = path::get_csharp_project_name(); String assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir() .path_join(assembly_name + ".dll"); diff --git a/modules/mono/utils/path_utils.cpp b/modules/mono/utils/path_utils.cpp index b5a3816ed7d..7b3f2127343 100644 --- a/modules/mono/utils/path_utils.cpp +++ b/modules/mono/utils/path_utils.cpp @@ -231,4 +231,27 @@ String relative_to(const String &p_path, const String &p_relative_to) { return relative_to_impl(path_abs_norm, relative_to_abs_norm); } + +String get_csharp_project_name() { + String name = ProjectSettings::get_singleton()->get_setting_with_override("dotnet/project/assembly_name"); + if (name.is_empty()) { + name = ProjectSettings::get_singleton()->get_setting_with_override("application/config/name"); + Vector invalid_chars = Vector({ // + // Windows reserved filename chars. + ":", "*", "?", "\"", "<", ">", "|", + // Directory separators. + "/", "\\", + // Other chars that have been found to break assembly loading. + ";", "'", "=", "," }); + name = name.strip_edges(); + for (int i = 0; i < invalid_chars.size(); i++) { + name = name.replace(invalid_chars[i], "-"); + } + } + if (name.is_empty()) { + name = "UnnamedProject"; + } + return name; +} + } // namespace path diff --git a/modules/mono/utils/path_utils.h b/modules/mono/utils/path_utils.h index 25d130fc58e..fc043c3f159 100644 --- a/modules/mono/utils/path_utils.h +++ b/modules/mono/utils/path_utils.h @@ -31,7 +31,6 @@ #ifndef MONO_PATH_UTILS_H #define MONO_PATH_UTILS_H -#include "core/string/string_builder.h" #include "core/string/ustring.h" namespace path { @@ -58,6 +57,8 @@ String abspath(const String &p_path); String realpath(const String &p_path); String relative_to(const String &p_path, const String &p_relative_to); + +String get_csharp_project_name(); } // namespace path #endif // MONO_PATH_UTILS_H