mirror of
				https://github.com/godotengine/godot.git
				synced 2025-11-04 07:31:16 +00:00 
			
		
		
		
	Merge pull request #30292 from neikeq/android_fixes
Mono: Android build and shared libraries fixes
This commit is contained in:
		
						commit
						e1478b7a87
					
				
					 12 changed files with 285 additions and 49 deletions
				
			
		
							
								
								
									
										69
									
								
								modules/mono/build_scripts/make_android_mono_config.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								modules/mono/build_scripts/make_android_mono_config.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,69 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def generate_compressed_config(config_src, output_dir):
 | 
				
			||||||
 | 
					    import os.path
 | 
				
			||||||
 | 
					    from compat import byte_to_str
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Header file
 | 
				
			||||||
 | 
					    with open(os.path.join(output_dir, 'android_mono_config.gen.h'), 'w') as header:
 | 
				
			||||||
 | 
					        header.write('''/* THIS FILE IS GENERATED DO NOT EDIT */
 | 
				
			||||||
 | 
					#ifndef ANDROID_MONO_CONFIG_GEN_H
 | 
				
			||||||
 | 
					#define ANDROID_MONO_CONFIG_GEN_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef ANDROID_ENABLED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "core/ustring.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					String get_godot_android_mono_config();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // ANDROID_ENABLED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // ANDROID_MONO_CONFIG_GEN_H
 | 
				
			||||||
 | 
					''')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Source file
 | 
				
			||||||
 | 
					    with open(os.path.join(output_dir, 'android_mono_config.gen.cpp'), 'w') as cpp:
 | 
				
			||||||
 | 
					        with open(config_src, 'rb') as f:
 | 
				
			||||||
 | 
					            buf = f.read()
 | 
				
			||||||
 | 
					            decompr_size = len(buf)
 | 
				
			||||||
 | 
					            import zlib
 | 
				
			||||||
 | 
					            buf = zlib.compress(buf)
 | 
				
			||||||
 | 
					            compr_size = len(buf)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            bytes_seq_str = ''
 | 
				
			||||||
 | 
					            for i, buf_idx in enumerate(range(compr_size)):
 | 
				
			||||||
 | 
					                if i > 0:
 | 
				
			||||||
 | 
					                    bytes_seq_str += ', '
 | 
				
			||||||
 | 
					                bytes_seq_str += byte_to_str(buf[buf_idx])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            cpp.write('''/* THIS FILE IS GENERATED DO NOT EDIT */
 | 
				
			||||||
 | 
					#include "android_mono_config.gen.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef ANDROID_ENABLED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "core/io/compression.h"
 | 
				
			||||||
 | 
					#include "core/pool_vector.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// config
 | 
				
			||||||
 | 
					static const int config_compressed_size = %d;
 | 
				
			||||||
 | 
					static const int config_uncompressed_size = %d;
 | 
				
			||||||
 | 
					static const unsigned char config_compressed_data[] = { %s };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					String get_godot_android_mono_config() {
 | 
				
			||||||
 | 
						PoolVector<uint8_t> data;
 | 
				
			||||||
 | 
						data.resize(config_uncompressed_size);
 | 
				
			||||||
 | 
						PoolVector<uint8_t>::Write w = data.write();
 | 
				
			||||||
 | 
						Compression::decompress(w.ptr(), config_uncompressed_size, config_compressed_data,
 | 
				
			||||||
 | 
								config_compressed_size, Compression::MODE_DEFLATE);
 | 
				
			||||||
 | 
						String s;
 | 
				
			||||||
 | 
						if (s.parse_utf8((const char *)w.ptr(), data.size())) {
 | 
				
			||||||
 | 
							ERR_FAIL_V(String());
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // ANDROID_ENABLED
 | 
				
			||||||
 | 
					''' % (compr_size, decompr_size, bytes_seq_str))
 | 
				
			||||||
| 
						 | 
					@ -23,30 +23,31 @@ def generate_header(src, dst, version_dst):
 | 
				
			||||||
                latest_mtime = mtime if mtime > latest_mtime else latest_mtime
 | 
					                latest_mtime = mtime if mtime > latest_mtime else latest_mtime
 | 
				
			||||||
                with open(filepath, 'rb') as f:
 | 
					                with open(filepath, 'rb') as f:
 | 
				
			||||||
                    buf = f.read()
 | 
					                    buf = f.read()
 | 
				
			||||||
                    decomp_size = len(buf)
 | 
					                    decompr_size = len(buf)
 | 
				
			||||||
                    import zlib
 | 
					                    import zlib
 | 
				
			||||||
                    buf = zlib.compress(buf)
 | 
					                    buf = zlib.compress(buf)
 | 
				
			||||||
 | 
					                    compr_size = len(buf)
 | 
				
			||||||
                    name = str(cs_file_count)
 | 
					                    name = str(cs_file_count)
 | 
				
			||||||
                    header.write('\n')
 | 
					                    header.write('\n')
 | 
				
			||||||
                    header.write('// ' + filepath_src_rel + '\n')
 | 
					                    header.write('// ' + filepath_src_rel + '\n')
 | 
				
			||||||
                    header.write('static const int _cs_' + name + '_compressed_size = ' + str(len(buf)) + ';\n')
 | 
					                    header.write('static const int _cs_' + name + '_compressed_size = ' + str(compr_size) + ';\n')
 | 
				
			||||||
                    header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decomp_size) + ';\n')
 | 
					                    header.write('static const int _cs_' + name + '_uncompressed_size = ' + str(decompr_size) + ';\n')
 | 
				
			||||||
                    header.write('static const unsigned char _cs_' + name + '_compressed[] = { ')
 | 
					                    header.write('static const unsigned char _cs_' + name + '_compressed[] = { ')
 | 
				
			||||||
                    for i, buf_idx in enumerate(range(len(buf))):
 | 
					                    for i, buf_idx in enumerate(range(compr_size)):
 | 
				
			||||||
                        if i > 0:
 | 
					                        if i > 0:
 | 
				
			||||||
                            header.write(', ')
 | 
					                            header.write(', ')
 | 
				
			||||||
                        header.write(byte_to_str(buf[buf_idx]))
 | 
					                        header.write(byte_to_str(buf[buf_idx]))
 | 
				
			||||||
 | 
					                    header.write(' };\n')
 | 
				
			||||||
                    inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \
 | 
					                    inserted_files += '\tr_files.insert("' + filepath_src_rel.replace('\\', '\\\\') + '", ' \
 | 
				
			||||||
                                        'CompressedFile(_cs_' + name + '_compressed_size, ' \
 | 
					                                        'GodotCsCompressedFile(_cs_' + name + '_compressed_size, ' \
 | 
				
			||||||
                                        '_cs_' + name + '_uncompressed_size, ' \
 | 
					                                        '_cs_' + name + '_uncompressed_size, ' \
 | 
				
			||||||
                                        '_cs_' + name + '_compressed));\n'
 | 
					                                        '_cs_' + name + '_compressed));\n'
 | 
				
			||||||
                    header.write(' };\n')
 | 
					        header.write('\nstruct GodotCsCompressedFile\n' '{\n'
 | 
				
			||||||
        header.write('\nstruct CompressedFile\n' '{\n'
 | 
					 | 
				
			||||||
            '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n'
 | 
					            '\tint compressed_size;\n' '\tint uncompressed_size;\n' '\tconst unsigned char* data;\n'
 | 
				
			||||||
            '\n\tCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n'
 | 
					            '\n\tGodotCsCompressedFile(int p_comp_size, int p_uncomp_size, const unsigned char* p_data)\n'
 | 
				
			||||||
            '\t{\n' '\t\tcompressed_size = p_comp_size;\n' '\t\tuncompressed_size = p_uncomp_size;\n'
 | 
					            '\t{\n' '\t\tcompressed_size = p_comp_size;\n' '\t\tuncompressed_size = p_uncomp_size;\n'
 | 
				
			||||||
            '\t\tdata = p_data;\n' '\t}\n' '\n\tCompressedFile() {}\n' '};\n'
 | 
					            '\t\tdata = p_data;\n' '\t}\n' '\n\tGodotCsCompressedFile() {}\n' '};\n'
 | 
				
			||||||
            '\nvoid get_compressed_files(Map<String, CompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n'
 | 
					            '\nvoid get_compressed_files(Map<String, GodotCsCompressedFile>& r_files)\n' '{\n' + inserted_files + '}\n'
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        header.write('\n#endif // TOOLS_ENABLED\n')
 | 
					        header.write('\n#endif // TOOLS_ENABLED\n')
 | 
				
			||||||
        header.write('\n#endif // CS_COMPRESSED_H\n')
 | 
					        header.write('\n#endif // CS_COMPRESSED_H\n')
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,7 +41,7 @@ def copy_file(src_dir, dst_dir, name):
 | 
				
			||||||
    dst_dir = Dir(dst_dir).abspath
 | 
					    dst_dir = Dir(dst_dir).abspath
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if not os.path.isdir(dst_dir):
 | 
					    if not os.path.isdir(dst_dir):
 | 
				
			||||||
        os.mkdir(dst_dir)
 | 
					        os.makedirs(dst_dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    copy(src_path, dst_dir)
 | 
					    copy(src_path, dst_dir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -65,6 +65,10 @@ def configure(env, env_mono):
 | 
				
			||||||
        # TODO: Implement this. We have to add the data directory to the apk, concretely the Api and Tools folders.
 | 
					        # TODO: Implement this. We have to add the data directory to the apk, concretely the Api and Tools folders.
 | 
				
			||||||
        raise RuntimeError('This module does not currently support building for android with tools enabled')
 | 
					        raise RuntimeError('This module does not currently support building for android with tools enabled')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if is_android and mono_static:
 | 
				
			||||||
 | 
					        # When static linking and doing something that requires libmono-native, we get a dlopen error as libmono-native seems to depend on libmonosgen-2.0
 | 
				
			||||||
 | 
					        raise RuntimeError('Linking Mono statically is not currently supported on Android')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (os.getenv('MONO32_PREFIX') or os.getenv('MONO64_PREFIX')) and not mono_prefix:
 | 
					    if (os.getenv('MONO32_PREFIX') or os.getenv('MONO64_PREFIX')) and not mono_prefix:
 | 
				
			||||||
        print("WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the 'mono_prefix' SCons parameter instead")
 | 
					        print("WARNING: The environment variables 'MONO32_PREFIX' and 'MONO64_PREFIX' are deprecated; use the 'mono_prefix' SCons parameter instead")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -188,7 +192,7 @@ def configure(env, env_mono):
 | 
				
			||||||
            if is_apple:
 | 
					            if is_apple:
 | 
				
			||||||
                env.Append(LIBS=['iconv', 'pthread'])
 | 
					                env.Append(LIBS=['iconv', 'pthread'])
 | 
				
			||||||
            elif is_android:
 | 
					            elif is_android:
 | 
				
			||||||
                env.Append(LIBS=['m', 'dl'])
 | 
					                pass # Nothing
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
 | 
					                env.Append(LIBS=['m', 'rt', 'dl', 'pthread'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -236,6 +240,14 @@ def configure(env, env_mono):
 | 
				
			||||||
            mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
 | 
					            mono_root = subprocess.check_output(['pkg-config', 'mono-2', '--variable=prefix']).decode('utf8').strip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        make_template_dir(env, mono_root)
 | 
					        make_template_dir(env, mono_root)
 | 
				
			||||||
 | 
					    elif not tools_enabled and is_android:
 | 
				
			||||||
 | 
					        # Compress Android Mono Config
 | 
				
			||||||
 | 
					        from . import make_android_mono_config
 | 
				
			||||||
 | 
					        config_file_path = os.path.join(mono_root, 'etc', 'mono', 'config')
 | 
				
			||||||
 | 
					        make_android_mono_config.generate_compressed_config(config_file_path, 'mono_gd/')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Copy the required shared libraries
 | 
				
			||||||
 | 
					        copy_mono_shared_libs(env, mono_root, None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if copy_mono_root:
 | 
					    if copy_mono_root:
 | 
				
			||||||
        if not mono_root:
 | 
					        if not mono_root:
 | 
				
			||||||
| 
						 | 
					@ -270,7 +282,6 @@ def make_template_dir(env, mono_root):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # Copy etc/mono/
 | 
					    # Copy etc/mono/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if platform != 'android':
 | 
					 | 
				
			||||||
    template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono')
 | 
					    template_mono_config_dir = os.path.join(template_mono_root_dir, 'etc', 'mono')
 | 
				
			||||||
    copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform'])
 | 
					    copy_mono_etc_dir(mono_root, template_mono_config_dir, env['platform'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -682,19 +682,20 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	GDMonoAssembly *proj_assembly = gdmono->get_project_assembly();
 | 
						GDMonoAssembly *proj_assembly = gdmono->get_project_assembly();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	String name = ProjectSettings::get_singleton()->get("application/config/name");
 | 
						String appname = ProjectSettings::get_singleton()->get("application/config/name");
 | 
				
			||||||
	if (name.empty()) {
 | 
						String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
 | 
				
			||||||
		name = "UnnamedProject";
 | 
						if (appname_safe.empty()) {
 | 
				
			||||||
 | 
							appname_safe = "UnnamedProject";
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	name += ".dll";
 | 
						appname_safe += ".dll";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (proj_assembly) {
 | 
						if (proj_assembly) {
 | 
				
			||||||
		String proj_asm_path = proj_assembly->get_path();
 | 
							String proj_asm_path = proj_assembly->get_path();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!FileAccess::exists(proj_assembly->get_path())) {
 | 
							if (!FileAccess::exists(proj_assembly->get_path())) {
 | 
				
			||||||
			// Maybe it wasn't loaded from the default path, so check this as well
 | 
								// Maybe it wasn't loaded from the default path, so check this as well
 | 
				
			||||||
			proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name);
 | 
								proj_asm_path = GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(appname_safe);
 | 
				
			||||||
			if (!FileAccess::exists(proj_asm_path))
 | 
								if (!FileAccess::exists(proj_asm_path))
 | 
				
			||||||
				return false; // No assembly to load
 | 
									return false; // No assembly to load
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -702,7 +703,7 @@ bool CSharpLanguage::is_assembly_reloading_needed() {
 | 
				
			||||||
		if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time())
 | 
							if (FileAccess::get_modified_time(proj_asm_path) <= proj_assembly->get_modified_time())
 | 
				
			||||||
			return false; // Already up to date
 | 
								return false; // Already up to date
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(name)))
 | 
							if (!FileAccess::exists(GodotSharpDirs::get_res_temp_assemblies_dir().plus_file(appname_safe)))
 | 
				
			||||||
			return false; // No assembly to load
 | 
								return false; // No assembly to load
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -914,12 +914,12 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Generate sources from compressed files
 | 
						// Generate sources from compressed files
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Map<String, CompressedFile> compressed_files;
 | 
						Map<String, GodotCsCompressedFile> compressed_files;
 | 
				
			||||||
	get_compressed_files(compressed_files);
 | 
						get_compressed_files(compressed_files);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (Map<String, CompressedFile>::Element *E = compressed_files.front(); E; E = E->next()) {
 | 
						for (Map<String, GodotCsCompressedFile>::Element *E = compressed_files.front(); E; E = E->next()) {
 | 
				
			||||||
		const String &file_name = E->key();
 | 
							const String &file_name = E->key();
 | 
				
			||||||
		const CompressedFile &file_data = E->value();
 | 
							const GodotCsCompressedFile &file_data = E->value();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		String output_file = path_join(core_dir, file_name);
 | 
							String output_file = path_join(core_dir, file_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -63,16 +63,17 @@ bool GodotSharpEditor::_create_project_solution() {
 | 
				
			||||||
	pr.step(TTR("Generating C# project..."));
 | 
						pr.step(TTR("Generating C# project..."));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	String path = OS::get_singleton()->get_resource_dir();
 | 
						String path = OS::get_singleton()->get_resource_dir();
 | 
				
			||||||
	String name = ProjectSettings::get_singleton()->get("application/config/name");
 | 
						String appname = ProjectSettings::get_singleton()->get("application/config/name");
 | 
				
			||||||
	if (name.empty()) {
 | 
						String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
 | 
				
			||||||
		name = "UnnamedProject";
 | 
						if (appname_safe.empty()) {
 | 
				
			||||||
 | 
							appname_safe = "UnnamedProject";
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	String guid = CSharpProject::generate_game_project(path, name);
 | 
						String guid = CSharpProject::generate_game_project(path, appname_safe);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (guid.length()) {
 | 
						if (guid.length()) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		DotNetSolution solution(name);
 | 
							DotNetSolution solution(appname_safe);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (!solution.set_path(path)) {
 | 
							if (!solution.set_path(path)) {
 | 
				
			||||||
			show_error_dialog(TTR("Failed to create solution."));
 | 
								show_error_dialog(TTR("Failed to create solution."));
 | 
				
			||||||
| 
						 | 
					@ -81,12 +82,12 @@ bool GodotSharpEditor::_create_project_solution() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		DotNetSolution::ProjectInfo proj_info;
 | 
							DotNetSolution::ProjectInfo proj_info;
 | 
				
			||||||
		proj_info.guid = guid;
 | 
							proj_info.guid = guid;
 | 
				
			||||||
		proj_info.relpath = name + ".csproj";
 | 
							proj_info.relpath = appname_safe + ".csproj";
 | 
				
			||||||
		proj_info.configs.push_back("Debug");
 | 
							proj_info.configs.push_back("Debug");
 | 
				
			||||||
		proj_info.configs.push_back("Release");
 | 
							proj_info.configs.push_back("Release");
 | 
				
			||||||
		proj_info.configs.push_back("Tools");
 | 
							proj_info.configs.push_back("Tools");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		solution.add_new_project(name, proj_info);
 | 
							solution.add_new_project(appname_safe, proj_info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		Error sln_error = solution.save();
 | 
							Error sln_error = solution.save();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -106,13 +106,14 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
 | 
				
			||||||
		Map<String, String> dependencies;
 | 
							Map<String, String> dependencies;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		String project_dll_name = ProjectSettings::get_singleton()->get("application/config/name");
 | 
							String project_dll_name = ProjectSettings::get_singleton()->get("application/config/name");
 | 
				
			||||||
		if (project_dll_name.empty()) {
 | 
							String project_dll_name_safe = OS::get_singleton()->get_safe_dir_name(project_dll_name);
 | 
				
			||||||
			project_dll_name = "UnnamedProject";
 | 
							if (project_dll_name_safe.empty()) {
 | 
				
			||||||
 | 
								project_dll_name_safe = "UnnamedProject";
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		String project_dll_src_dir = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config);
 | 
							String project_dll_src_dir = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config);
 | 
				
			||||||
		String project_dll_src_path = project_dll_src_dir.plus_file(project_dll_name + ".dll");
 | 
							String project_dll_src_path = project_dll_src_dir.plus_file(project_dll_name_safe + ".dll");
 | 
				
			||||||
		dependencies.insert(project_dll_name, project_dll_src_path);
 | 
							dependencies.insert(project_dll_name_safe, project_dll_src_path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain");
 | 
								MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain");
 | 
				
			||||||
| 
						 | 
					@ -122,10 +123,10 @@ void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug
 | 
				
			||||||
			_GDMONO_SCOPE_DOMAIN_(export_domain);
 | 
								_GDMONO_SCOPE_DOMAIN_(export_domain);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			GDMonoAssembly *scripts_assembly = NULL;
 | 
								GDMonoAssembly *scripts_assembly = NULL;
 | 
				
			||||||
			bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name,
 | 
								bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name_safe,
 | 
				
			||||||
					project_dll_src_path, &scripts_assembly, /* refonly: */ true);
 | 
										project_dll_src_path, &scripts_assembly, /* refonly: */ true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ERR_EXPLAIN("Cannot load assembly (refonly): " + project_dll_name);
 | 
								ERR_EXPLAIN("Cannot load assembly (refonly): " + project_dll_name_safe);
 | 
				
			||||||
			ERR_FAIL_COND(!load_success);
 | 
								ERR_FAIL_COND(!load_success);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			Vector<String> search_dirs;
 | 
								Vector<String> search_dirs;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,6 +39,10 @@
 | 
				
			||||||
#include "editor/editor_settings.h"
 | 
					#include "editor/editor_settings.h"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __ANDROID__
 | 
				
			||||||
 | 
					#include "utils/android_utils.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace GodotSharpDirs {
 | 
					namespace GodotSharpDirs {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
String _get_expected_build_config() {
 | 
					String _get_expected_build_config() {
 | 
				
			||||||
| 
						 | 
					@ -129,15 +133,16 @@ private:
 | 
				
			||||||
		mono_solutions_dir = mono_user_dir.plus_file("solutions");
 | 
							mono_solutions_dir = mono_user_dir.plus_file("solutions");
 | 
				
			||||||
		build_logs_dir = mono_user_dir.plus_file("build_logs");
 | 
							build_logs_dir = mono_user_dir.plus_file("build_logs");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		String name = ProjectSettings::get_singleton()->get("application/config/name");
 | 
							String appname = ProjectSettings::get_singleton()->get("application/config/name");
 | 
				
			||||||
		if (name.empty()) {
 | 
							String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
 | 
				
			||||||
			name = "UnnamedProject";
 | 
							if (appname_safe.empty()) {
 | 
				
			||||||
 | 
								appname_safe = "UnnamedProject";
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		String base_path = ProjectSettings::get_singleton()->globalize_path("res://");
 | 
							String base_path = ProjectSettings::get_singleton()->globalize_path("res://");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		sln_filepath = base_path.plus_file(name + ".sln");
 | 
							sln_filepath = base_path.plus_file(appname_safe + ".sln");
 | 
				
			||||||
		csproj_filepath = base_path.plus_file(name + ".csproj");
 | 
							csproj_filepath = base_path.plus_file(appname_safe + ".csproj");
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
 | 
							String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir();
 | 
				
			||||||
| 
						 | 
					@ -150,7 +155,12 @@ private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		String data_mono_root_dir = data_dir_root.plus_file("Mono");
 | 
							String data_mono_root_dir = data_dir_root.plus_file("Mono");
 | 
				
			||||||
		data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
 | 
							data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if __ANDROID__
 | 
				
			||||||
 | 
							data_mono_lib_dir = GDMonoUtils::Android::get_app_native_lib_dir();
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
		data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
 | 
							data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef WINDOWS_ENABLED
 | 
					#ifdef WINDOWS_ENABLED
 | 
				
			||||||
		data_mono_bin_dir = data_mono_root_dir.plus_file("bin");
 | 
							data_mono_bin_dir = data_mono_root_dir.plus_file("bin");
 | 
				
			||||||
| 
						 | 
					@ -173,15 +183,21 @@ private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		String appname = OS::get_singleton()->get_safe_dir_name(ProjectSettings::get_singleton()->get("application/config/name"));
 | 
							String appname = ProjectSettings::get_singleton()->get("application/config/name");
 | 
				
			||||||
		String data_dir_root = exe_dir.plus_file("data_" + appname);
 | 
							String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
 | 
				
			||||||
 | 
							String data_dir_root = exe_dir.plus_file("data_" + appname_safe);
 | 
				
			||||||
		if (!DirAccess::exists(data_dir_root)) {
 | 
							if (!DirAccess::exists(data_dir_root)) {
 | 
				
			||||||
			data_dir_root = exe_dir.plus_file("data_Godot");
 | 
								data_dir_root = exe_dir.plus_file("data_Godot");
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		String data_mono_root_dir = data_dir_root.plus_file("Mono");
 | 
							String data_mono_root_dir = data_dir_root.plus_file("Mono");
 | 
				
			||||||
		data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
 | 
							data_mono_etc_dir = data_mono_root_dir.plus_file("etc");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if __ANDROID__
 | 
				
			||||||
 | 
							data_mono_lib_dir = GDMonoUtils::Android::get_app_native_lib_dir();
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
		data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
 | 
							data_mono_lib_dir = data_mono_root_dir.plus_file("lib");
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef WINDOWS_ENABLED
 | 
					#ifdef WINDOWS_ENABLED
 | 
				
			||||||
		data_mono_bin_dir = data_mono_root_dir.plus_file("bin");
 | 
							data_mono_bin_dir = data_mono_root_dir.plus_file("bin");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -56,6 +56,10 @@
 | 
				
			||||||
#include "main/main.h"
 | 
					#include "main/main.h"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef ANDROID_ENABLED
 | 
				
			||||||
 | 
					#include "android_mono_config.gen.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define OUT_OF_SYNC_ERR_MESSAGE(m_assembly_name) "The assembly '" m_assembly_name "' is out of sync. "                    \
 | 
					#define OUT_OF_SYNC_ERR_MESSAGE(m_assembly_name) "The assembly '" m_assembly_name "' is out of sync. "                    \
 | 
				
			||||||
												 "This error is expected if you just upgraded to a newer Godot version. " \
 | 
																	 "This error is expected if you just upgraded to a newer Godot version. " \
 | 
				
			||||||
												 "Building the project will update the assembly to the correct version."
 | 
																	 "Building the project will update the assembly to the correct version."
 | 
				
			||||||
| 
						 | 
					@ -287,7 +291,11 @@ void GDMono::initialize() {
 | 
				
			||||||
	gdmono_debug_init();
 | 
						gdmono_debug_init();
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef ANDROID_ENABLED
 | 
				
			||||||
 | 
						mono_config_parse_memory(get_godot_android_mono_config().utf8().get_data());
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
	mono_config_parse(NULL);
 | 
						mono_config_parse(NULL);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL);
 | 
						mono_install_unhandled_exception_hook(&unhandled_exception_hook, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -651,12 +659,13 @@ bool GDMono::_load_project_assembly() {
 | 
				
			||||||
	if (project_assembly)
 | 
						if (project_assembly)
 | 
				
			||||||
		return true;
 | 
							return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	String name = ProjectSettings::get_singleton()->get("application/config/name");
 | 
						String appname = ProjectSettings::get_singleton()->get("application/config/name");
 | 
				
			||||||
	if (name.empty()) {
 | 
						String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
 | 
				
			||||||
		name = "UnnamedProject";
 | 
						if (appname_safe.empty()) {
 | 
				
			||||||
 | 
							appname_safe = "UnnamedProject";
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool success = load_assembly(name, &project_assembly);
 | 
						bool success = load_assembly(appname_safe, &project_assembly);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (success) {
 | 
						if (success) {
 | 
				
			||||||
		mono_assembly_set_main(project_assembly->get_assembly());
 | 
							mono_assembly_set_main(project_assembly->get_assembly());
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -270,7 +270,18 @@ Error GDMonoAssembly::load(bool p_refonly) {
 | 
				
			||||||
	Vector<uint8_t> data = FileAccess::get_file_as_array(path);
 | 
						Vector<uint8_t> data = FileAccess::get_file_as_array(path);
 | 
				
			||||||
	ERR_FAIL_COND_V(data.empty(), ERR_FILE_CANT_READ);
 | 
						ERR_FAIL_COND_V(data.empty(), ERR_FILE_CANT_READ);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	String image_filename = ProjectSettings::get_singleton()->globalize_path(path);
 | 
						String image_filename;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef ANDROID_ENABLED
 | 
				
			||||||
 | 
						if (path.begins_with("res://")) {
 | 
				
			||||||
 | 
							image_filename = path.substr(6, path.length());
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							image_filename = ProjectSettings::get_singleton()->globalize_path(path);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						// FIXME: globalize_path does not work on exported games
 | 
				
			||||||
 | 
						image_filename = ProjectSettings::get_singleton()->globalize_path(path);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	MonoImageOpenStatus status = MONO_IMAGE_OK;
 | 
						MonoImageOpenStatus status = MONO_IMAGE_OK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										68
									
								
								modules/mono/utils/android_utils.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								modules/mono/utils/android_utils.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					/*************************************************************************/
 | 
				
			||||||
 | 
					/*  android_utils.cpp                                                    */
 | 
				
			||||||
 | 
					/*************************************************************************/
 | 
				
			||||||
 | 
					/*                       This file is part of:                           */
 | 
				
			||||||
 | 
					/*                           GODOT ENGINE                                */
 | 
				
			||||||
 | 
					/*                      https://godotengine.org                          */
 | 
				
			||||||
 | 
					/*************************************************************************/
 | 
				
			||||||
 | 
					/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
 | 
				
			||||||
 | 
					/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
 | 
				
			||||||
 | 
					/*                                                                       */
 | 
				
			||||||
 | 
					/* Permission is hereby granted, free of charge, to any person obtaining */
 | 
				
			||||||
 | 
					/* a copy of this software and associated documentation files (the       */
 | 
				
			||||||
 | 
					/* "Software"), to deal in the Software without restriction, including   */
 | 
				
			||||||
 | 
					/* without limitation the rights to use, copy, modify, merge, publish,   */
 | 
				
			||||||
 | 
					/* distribute, sublicense, and/or sell copies of the Software, and to    */
 | 
				
			||||||
 | 
					/* permit persons to whom the Software is furnished to do so, subject to */
 | 
				
			||||||
 | 
					/* the following conditions:                                             */
 | 
				
			||||||
 | 
					/*                                                                       */
 | 
				
			||||||
 | 
					/* The above copyright notice and this permission notice shall be        */
 | 
				
			||||||
 | 
					/* included in all copies or substantial portions of the Software.       */
 | 
				
			||||||
 | 
					/*                                                                       */
 | 
				
			||||||
 | 
					/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
 | 
				
			||||||
 | 
					/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
 | 
				
			||||||
 | 
					/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
 | 
				
			||||||
 | 
					/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
 | 
				
			||||||
 | 
					/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
 | 
				
			||||||
 | 
					/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
 | 
				
			||||||
 | 
					/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 | 
				
			||||||
 | 
					/*************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "android_utils.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __ANDROID__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "platform/android/thread_jandroid.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GDMonoUtils {
 | 
				
			||||||
 | 
					namespace Android {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					String get_app_native_lib_dir() {
 | 
				
			||||||
 | 
						JNIEnv *env = ThreadAndroid::get_env();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						jclass activityThreadClass = env->FindClass("android/app/ActivityThread");
 | 
				
			||||||
 | 
						jmethodID currentActivityThread = env->GetStaticMethodID(activityThreadClass, "currentActivityThread", "()Landroid/app/ActivityThread;");
 | 
				
			||||||
 | 
						jobject activityThread = env->CallStaticObjectMethod(activityThreadClass, currentActivityThread);
 | 
				
			||||||
 | 
						jmethodID getApplication = env->GetMethodID(activityThreadClass, "getApplication", "()Landroid/app/Application;");
 | 
				
			||||||
 | 
						jobject ctx = env->CallObjectMethod(activityThread, getApplication);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						jmethodID getApplicationInfo = env->GetMethodID(env->GetObjectClass(ctx), "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
 | 
				
			||||||
 | 
						jobject applicationInfo = env->CallObjectMethod(ctx, getApplicationInfo);
 | 
				
			||||||
 | 
						jfieldID nativeLibraryDirField = env->GetFieldID(env->GetObjectClass(applicationInfo), "nativeLibraryDir", "Ljava/lang/String;");
 | 
				
			||||||
 | 
						jstring nativeLibraryDir = (jstring)env->GetObjectField(applicationInfo, nativeLibraryDirField);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						String result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const char *const nativeLibraryDir_utf8 = env->GetStringUTFChars(nativeLibraryDir, NULL);
 | 
				
			||||||
 | 
						if (nativeLibraryDir_utf8) {
 | 
				
			||||||
 | 
							result.parse_utf8(nativeLibraryDir_utf8);
 | 
				
			||||||
 | 
							env->ReleaseStringUTFChars(nativeLibraryDir, nativeLibraryDir_utf8);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace Android
 | 
				
			||||||
 | 
					} // namespace GDMonoUtils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // __ANDROID__
 | 
				
			||||||
							
								
								
									
										48
									
								
								modules/mono/utils/android_utils.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								modules/mono/utils/android_utils.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,48 @@
 | 
				
			||||||
 | 
					/*************************************************************************/
 | 
				
			||||||
 | 
					/*  android_utils.h                                                      */
 | 
				
			||||||
 | 
					/*************************************************************************/
 | 
				
			||||||
 | 
					/*                       This file is part of:                           */
 | 
				
			||||||
 | 
					/*                           GODOT ENGINE                                */
 | 
				
			||||||
 | 
					/*                      https://godotengine.org                          */
 | 
				
			||||||
 | 
					/*************************************************************************/
 | 
				
			||||||
 | 
					/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
 | 
				
			||||||
 | 
					/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
 | 
				
			||||||
 | 
					/*                                                                       */
 | 
				
			||||||
 | 
					/* Permission is hereby granted, free of charge, to any person obtaining */
 | 
				
			||||||
 | 
					/* a copy of this software and associated documentation files (the       */
 | 
				
			||||||
 | 
					/* "Software"), to deal in the Software without restriction, including   */
 | 
				
			||||||
 | 
					/* without limitation the rights to use, copy, modify, merge, publish,   */
 | 
				
			||||||
 | 
					/* distribute, sublicense, and/or sell copies of the Software, and to    */
 | 
				
			||||||
 | 
					/* permit persons to whom the Software is furnished to do so, subject to */
 | 
				
			||||||
 | 
					/* the following conditions:                                             */
 | 
				
			||||||
 | 
					/*                                                                       */
 | 
				
			||||||
 | 
					/* The above copyright notice and this permission notice shall be        */
 | 
				
			||||||
 | 
					/* included in all copies or substantial portions of the Software.       */
 | 
				
			||||||
 | 
					/*                                                                       */
 | 
				
			||||||
 | 
					/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
 | 
				
			||||||
 | 
					/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
 | 
				
			||||||
 | 
					/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
 | 
				
			||||||
 | 
					/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
 | 
				
			||||||
 | 
					/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
 | 
				
			||||||
 | 
					/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
 | 
				
			||||||
 | 
					/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 | 
				
			||||||
 | 
					/*************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef ANDROID_UTILS_H
 | 
				
			||||||
 | 
					#define ANDROID_UTILS_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __ANDROID__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "core/ustring.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GDMonoUtils {
 | 
				
			||||||
 | 
					namespace Android {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					String get_app_native_lib_dir();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace Android
 | 
				
			||||||
 | 
					} // namespace GDMonoUtils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // __ANDROID__
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif // ANDROID_UTILS_H
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue