Path: blob/master/editor/project_upgrade/project_converter_3_to_4.cpp
21520 views
/**************************************************************************/1/* project_converter_3_to_4.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "project_converter_3_to_4.h"3132#ifndef DISABLE_DEPRECATED3334#include "core/error/error_macros.h"35#include "core/io/dir_access.h"36#include "core/io/file_access.h"37#include "core/object/ref_counted.h"38#include "core/os/keyboard.h"39#include "core/os/time.h"40#include "core/templates/list.h"41#include "editor/project_upgrade/renames_map_3_to_4.h"4243#include "modules/regex/regex.h"4445// Find "OS.set_property(x)", capturing x into $1.46static String make_regex_gds_os_property_set(const String &name_set) {47return String("\\bOS\\.") + name_set + "\\s*\\((.*)\\)";48}49// Find "OS.property = x", capturing x into $1 or $2.50static String make_regex_gds_os_property_assign(const String &name) {51return String("\\bOS\\.") + name + "\\s*=\\s*([^#]+)";52}53// Find "OS.property" OR "OS.get_property()" / "OS.is_property()".54static String make_regex_gds_os_property_get(const String &name, const String &get) {55return String("\\bOS\\.(") + get + "_)?" + name + "(\\s*\\(\\s*\\))?";56}5758class ProjectConverter3To4::RegExContainer {59public:60// Custom GDScript.61RegEx reg_is_empty = RegEx("\\bempty\\(");62RegEx reg_super = RegEx("([\t ])\\.([a-zA-Z_])");63RegEx reg_json_to = RegEx("\\bto_json\\b");64RegEx reg_json_parse = RegEx("([\t ]{0,})([^\n]+)parse_json\\(([^\n]+)");65RegEx reg_json_non_new = RegEx("([\t ]{0,})([^\n]+)JSON\\.parse\\(([^\n]+)");66RegEx reg_json_print = RegEx("\\bJSON\\b\\.print\\(");67RegEx reg_export_simple = RegEx("export[ ]*\\(([a-zA-Z0-9_]+)\\)[ ]*var[ ]+([a-zA-Z0-9_]+)");68RegEx reg_export_typed = RegEx("export[ ]*\\(([a-zA-Z0-9_]+)\\)[ ]*var[ ]+([a-zA-Z0-9_]+)[ ]*:[ ]*[a-zA-Z0-9_]+");69RegEx reg_export_inferred_type = RegEx("export[ ]*\\([a-zA-Z0-9_]+\\)[ ]*var[ ]+([a-zA-Z0-9_]+)[ ]*:[ ]*=");70RegEx reg_export_advanced = RegEx("export[ ]*\\(([^)^\n]+)\\)[ ]*var[ ]+([a-zA-Z0-9_]+)([^\n]+)");71RegEx reg_setget_setget = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+?)[ \t]*setget[ \t]+([a-zA-Z0-9_]+)[ \t]*,[ \t]*([a-zA-Z0-9_]+)");72RegEx reg_setget_set = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+?)[ \t]*setget[ \t]+([a-zA-Z0-9_]+)[ \t]*[,]*[^\n]*$");73RegEx reg_setget_get = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+?)[ \t]*setget[ \t]+,[ \t]*([a-zA-Z0-9_]+)[ \t]*$");74RegEx reg_join = RegEx("([\\(\\)a-zA-Z0-9_]+)\\.join\\(([^\n^\\)]+)\\)");75RegEx reg_image_lock = RegEx("([a-zA-Z0-9_\\.]+)\\.lock\\(\\)");76RegEx reg_image_unlock = RegEx("([a-zA-Z0-9_\\.]+)\\.unlock\\(\\)");77RegEx reg_instantiate = RegEx("\\.instance\\(([^\\)]*)\\)");78// Simple OS properties with getters/setters.79RegEx reg_os_current_screen = RegEx("\\bOS\\.((set_|get_)?)current_screen\\b");80RegEx reg_os_min_window_size = RegEx("\\bOS\\.((set_|get_)?)min_window_size\\b");81RegEx reg_os_max_window_size = RegEx("\\bOS\\.((set_|get_)?)max_window_size\\b");82RegEx reg_os_window_position = RegEx("\\bOS\\.((set_|get_)?)window_position\\b");83RegEx reg_os_window_size = RegEx("\\bOS\\.((set_|get_)?)window_size\\b");84RegEx reg_os_getset_screen_orient = RegEx("\\bOS\\.(s|g)et_screen_orientation\\b");85// OS property getters/setters for non trivial replacements.86RegEx reg_os_set_window_resizable = RegEx(make_regex_gds_os_property_set("set_window_resizable"));87RegEx reg_os_assign_window_resizable = RegEx(make_regex_gds_os_property_assign("window_resizable"));88RegEx reg_os_is_window_resizable = RegEx(make_regex_gds_os_property_get("window_resizable", "is"));89RegEx reg_os_set_fullscreen = RegEx(make_regex_gds_os_property_set("set_window_fullscreen"));90RegEx reg_os_assign_fullscreen = RegEx(make_regex_gds_os_property_assign("window_fullscreen"));91RegEx reg_os_is_fullscreen = RegEx(make_regex_gds_os_property_get("window_fullscreen", "is"));92RegEx reg_os_set_maximized = RegEx(make_regex_gds_os_property_set("set_window_maximized"));93RegEx reg_os_assign_maximized = RegEx(make_regex_gds_os_property_assign("window_maximized"));94RegEx reg_os_is_maximized = RegEx(make_regex_gds_os_property_get("window_maximized", "is"));95RegEx reg_os_set_minimized = RegEx(make_regex_gds_os_property_set("set_window_minimized"));96RegEx reg_os_assign_minimized = RegEx(make_regex_gds_os_property_assign("window_minimized"));97RegEx reg_os_is_minimized = RegEx(make_regex_gds_os_property_get("window_minimized", "is"));98RegEx reg_os_set_vsync = RegEx(make_regex_gds_os_property_set("set_use_vsync"));99RegEx reg_os_assign_vsync = RegEx(make_regex_gds_os_property_assign("vsync_enabled"));100RegEx reg_os_is_vsync = RegEx(make_regex_gds_os_property_get("vsync_enabled", "is"));101// OS properties specific cases & specific replacements.102RegEx reg_os_assign_screen_orient = RegEx("^(\\s*)OS\\.screen_orientation\\s*=\\s*([^#]+)"); // $1 - indent, $2 - value103RegEx reg_os_set_always_on_top = RegEx(make_regex_gds_os_property_set("set_window_always_on_top"));104RegEx reg_os_is_always_on_top = RegEx("\\bOS\\.is_window_always_on_top\\s*\\(.*\\)");105RegEx reg_os_set_borderless = RegEx(make_regex_gds_os_property_set("set_borderless_window"));106RegEx reg_os_get_borderless = RegEx("\\bOS\\.get_borderless_window\\s*\\(\\s*\\)");107RegEx reg_os_screen_orient_enum = RegEx("\\bOS\\.SCREEN_ORIENTATION_(\\w+)\\b"); // $1 - constant suffix108109// GDScript keywords.110RegEx keyword_gdscript_tool = RegEx("^tool");111RegEx keyword_gdscript_export_single = RegEx("^export");112RegEx keyword_gdscript_export_multi = RegEx("([\t]+)export\\b");113RegEx keyword_gdscript_onready = RegEx("^onready");114RegEx keyword_gdscript_remote = RegEx("^remote func");115RegEx keyword_gdscript_remotesync = RegEx("^remotesync func");116RegEx keyword_gdscript_sync = RegEx("^sync func");117RegEx keyword_gdscript_slave = RegEx("^slave func");118RegEx keyword_gdscript_puppet = RegEx("^puppet func");119RegEx keyword_gdscript_puppetsync = RegEx("^puppetsync func");120RegEx keyword_gdscript_master = RegEx("^master func");121RegEx keyword_gdscript_mastersync = RegEx("^mastersync func");122123RegEx gdscript_comment = RegEx("^\\s*#");124RegEx csharp_comment = RegEx("^\\s*\\/\\/");125126// CSharp keywords.127RegEx keyword_csharp_remote = RegEx("\\[Remote(Attribute)?(\\(\\))?\\]");128RegEx keyword_csharp_remotesync = RegEx("\\[(Remote)?Sync(Attribute)?(\\(\\))?\\]");129RegEx keyword_csharp_puppet = RegEx("\\[(Puppet|Slave)(Attribute)?(\\(\\))?\\]");130RegEx keyword_csharp_puppetsync = RegEx("\\[PuppetSync(Attribute)?(\\(\\))?\\]");131RegEx keyword_csharp_master = RegEx("\\[Master(Attribute)?(\\(\\))?\\]");132RegEx keyword_csharp_mastersync = RegEx("\\[MasterSync(Attribute)?(\\(\\))?\\]");133134// Colors.135LocalVector<RegEx *> color_regexes;136LocalVector<String> color_renamed;137138RegEx color_hexadecimal_short_constructor = RegEx("Color\\(\"#?([a-fA-F0-9]{1})([a-fA-F0-9]{3})\\b");139RegEx color_hexadecimal_full_constructor = RegEx("Color\\(\"#?([a-fA-F0-9]{2})([a-fA-F0-9]{6})\\b");140141// Classes.142LocalVector<RegEx *> class_tscn_regexes;143LocalVector<RegEx *> class_gd_regexes;144LocalVector<RegEx *> class_shader_regexes;145146// Keycode.147RegEx input_map_keycode = RegEx("\\b,\"((physical_)?)scancode\":(\\d+)\\b");148149// Button index and joypad axis.150RegEx joypad_button_index = RegEx("\\b,\"button_index\":(\\d+),(\"pressure\":\\d+\\.\\d+,\"pressed\":(false|true))\\b");151RegEx joypad_axis = RegEx("\\b,\"axis\":(\\d+)\\b");152153// Index represents Godot 3's value, entry represents Godot 4 value equivalency.154// i.e: Button4(L1 - Godot3) -> joypad_button_mappings[4]=9 -> Button9(L1 - Godot4).155int joypad_button_mappings[23] = { 0, 1, 2, 3, 9, 10, -1 /*L2*/, -1 /*R2*/, 7, 8, 4, 6, 11, 12, 13, 14, 5, 15, 16, 17, 18, 19, 20 };156// Entries for L2 and R2 are -1 since they match to joypad axes and no longer to joypad buttons in Godot 4.157158// Animation suffixes.159RegEx animation_suffix = RegEx("([\"'])([a-zA-Z0-9_-]+)(-(?:loop|cycle))([\"'])");160161LocalVector<RegEx *> class_regexes;162163RegEx class_temp_tscn = RegEx("\\bTEMP_RENAMED_CLASS.tscn\\b");164RegEx class_temp_gd = RegEx("\\bTEMP_RENAMED_CLASS.gd\\b");165RegEx class_temp_shader = RegEx("\\bTEMP_RENAMED_CLASS.shader\\b");166167LocalVector<String> class_temp_tscn_renames;168LocalVector<String> class_temp_gd_renames;169LocalVector<String> class_temp_shader_renames;170171// Common.172LocalVector<RegEx *> enum_regexes;173LocalVector<RegEx *> gdscript_function_regexes;174LocalVector<RegEx *> project_settings_regexes;175LocalVector<RegEx *> project_godot_regexes;176LocalVector<RegEx *> input_map_regexes;177LocalVector<RegEx *> gdscript_properties_regexes;178LocalVector<RegEx *> gdscript_signals_regexes;179LocalVector<RegEx *> shaders_regexes;180LocalVector<RegEx *> builtin_types_regexes;181LocalVector<RegEx *> theme_override_regexes;182LocalVector<RegEx *> csharp_function_regexes;183LocalVector<RegEx *> csharp_properties_regexes;184LocalVector<RegEx *> csharp_signal_regexes;185186RegExContainer() {187// Common.188{189// Enum.190for (unsigned int current_index = 0; RenamesMap3To4::enum_renames[current_index][0]; current_index++) {191enum_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::enum_renames[current_index][0] + "\\b")));192}193// GDScript functions.194for (unsigned int current_index = 0; RenamesMap3To4::gdscript_function_renames[current_index][0]; current_index++) {195gdscript_function_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::gdscript_function_renames[current_index][0] + "\\b")));196}197// Project Settings in scripts.198for (unsigned int current_index = 0; RenamesMap3To4::project_settings_renames[current_index][0]; current_index++) {199project_settings_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::project_settings_renames[current_index][0] + "\\b")));200}201// Project Settings in project.godot.202for (unsigned int current_index = 0; RenamesMap3To4::project_godot_renames[current_index][0]; current_index++) {203project_godot_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::project_godot_renames[current_index][0] + "\\b")));204}205// Input Map.206for (unsigned int current_index = 0; RenamesMap3To4::input_map_renames[current_index][0]; current_index++) {207input_map_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::input_map_renames[current_index][0] + "\\b")));208}209// GDScript properties.210for (unsigned int current_index = 0; RenamesMap3To4::gdscript_properties_renames[current_index][0]; current_index++) {211gdscript_properties_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::gdscript_properties_renames[current_index][0] + "\\b")));212}213// GDScript Signals.214for (unsigned int current_index = 0; RenamesMap3To4::gdscript_signals_renames[current_index][0]; current_index++) {215gdscript_signals_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::gdscript_signals_renames[current_index][0] + "\\b")));216}217// Shaders.218for (unsigned int current_index = 0; RenamesMap3To4::shaders_renames[current_index][0]; current_index++) {219shaders_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::shaders_renames[current_index][0] + "\\b")));220}221// Builtin types.222for (unsigned int current_index = 0; RenamesMap3To4::builtin_types_renames[current_index][0]; current_index++) {223builtin_types_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::builtin_types_renames[current_index][0] + "\\b")));224}225// Theme overrides.226for (unsigned int current_index = 0; RenamesMap3To4::theme_override_renames[current_index][0]; current_index++) {227theme_override_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::theme_override_renames[current_index][0] + "\\b")));228}229// CSharp function renames.230for (unsigned int current_index = 0; RenamesMap3To4::csharp_function_renames[current_index][0]; current_index++) {231csharp_function_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::csharp_function_renames[current_index][0] + "\\b")));232}233// CSharp properties renames.234for (unsigned int current_index = 0; RenamesMap3To4::csharp_properties_renames[current_index][0]; current_index++) {235csharp_properties_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::csharp_properties_renames[current_index][0] + "\\b")));236}237// CSharp signals renames.238for (unsigned int current_index = 0; RenamesMap3To4::csharp_signals_renames[current_index][0]; current_index++) {239csharp_signal_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::csharp_signals_renames[current_index][0] + "\\b")));240}241}242243// Colors.244{245for (unsigned int current_index = 0; RenamesMap3To4::color_renames[current_index][0]; current_index++) {246color_regexes.push_back(memnew(RegEx(String("\\bColor.") + RenamesMap3To4::color_renames[current_index][0] + "\\b")));247color_renamed.push_back(String("Color.") + RenamesMap3To4::color_renames[current_index][1]);248}249}250// Classes.251{252for (unsigned int current_index = 0; RenamesMap3To4::class_renames[current_index][0]; current_index++) {253const String class_name = RenamesMap3To4::class_renames[current_index][0];254class_tscn_regexes.push_back(memnew(RegEx(String("\\b") + class_name + ".tscn\\b")));255class_gd_regexes.push_back(memnew(RegEx(String("\\b") + class_name + ".gd\\b")));256class_shader_regexes.push_back(memnew(RegEx(String("\\b") + class_name + ".shader\\b")));257258class_regexes.push_back(memnew(RegEx(String("\\b") + class_name + "\\b")));259260class_temp_tscn_renames.push_back(class_name + ".tscn");261class_temp_gd_renames.push_back(class_name + ".gd");262class_temp_shader_renames.push_back(class_name + ".shader");263}264}265}266~RegExContainer() {267for (RegEx *regex : color_regexes) {268memdelete(regex);269}270for (unsigned int i = 0; i < class_tscn_regexes.size(); i++) {271memdelete(class_tscn_regexes[i]);272memdelete(class_gd_regexes[i]);273memdelete(class_shader_regexes[i]);274memdelete(class_regexes[i]);275}276for (RegEx *regex : enum_regexes) {277memdelete(regex);278}279for (RegEx *regex : gdscript_function_regexes) {280memdelete(regex);281}282for (RegEx *regex : project_settings_regexes) {283memdelete(regex);284}285for (RegEx *regex : project_godot_regexes) {286memdelete(regex);287}288for (RegEx *regex : input_map_regexes) {289memdelete(regex);290}291for (RegEx *regex : gdscript_properties_regexes) {292memdelete(regex);293}294for (RegEx *regex : gdscript_signals_regexes) {295memdelete(regex);296}297for (RegEx *regex : shaders_regexes) {298memdelete(regex);299}300for (RegEx *regex : builtin_types_regexes) {301memdelete(regex);302}303for (RegEx *regex : theme_override_regexes) {304memdelete(regex);305}306for (RegEx *regex : csharp_function_regexes) {307memdelete(regex);308}309for (RegEx *regex : csharp_properties_regexes) {310memdelete(regex);311}312for (RegEx *regex : csharp_signal_regexes) {313memdelete(regex);314}315}316};317318ProjectConverter3To4::ProjectConverter3To4(int p_maximum_file_size_kb, int p_maximum_line_length) {319maximum_file_size = p_maximum_file_size_kb * 1024;320maximum_line_length = p_maximum_line_length;321}322323// Function responsible for converting project.324bool ProjectConverter3To4::convert() {325print_line("Starting conversion.");326uint64_t conversion_start_time = Time::get_singleton()->get_ticks_msec();327328RegExContainer reg_container = RegExContainer();329330int cached_maximum_line_length = maximum_line_length;331maximum_line_length = 10000; // Use only for tests bigger value, to not break them.332333ERR_FAIL_COND_V_MSG(!test_array_names(), false, "Cannot start converting due to problems with data in arrays.");334ERR_FAIL_COND_V_MSG(!test_conversion(reg_container), false, "Aborting conversion due to validation tests failing");335336maximum_line_length = cached_maximum_line_length;337338// Checking if folder contains valid Godot 3 project.339// Project should not be converted more than once.340{341String converter_text = "; Project was converted by built-in tool to Godot 4";342343ERR_FAIL_COND_V_MSG(!FileAccess::exists("project.godot"), false, "Current working directory doesn't contain a \"project.godot\" file for a Godot 3 project.");344345Error err = OK;346String project_godot_content = FileAccess::get_file_as_string("project.godot", &err);347348ERR_FAIL_COND_V_MSG(err != OK, false, "Unable to read \"project.godot\".");349ERR_FAIL_COND_V_MSG(project_godot_content.contains(converter_text), false, "Project was already converted with this tool.");350351Ref<FileAccess> file = FileAccess::open("project.godot", FileAccess::WRITE);352ERR_FAIL_COND_V_MSG(file.is_null(), false, "Unable to open \"project.godot\".");353354file->store_string(converter_text + "\n" + project_godot_content);355}356357Vector<String> collected_files = check_for_files();358359uint32_t converted_files = 0;360361// Check file by file.362for (int i = 0; i < collected_files.size(); i++) {363String file_name = collected_files[i];364Vector<SourceLine> source_lines;365uint32_t ignored_lines = 0;366{367Ref<FileAccess> file = FileAccess::open(file_name, FileAccess::READ);368ERR_CONTINUE_MSG(file.is_null(), vformat("Unable to read content of \"%s\".", file_name));369while (!file->eof_reached()) {370String line = file->get_line();371372SourceLine source_line;373source_line.line = line;374source_line.is_comment = reg_container.gdscript_comment.search_all(line).size() > 0 || reg_container.csharp_comment.search_all(line).size() > 0;375source_lines.append(source_line);376}377}378String file_content_before = collect_string_from_vector(source_lines);379uint64_t hash_before = file_content_before.hash();380uint64_t file_size = file_content_before.size();381print_line(vformat("Trying to convert\t%d/%d file - \"%s\" with size - %d KB", i + 1, collected_files.size(), file_name.trim_prefix("res://"), file_size / 1024));382383Vector<String> reason;384bool is_ignored = false;385uint64_t start_time = Time::get_singleton()->get_ticks_msec();386387if (file_name.ends_with(".shader")) {388DirAccess::remove_file_or_error(file_name.trim_prefix("res://"));389file_name = file_name.replace(".shader", ".gdshader");390}391392if (file_size < uint64_t(maximum_file_size)) {393// ".tscn" must work exactly the same as ".gd" files because they may contain built-in Scripts.394if (file_name.ends_with(".gd")) {395fix_tool_declaration(source_lines, reg_container);396397rename_classes(source_lines, reg_container); // Using only specialized function.398399rename_common(RenamesMap3To4::enum_renames, reg_container.enum_regexes, source_lines);400rename_colors(source_lines, reg_container); // Require to additional rename.401402rename_common(RenamesMap3To4::gdscript_function_renames, reg_container.gdscript_function_regexes, source_lines);403rename_gdscript_functions(source_lines, reg_container, false); // Require to additional rename.404405rename_common(RenamesMap3To4::project_settings_renames, reg_container.project_settings_regexes, source_lines);406rename_gdscript_keywords(source_lines, reg_container, false);407rename_common(RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, source_lines);408rename_common(RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, source_lines);409rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, source_lines);410rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, source_lines);411rename_common(RenamesMap3To4::theme_override_renames, reg_container.theme_override_regexes, source_lines);412rename_animation_suffixes(source_lines, reg_container);413414custom_rename(source_lines, "\\.shader", ".gdshader");415416convert_hexadecimal_colors(source_lines, reg_container);417} else if (file_name.ends_with(".tscn")) {418fix_pause_mode(source_lines, reg_container);419420rename_classes(source_lines, reg_container); // Using only specialized function.421422rename_common(RenamesMap3To4::enum_renames, reg_container.enum_regexes, source_lines);423rename_colors(source_lines, reg_container); // Require to do additional renames.424425rename_common(RenamesMap3To4::gdscript_function_renames, reg_container.gdscript_function_regexes, source_lines);426rename_gdscript_functions(source_lines, reg_container, true); // Require to do additional renames.427428rename_common(RenamesMap3To4::project_settings_renames, reg_container.project_settings_regexes, source_lines);429rename_gdscript_keywords(source_lines, reg_container, true);430rename_common(RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, source_lines);431rename_common(RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, source_lines);432rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, source_lines);433rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, source_lines);434rename_common(RenamesMap3To4::theme_override_renames, reg_container.theme_override_regexes, source_lines);435rename_animation_suffixes(source_lines, reg_container);436437custom_rename(source_lines, "\\.shader", ".gdshader");438439convert_hexadecimal_colors(source_lines, reg_container);440} else if (file_name.ends_with(".cs")) { // TODO, C# should use different methods.441rename_classes(source_lines, reg_container); // Using only specialized function.442rename_common(RenamesMap3To4::csharp_function_renames, reg_container.csharp_function_regexes, source_lines);443rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, source_lines);444rename_common(RenamesMap3To4::csharp_properties_renames, reg_container.csharp_properties_regexes, source_lines);445rename_common(RenamesMap3To4::csharp_signals_renames, reg_container.csharp_signal_regexes, source_lines);446rename_csharp_functions(source_lines, reg_container);447rename_csharp_attributes(source_lines, reg_container);448custom_rename(source_lines, "public class ", "public partial class ");449convert_hexadecimal_colors(source_lines, reg_container);450} else if (file_name.ends_with(".gdshader") || file_name.ends_with(".shader")) {451rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, source_lines);452} else if (file_name.ends_with("tres")) {453rename_classes(source_lines, reg_container); // Using only specialized function.454455rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, source_lines);456rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, source_lines);457458custom_rename(source_lines, "\\.shader", ".gdshader");459} else if (file_name.ends_with("project.godot")) {460rename_common(RenamesMap3To4::project_godot_renames, reg_container.project_godot_regexes, source_lines);461rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, source_lines);462rename_input_map_scancode(source_lines, reg_container);463rename_joypad_buttons_and_axes(source_lines, reg_container);464rename_common(RenamesMap3To4::input_map_renames, reg_container.input_map_regexes, source_lines);465custom_rename(source_lines, "config_version=4", "config_version=5");466} else if (file_name.ends_with(".csproj")) {467// TODO468} else if (file_name.ends_with(".import")) {469for (SourceLine &source_line : source_lines) {470String &line = source_line.line;471if (line.contains("nodes/root_type=\"Spatial\"")) {472line = "nodes/root_type=\"Node3D\"";473} else if (line == "importer=\"ogg_vorbis\"") {474line = "importer=\"oggvorbisstr\"";475}476}477} else {478ERR_PRINT(file_name + " is not supported!");479continue;480}481482for (SourceLine &source_line : source_lines) {483if (source_line.is_comment) {484continue;485}486487String &line = source_line.line;488if (uint64_t(line.length()) > maximum_line_length) {489ignored_lines += 1;490}491}492} else {493reason.append(vformat(" ERROR: File has exceeded the maximum size allowed - %d KB", maximum_file_size / 1024));494is_ignored = true;495}496497uint64_t end_time = Time::get_singleton()->get_ticks_msec();498if (is_ignored) {499String end_message = vformat(" Checking file took %d ms.", end_time - start_time);500print_line(end_message);501} else {502String file_content_after = collect_string_from_vector(source_lines);503uint64_t hash_after = file_content_after.hash64();504// Don't need to save file without any changes.505// Save if this is a shader, because it was renamed.506if (hash_before != hash_after || file_name.ends_with(".gdshader")) {507converted_files++;508509Ref<FileAccess> file = FileAccess::open(file_name, FileAccess::WRITE);510ERR_CONTINUE_MSG(file.is_null(), vformat("Unable to apply changes to \"%s\", no writing access.", file_name));511file->store_string(file_content_after);512reason.append(vformat(" File was changed, conversion took %d ms.", end_time - start_time));513} else {514reason.append(vformat(" File was left unchanged, checking took %d ms.", end_time - start_time));515}516if (ignored_lines != 0) {517reason.append(vformat(" Ignored %d lines, because their length exceeds maximum allowed characters - %d.", ignored_lines, maximum_line_length));518}519}520for (int k = 0; k < reason.size(); k++) {521print_line(reason[k]);522}523}524print_line(vformat("Conversion ended - all files(%d), converted files: (%d), not converted files: (%d).", collected_files.size(), converted_files, collected_files.size() - converted_files));525uint64_t conversion_end_time = Time::get_singleton()->get_ticks_msec();526print_line(vformat("Conversion of all files took %10.3f seconds.", (conversion_end_time - conversion_start_time) / 1000.0));527return true;528}529530// Function responsible for validating project conversion.531bool ProjectConverter3To4::validate_conversion() {532print_line("Starting checking if project conversion can be done.");533uint64_t conversion_start_time = Time::get_singleton()->get_ticks_msec();534535RegExContainer reg_container = RegExContainer();536537int cached_maximum_line_length = maximum_line_length;538maximum_line_length = 10000; // To avoid breaking the tests, only use this for the their larger value.539540ERR_FAIL_COND_V_MSG(!test_array_names(), false, "Cannot start converting due to problems with data in arrays.");541ERR_FAIL_COND_V_MSG(!test_conversion(reg_container), false, "Aborting conversion due to validation tests failing");542543maximum_line_length = cached_maximum_line_length;544545// Checking if folder contains valid Godot 3 project.546// Project should not be converted more than once.547{548String conventer_text = "; Project was converted by built-in tool to Godot 4";549550ERR_FAIL_COND_V_MSG(!FileAccess::exists("project.godot"), false, "Current directory doesn't contain any Godot 3 project");551552Error err = OK;553String project_godot_content = FileAccess::get_file_as_string("project.godot", &err);554555ERR_FAIL_COND_V_MSG(err != OK, false, "Failed to read content of \"project.godot\" file.");556ERR_FAIL_COND_V_MSG(project_godot_content.contains(conventer_text), false, "Project already was converted with this tool.");557}558559Vector<String> collected_files = check_for_files();560561uint32_t converted_files = 0;562563// Check file by file.564for (int i = 0; i < collected_files.size(); i++) {565const String &file_name = collected_files[i];566Vector<String> lines;567uint32_t ignored_lines = 0;568uint64_t file_size = 0;569{570Ref<FileAccess> file = FileAccess::open(file_name, FileAccess::READ);571ERR_CONTINUE_MSG(file.is_null(), vformat("Unable to read content of \"%s\".", file_name));572while (!file->eof_reached()) {573String line = file->get_line();574file_size += line.size();575lines.append(line);576}577}578print_line(vformat("Checking for conversion - %d/%d file - \"%s\" with size - %d KB", i + 1, collected_files.size(), file_name.trim_prefix("res://"), file_size / 1024));579580Vector<String> changed_elements;581Vector<String> reason;582bool is_ignored = false;583uint64_t start_time = Time::get_singleton()->get_ticks_msec();584585if (file_name.ends_with(".shader")) {586reason.append("\tFile extension will be renamed from \"shader\" to \"gdshader\".");587}588589if (file_size < uint64_t(maximum_file_size)) {590if (file_name.ends_with(".gd")) {591changed_elements.append_array(check_for_rename_classes(lines, reg_container));592593changed_elements.append_array(check_for_rename_common(RenamesMap3To4::enum_renames, reg_container.enum_regexes, lines));594changed_elements.append_array(check_for_rename_colors(lines, reg_container));595596changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_function_renames, reg_container.gdscript_function_regexes, lines));597changed_elements.append_array(check_for_rename_gdscript_functions(lines, reg_container, false));598599changed_elements.append_array(check_for_rename_common(RenamesMap3To4::project_settings_renames, reg_container.project_settings_regexes, lines));600changed_elements.append_array(check_for_rename_gdscript_keywords(lines, reg_container, false));601changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, lines));602changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, lines));603changed_elements.append_array(check_for_rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, lines));604changed_elements.append_array(check_for_rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, lines));605changed_elements.append_array(check_for_rename_common(RenamesMap3To4::theme_override_renames, reg_container.theme_override_regexes, lines));606changed_elements.append_array(check_for_rename_animation_suffixes(lines, reg_container));607608changed_elements.append_array(check_for_custom_rename(lines, "\\.shader", ".gdshader"));609} else if (file_name.ends_with(".tscn")) {610changed_elements.append_array(check_for_rename_classes(lines, reg_container));611612changed_elements.append_array(check_for_rename_common(RenamesMap3To4::enum_renames, reg_container.enum_regexes, lines));613changed_elements.append_array(check_for_rename_colors(lines, reg_container));614615changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_function_renames, reg_container.gdscript_function_regexes, lines));616changed_elements.append_array(check_for_rename_gdscript_functions(lines, reg_container, true));617618changed_elements.append_array(check_for_rename_common(RenamesMap3To4::project_settings_renames, reg_container.project_settings_regexes, lines));619changed_elements.append_array(check_for_rename_gdscript_keywords(lines, reg_container, true));620changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, lines));621changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, lines));622changed_elements.append_array(check_for_rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, lines));623changed_elements.append_array(check_for_rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, lines));624changed_elements.append_array(check_for_rename_common(RenamesMap3To4::theme_override_renames, reg_container.theme_override_regexes, lines));625changed_elements.append_array(check_for_rename_animation_suffixes(lines, reg_container));626627changed_elements.append_array(check_for_custom_rename(lines, "\\.shader", ".gdshader"));628} else if (file_name.ends_with(".cs")) {629changed_elements.append_array(check_for_rename_classes(lines, reg_container));630changed_elements.append_array(check_for_rename_common(RenamesMap3To4::csharp_function_renames, reg_container.csharp_function_regexes, lines));631changed_elements.append_array(check_for_rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, lines));632changed_elements.append_array(check_for_rename_common(RenamesMap3To4::csharp_properties_renames, reg_container.csharp_properties_regexes, lines));633changed_elements.append_array(check_for_rename_common(RenamesMap3To4::csharp_signals_renames, reg_container.csharp_signal_regexes, lines));634changed_elements.append_array(check_for_rename_csharp_functions(lines, reg_container));635changed_elements.append_array(check_for_rename_csharp_attributes(lines, reg_container));636changed_elements.append_array(check_for_custom_rename(lines, "public class ", "public partial class "));637} else if (file_name.ends_with(".gdshader") || file_name.ends_with(".shader")) {638changed_elements.append_array(check_for_rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, lines));639} else if (file_name.ends_with("tres")) {640changed_elements.append_array(check_for_rename_classes(lines, reg_container));641642changed_elements.append_array(check_for_rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, lines));643changed_elements.append_array(check_for_rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, lines));644645changed_elements.append_array(check_for_custom_rename(lines, "\\.shader", ".gdshader"));646} else if (file_name.ends_with("project.godot")) {647changed_elements.append_array(check_for_rename_common(RenamesMap3To4::project_godot_renames, reg_container.project_godot_regexes, lines));648changed_elements.append_array(check_for_rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, lines));649changed_elements.append_array(check_for_rename_input_map_scancode(lines, reg_container));650changed_elements.append_array(check_for_rename_joypad_buttons_and_axes(lines, reg_container));651changed_elements.append_array(check_for_rename_common(RenamesMap3To4::input_map_renames, reg_container.input_map_regexes, lines));652} else if (file_name.ends_with(".csproj")) {653// TODO654} else {655ERR_PRINT(vformat("\"%s\", is not supported!", file_name));656continue;657}658659for (String &line : lines) {660if (uint64_t(line.length()) > maximum_line_length) {661ignored_lines += 1;662}663}664} else {665reason.append(vformat("\tERROR: File has exceeded the maximum size allowed - %d KB.", maximum_file_size / 1024));666is_ignored = true;667}668669uint64_t end_time = Time::get_singleton()->get_ticks_msec();670String end_message = vformat(" Checking file took %10.3f ms.", (end_time - start_time) / 1000.0);671if (ignored_lines != 0) {672end_message += vformat(" Ignored %d lines, because their length exceeds maximum allowed characters - %d.", ignored_lines, maximum_line_length);673}674print_line(end_message);675676for (int k = 0; k < reason.size(); k++) {677print_line(reason[k]);678}679680if (changed_elements.size() > 0 && !is_ignored) {681converted_files++;682683for (int k = 0; k < changed_elements.size(); k++) {684print_line(String("\t\t") + changed_elements[k]);685}686}687}688689print_line(vformat("Checking for valid conversion ended - all files(%d), files which would be converted(%d), files which would not be converted(%d).", collected_files.size(), converted_files, collected_files.size() - converted_files));690uint64_t conversion_end_time = Time::get_singleton()->get_ticks_msec();691print_line(vformat("Conversion of all files took %10.3f seconds.", (conversion_end_time - conversion_start_time) / 1000.0));692return true;693}694695// Collect files which will be checked, excluding ".txt", ".mp4", ".wav" etc. files.696Vector<String> ProjectConverter3To4::check_for_files() {697Vector<String> collected_files = Vector<String>();698699Vector<String> directories_to_check = Vector<String>();700directories_to_check.push_back("res://");701702while (!directories_to_check.is_empty()) {703String path = directories_to_check.get(directories_to_check.size() - 1); // Is there any pop_back function?704directories_to_check.resize(directories_to_check.size() - 1); // Remove last element705706Ref<DirAccess> dir = DirAccess::open(path);707if (dir.is_valid()) {708dir->set_include_hidden(true);709dir->list_dir_begin();710String current_dir = dir->get_current_dir();711String file_name = dir->_get_next();712713while (file_name != "") {714if (file_name == ".git" || file_name == ".godot") {715file_name = dir->_get_next();716continue;717}718if (dir->current_is_dir()) {719directories_to_check.append(current_dir.path_join(file_name) + "/");720} else {721bool proper_extension = false;722if (file_name.ends_with(".gd") || file_name.ends_with(".shader") || file_name.ends_with(".gdshader") || file_name.ends_with(".tscn") || file_name.ends_with(".tres") || file_name.ends_with(".godot") || file_name.ends_with(".cs") || file_name.ends_with(".csproj") || file_name.ends_with(".import")) {723proper_extension = true;724}725726if (proper_extension) {727collected_files.append(current_dir.path_join(file_name));728}729}730file_name = dir->_get_next();731}732} else {733print_verbose("Failed to open " + path);734}735}736return collected_files;737}738739Vector<SourceLine> ProjectConverter3To4::split_lines(const String &text) {740Vector<String> lines = text.split("\n");741Vector<SourceLine> source_lines;742for (String &line : lines) {743SourceLine source_line;744source_line.line = line;745source_line.is_comment = false;746747source_lines.append(source_line);748}749return source_lines;750}751752// Test expected results of gdscript753bool ProjectConverter3To4::test_conversion_gdscript_builtin(const String &name, const String &expected, void (ProjectConverter3To4::*func)(Vector<SourceLine> &, const RegExContainer &, bool), const String &what, const RegExContainer ®_container, bool builtin_script) {754Vector<SourceLine> got = split_lines(name);755756(this->*func)(got, reg_container, builtin_script);757String got_str = collect_string_from_vector(got);758ERR_FAIL_COND_V_MSG(expected != got_str, false, vformat("Failed to convert %s \"%s\" to \"%s\", got instead \"%s\"", what, name, expected, got_str));759760return true;761}762763bool ProjectConverter3To4::test_conversion_with_regex(const String &name, const String &expected, void (ProjectConverter3To4::*func)(Vector<SourceLine> &, const RegExContainer &), const String &what, const RegExContainer ®_container) {764Vector<SourceLine> got = split_lines(name);765766(this->*func)(got, reg_container);767String got_str = collect_string_from_vector(got);768ERR_FAIL_COND_V_MSG(expected != got_str, false, vformat("Failed to convert %s \"%s\" to \"%s\", got instead \"%s\"", what, name, expected, got_str));769770return true;771}772773bool ProjectConverter3To4::test_conversion_basic(const String &name, const String &expected, const char *array[][2], LocalVector<RegEx *> ®ex_cache, const String &what) {774Vector<SourceLine> got = split_lines(name);775776rename_common(array, regex_cache, got);777String got_str = collect_string_from_vector(got);778ERR_FAIL_COND_V_MSG(expected != got_str, false, vformat("Failed to convert %s \"%s\" to \"%s\", got instead \"%s\"", what, name, expected, got_str));779780return true;781}782783// Validate if conversions are proper.784bool ProjectConverter3To4::test_conversion(RegExContainer ®_container) {785bool valid = true;786787valid = valid && test_conversion_with_regex("tool", "@tool", &ProjectConverter3To4::fix_tool_declaration, "gdscript keyword", reg_container);788valid = valid && test_conversion_with_regex("\n tool", "\n tool", &ProjectConverter3To4::fix_tool_declaration, "gdscript keyword", reg_container);789valid = valid && test_conversion_with_regex("\n\ntool", "@tool\n\n", &ProjectConverter3To4::fix_tool_declaration, "gdscript keyword", reg_container);790791valid = valid && test_conversion_with_regex("pause_mode = 2", "pause_mode = 3", &ProjectConverter3To4::fix_pause_mode, "pause_mode", reg_container);792valid = valid && test_conversion_with_regex("pause_mode = 1", "pause_mode = 1", &ProjectConverter3To4::fix_pause_mode, "pause_mode", reg_container);793valid = valid && test_conversion_with_regex("pause_mode = 3", "pause_mode = 3", &ProjectConverter3To4::fix_pause_mode, "pause_mode", reg_container);794valid = valid && test_conversion_with_regex("somepause_mode = 2", "somepause_mode = 2", &ProjectConverter3To4::fix_pause_mode, "pause_mode", reg_container);795valid = valid && test_conversion_with_regex("pause_mode_ext = 2", "pause_mode_ext = 2", &ProjectConverter3To4::fix_pause_mode, "pause_mode", reg_container);796797valid = valid && test_conversion_basic("TYPE_REAL", "TYPE_FLOAT", RenamesMap3To4::enum_renames, reg_container.enum_regexes, "enum");798799valid = valid && test_conversion_basic("can_instance", "can_instantiate", RenamesMap3To4::gdscript_function_renames, reg_container.gdscript_function_regexes, "gdscript function");800801valid = valid && test_conversion_basic("CanInstance", "CanInstantiate", RenamesMap3To4::csharp_function_renames, reg_container.csharp_function_regexes, "csharp function");802803valid = valid && test_conversion_basic("translation", "position", RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, "gdscript property");804805valid = valid && test_conversion_basic("Translation", "Position", RenamesMap3To4::csharp_properties_renames, reg_container.csharp_properties_regexes, "csharp property");806807valid = valid && test_conversion_basic("NORMALMAP", "NORMAL_MAP", RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, "shader");808809valid = valid && test_conversion_basic("text_entered", "text_submitted", RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, "gdscript signal");810811valid = valid && test_conversion_basic("TextEntered", "TextSubmitted", RenamesMap3To4::csharp_signals_renames, reg_container.csharp_signal_regexes, "csharp signal");812813valid = valid && test_conversion_basic("audio/channel_disable_threshold_db", "audio/buses/channel_disable_threshold_db", RenamesMap3To4::project_settings_renames, reg_container.project_settings_regexes, "project setting");814815valid = valid && test_conversion_basic("\"device\":-1,\"alt\":false,\"shift\":false,\"control\":false,\"meta\":false,\"doubleclick\":false,\"scancode\":0,\"physical_scancode\":16777254,\"script\":null", "\"device\":-1,\"alt_pressed\":false,\"shift_pressed\":false,\"ctrl_pressed\":false,\"meta_pressed\":false,\"double_click\":false,\"keycode\":0,\"physical_keycode\":16777254,\"script\":null", RenamesMap3To4::input_map_renames, reg_container.input_map_regexes, "input map");816817valid = valid && test_conversion_basic("Transform", "Transform3D", RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, "builtin type");818819valid = valid && test_conversion_basic("custom_constants/margin_right", "theme_override_constants/margin_right", RenamesMap3To4::theme_override_renames, reg_container.theme_override_regexes, "theme overrides");820821// Custom Renames.822823valid = valid && test_conversion_with_regex("(Connect(A,B,C,D,E,F,G) != OK):", "(Connect(A, new Callable(B, C), D, E, F, G) != OK):", &ProjectConverter3To4::rename_csharp_functions, "custom rename csharp", reg_container);824valid = valid && test_conversion_with_regex("(Disconnect(A,B,C) != OK):", "(Disconnect(A, new Callable(B, C)) != OK):", &ProjectConverter3To4::rename_csharp_functions, "custom rename csharp", reg_container);825valid = valid && test_conversion_with_regex("(IsConnected(A,B,C) != OK):", "(IsConnected(A, new Callable(B, C)) != OK):", &ProjectConverter3To4::rename_csharp_functions, "custom rename", reg_container);826827valid = valid && test_conversion_with_regex("[Remote]", "[RPC(MultiplayerAPI.RPCMode.AnyPeer)]", &ProjectConverter3To4::rename_csharp_attributes, "custom rename csharp", reg_container);828valid = valid && test_conversion_with_regex("[RemoteSync]", "[RPC(MultiplayerAPI.RPCMode.AnyPeer, CallLocal = true)]", &ProjectConverter3To4::rename_csharp_attributes, "custom rename csharp", reg_container);829valid = valid && test_conversion_with_regex("[Sync]", "[RPC(MultiplayerAPI.RPCMode.AnyPeer, CallLocal = true)]", &ProjectConverter3To4::rename_csharp_attributes, "custom rename csharp", reg_container);830valid = valid && test_conversion_with_regex("[Slave]", "[RPC]", &ProjectConverter3To4::rename_csharp_attributes, "custom rename csharp", reg_container);831valid = valid && test_conversion_with_regex("[Puppet]", "[RPC]", &ProjectConverter3To4::rename_csharp_attributes, "custom rename csharp", reg_container);832valid = valid && test_conversion_with_regex("[PuppetSync]", "[RPC(CallLocal = true)]", &ProjectConverter3To4::rename_csharp_attributes, "custom rename csharp", reg_container);833valid = valid && test_conversion_with_regex("[Master]", "The master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using Multiplayer.GetRemoteSenderId()\n[RPC]", &ProjectConverter3To4::rename_csharp_attributes, "custom rename csharp", reg_container);834valid = valid && test_conversion_with_regex("[MasterSync]", "The master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using Multiplayer.GetRemoteSenderId()\n[RPC(CallLocal = true)]", &ProjectConverter3To4::rename_csharp_attributes, "custom rename csharp", reg_container);835836valid = valid && test_conversion_gdscript_builtin("\tif OS.window_resizable: pass", "\tif (not get_window().unresizable): pass", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);837valid = valid && test_conversion_gdscript_builtin("\tif OS.is_window_resizable(): pass", "\tif (not get_window().unresizable): pass", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);838valid = valid && test_conversion_gdscript_builtin("\tOS.set_window_resizable(Settings.resizable)", "\tget_window().unresizable = not (Settings.resizable)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);839valid = valid && test_conversion_gdscript_builtin("\tOS.window_resizable = Settings.resizable", "\tget_window().unresizable = not (Settings.resizable)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);840841valid = valid && test_conversion_gdscript_builtin("\tif OS.window_fullscreen: pass", "\tif ((get_window().mode == Window.MODE_EXCLUSIVE_FULLSCREEN) or (get_window().mode == Window.MODE_FULLSCREEN)): pass", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);842valid = valid && test_conversion_gdscript_builtin("\tif OS.is_window_fullscreen(): pass", "\tif ((get_window().mode == Window.MODE_EXCLUSIVE_FULLSCREEN) or (get_window().mode == Window.MODE_FULLSCREEN)): pass", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);843valid = valid && test_conversion_gdscript_builtin("\tOS.set_window_fullscreen(Settings.fullscreen)", "\tget_window().mode = Window.MODE_EXCLUSIVE_FULLSCREEN if (Settings.fullscreen) else Window.MODE_WINDOWED", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);844valid = valid && test_conversion_gdscript_builtin("\tOS.window_fullscreen = Settings.fullscreen", "\tget_window().mode = Window.MODE_EXCLUSIVE_FULLSCREEN if (Settings.fullscreen) else Window.MODE_WINDOWED", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);845846valid = valid && test_conversion_gdscript_builtin("\tif OS.window_maximized: pass", "\tif (get_window().mode == Window.MODE_MAXIMIZED): pass", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);847valid = valid && test_conversion_gdscript_builtin("\tif OS.is_window_maximized(): pass", "\tif (get_window().mode == Window.MODE_MAXIMIZED): pass", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);848valid = valid && test_conversion_gdscript_builtin("\tOS.set_window_maximized(Settings.maximized)", "\tget_window().mode = Window.MODE_MAXIMIZED if (Settings.maximized) else Window.MODE_WINDOWED", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);849valid = valid && test_conversion_gdscript_builtin("\tOS.window_maximized = Settings.maximized", "\tget_window().mode = Window.MODE_MAXIMIZED if (Settings.maximized) else Window.MODE_WINDOWED", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);850851valid = valid && test_conversion_gdscript_builtin("\tif OS.window_minimized: pass", "\tif (get_window().mode == Window.MODE_MINIMIZED): pass", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);852valid = valid && test_conversion_gdscript_builtin("\tif OS.is_window_minimized(): pass", "\tif (get_window().mode == Window.MODE_MINIMIZED): pass", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);853valid = valid && test_conversion_gdscript_builtin("\tOS.set_window_minimized(Settings.minimized)", "\tget_window().mode = Window.MODE_MINIMIZED if (Settings.minimized) else Window.MODE_WINDOWED", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);854valid = valid && test_conversion_gdscript_builtin("\tOS.window_minimized = Settings.minimized", "\tget_window().mode = Window.MODE_MINIMIZED if (Settings.minimized) else Window.MODE_WINDOWED", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);855856valid = valid && test_conversion_gdscript_builtin("\tif OS.vsync_enabled: pass", "\tif (DisplayServer.window_get_vsync_mode() != DisplayServer.VSYNC_DISABLED): pass", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);857valid = valid && test_conversion_gdscript_builtin("\tif OS.is_vsync_enabled(): pass", "\tif (DisplayServer.window_get_vsync_mode() != DisplayServer.VSYNC_DISABLED): pass", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);858valid = valid && test_conversion_gdscript_builtin("\tOS.set_use_vsync(Settings.vsync)", "\tDisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED if (Settings.vsync) else DisplayServer.VSYNC_DISABLED)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);859valid = valid && test_conversion_gdscript_builtin("\tOS.vsync_enabled = Settings.vsync", "\tDisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED if (Settings.vsync) else DisplayServer.VSYNC_DISABLED)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);860861valid = valid && test_conversion_gdscript_builtin("\tif OS.screen_orientation = OS.SCREEN_ORIENTATION_VERTICAL: pass", "\tif DisplayServer.screen_get_orientation() = DisplayServer.SCREEN_VERTICAL: pass", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);862valid = valid && test_conversion_gdscript_builtin("\tif OS.get_screen_orientation() = OS.SCREEN_ORIENTATION_LANDSCAPE: pass", "\tif DisplayServer.screen_get_orientation() = DisplayServer.SCREEN_LANDSCAPE: pass", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);863valid = valid && test_conversion_gdscript_builtin("\tOS.set_screen_orientation(Settings.orient)", "\tDisplayServer.screen_set_orientation(Settings.orient)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);864valid = valid && test_conversion_gdscript_builtin("\tOS.screen_orientation = Settings.orient", "\tDisplayServer.screen_set_orientation(Settings.orient)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);865866valid = valid && test_conversion_gdscript_builtin("\tif OS.is_window_always_on_top(): pass", "\tif get_window().always_on_top: pass", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);867valid = valid && test_conversion_gdscript_builtin("\tOS.set_window_always_on_top(Settings.alwaystop)", "\tget_window().always_on_top = (Settings.alwaystop)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);868869valid = valid && test_conversion_gdscript_builtin("\tif OS.get_borderless_window(): pass", "\tif get_window().borderless: pass", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);870valid = valid && test_conversion_gdscript_builtin("\tOS.set_borderless_window(Settings.borderless)", "\tget_window().borderless = (Settings.borderless)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);871872valid = valid && test_conversion_gdscript_builtin("\tvar aa = roman(r.move_and_slide( a, b, c, d, e, f )) # Roman", "\tr.set_velocity(a)\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `f`\n\tr.move_and_slide()\n\tvar aa = roman(r.velocity) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);873valid = valid && test_conversion_gdscript_builtin("\tmove_and_slide( a, b, c, d, e, f ) # Roman", "\tset_velocity(a)\n\tset_up_direction(b)\n\tset_floor_stop_on_slope_enabled(c)\n\tset_max_slides(d)\n\tset_floor_max_angle(e)\n\t# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `f`\n\tmove_and_slide() # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);874valid = valid && test_conversion_gdscript_builtin("\tvar aa = roman(r.move_and_slide_with_snap( a, g, b, c, d, e, f )) # Roman", "\tr.set_velocity(a)\n\t# TODOConverter3To4 looks that snap in Godot 4 is float, not vector like in Godot 3 - previous value `g`\n\tr.set_up_direction(b)\n\tr.set_floor_stop_on_slope_enabled(c)\n\tr.set_max_slides(d)\n\tr.set_floor_max_angle(e)\n\t# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `f`\n\tr.move_and_slide()\n\tvar aa = roman(r.velocity) # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);875valid = valid && test_conversion_gdscript_builtin("\tmove_and_slide_with_snap( a, g, b, c, d, e, f ) # Roman", "\tset_velocity(a)\n\t# TODOConverter3To4 looks that snap in Godot 4 is float, not vector like in Godot 3 - previous value `g`\n\tset_up_direction(b)\n\tset_floor_stop_on_slope_enabled(c)\n\tset_max_slides(d)\n\tset_floor_max_angle(e)\n\t# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `f`\n\tmove_and_slide() # Roman", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);876877valid = valid && test_conversion_gdscript_builtin("remove_and_slide(a,b,c,d,e,f)", "remove_and_slide(a,b,c,d,e,f)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);878879valid = valid && test_conversion_gdscript_builtin("list_dir_begin( a , b )", "list_dir_begin() # TODOConverter3To4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);880valid = valid && test_conversion_gdscript_builtin("list_dir_begin( a )", "list_dir_begin() # TODOConverter3To4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);881valid = valid && test_conversion_gdscript_builtin("list_dir_begin( )", "list_dir_begin() # TODOConverter3To4 fill missing arguments https://github.com/godotengine/godot/pull/40547", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);882883valid = valid && test_conversion_gdscript_builtin("sort_custom( a , b )", "sort_custom(Callable(a, b))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);884885valid = valid && test_conversion_gdscript_builtin("func c(var a, var b)", "func c(a, b)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);886887valid = valid && test_conversion_gdscript_builtin("draw_line(1, 2, 3, 4, 5)", "draw_line(1, 2, 3, 4)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);888889valid = valid && test_conversion_gdscript_builtin("\timage.lock()", "\tfalse # image.lock() # TODOConverter3To4, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);890valid = valid && test_conversion_gdscript_builtin("\timage.unlock()", "\tfalse # image.unlock() # TODOConverter3To4, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);891valid = valid && test_conversion_gdscript_builtin("\troman.image.unlock()", "\tfalse # roman.image.unlock() # TODOConverter3To4, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);892valid = valid && test_conversion_gdscript_builtin("\tmtx.lock()", "\tmtx.lock()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);893valid = valid && test_conversion_gdscript_builtin("\tmutex.unlock()", "\tmutex.unlock()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);894895valid = valid && test_conversion_with_regex("extends CSGBox", "extends CSGBox3D", &ProjectConverter3To4::rename_classes, "classes", reg_container);896valid = valid && test_conversion_with_regex("CSGBox", "CSGBox3D", &ProjectConverter3To4::rename_classes, "classes", reg_container);897valid = valid && test_conversion_with_regex("Spatial", "Node3D", &ProjectConverter3To4::rename_classes, "classes", reg_container);898valid = valid && test_conversion_with_regex("Spatial.tscn", "Spatial.tscn", &ProjectConverter3To4::rename_classes, "classes", reg_container);899valid = valid && test_conversion_with_regex("Spatial.gd", "Spatial.gd", &ProjectConverter3To4::rename_classes, "classes", reg_container);900valid = valid && test_conversion_with_regex("Spatial.shader", "Spatial.shader", &ProjectConverter3To4::rename_classes, "classes", reg_container);901valid = valid && test_conversion_with_regex("Spatial.other", "Node3D.other", &ProjectConverter3To4::rename_classes, "classes", reg_container);902903valid = valid && test_conversion_gdscript_builtin("\nonready", "\n@onready", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);904valid = valid && test_conversion_gdscript_builtin("onready", "@onready", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);905valid = valid && test_conversion_gdscript_builtin(" onready", " onready", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);906valid = valid && test_conversion_gdscript_builtin("\nexport", "\n@export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);907valid = valid && test_conversion_gdscript_builtin("\texport", "\t@export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);908valid = valid && test_conversion_gdscript_builtin("\texport_dialog", "\texport_dialog", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);909valid = valid && test_conversion_gdscript_builtin("export", "@export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);910valid = valid && test_conversion_gdscript_builtin(" export", " export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);911valid = valid && test_conversion_gdscript_builtin("\n\nremote func", "\n\n@rpc(\"any_peer\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);912valid = valid && test_conversion_gdscript_builtin("\n\nremote func", "\n\n@rpc(\\\"any_peer\\\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, true);913valid = valid && test_conversion_gdscript_builtin("\n\nremotesync func", "\n\n@rpc(\"any_peer\", \"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);914valid = valid && test_conversion_gdscript_builtin("\n\nremotesync func", "\n\n@rpc(\\\"any_peer\\\", \\\"call_local\\\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, true);915valid = valid && test_conversion_gdscript_builtin("\n\nsync func", "\n\n@rpc(\"any_peer\", \"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);916valid = valid && test_conversion_gdscript_builtin("\n\nsync func", "\n\n@rpc(\\\"any_peer\\\", \\\"call_local\\\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, true);917valid = valid && test_conversion_gdscript_builtin("\n\nslave func", "\n\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);918valid = valid && test_conversion_gdscript_builtin("\n\npuppet func", "\n\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);919valid = valid && test_conversion_gdscript_builtin("\n\npuppetsync func", "\n\n@rpc(\"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);920valid = valid && test_conversion_gdscript_builtin("\n\npuppetsync func", "\n\n@rpc(\\\"call_local\\\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, true);921valid = valid && test_conversion_gdscript_builtin("\n\nmaster func", "\n\nThe master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);922valid = valid && test_conversion_gdscript_builtin("\n\nmastersync func", "\n\nThe master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n@rpc(\"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);923924valid = valid && test_conversion_gdscript_builtin("var size: Vector2 = Vector2() setget set_function, get_function", "var size: Vector2 = Vector2(): get = get_function, set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);925valid = valid && test_conversion_gdscript_builtin("var size: Vector2 = Vector2() setget set_function, ", "var size: Vector2 = Vector2(): set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);926valid = valid && test_conversion_gdscript_builtin("var size: Vector2 = Vector2() setget set_function", "var size: Vector2 = Vector2(): set = set_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);927valid = valid && test_conversion_gdscript_builtin("var size: Vector2 = Vector2() setget , get_function", "var size: Vector2 = Vector2(): get = get_function", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);928929valid = valid && test_conversion_gdscript_builtin("get_node(@", "get_node(", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);930931valid = valid && test_conversion_gdscript_builtin("yield(this, \"timeout\")", "await this.timeout", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);932valid = valid && test_conversion_gdscript_builtin("yield(this, \\\"timeout\\\")", "await this.timeout", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, true);933934valid = valid && test_conversion_gdscript_builtin(" Transform.xform(Vector3(a,b,c) + Vector3.UP) ", " Transform * (Vector3(a,b,c) + Vector3.UP) ", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);935valid = valid && test_conversion_gdscript_builtin(" Transform.xform_inv(Vector3(a,b,c) + Vector3.UP) ", " (Vector3(a,b,c) + Vector3.UP) * Transform ", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);936937valid = valid && test_conversion_gdscript_builtin("export(float) var lifetime = 3.0", "export var lifetime: float = 3.0", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);938valid = valid && test_conversion_gdscript_builtin("export (int)var spaces=1", "export var spaces: int=1", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);939valid = valid && test_conversion_gdscript_builtin("export(String, 'AnonymousPro', 'CourierPrime') var _font_name = 'AnonymousPro'", "export var _font_name = 'AnonymousPro' # (String, 'AnonymousPro', 'CourierPrime')", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false); // TODO, this is only a workaround940valid = valid && test_conversion_gdscript_builtin("export(PackedScene) var mob_scene", "export var mob_scene: PackedScene", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);941valid = valid && test_conversion_gdscript_builtin("export(float) var lifetime: float = 3.0", "export var lifetime: float = 3.0", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);942valid = valid && test_conversion_gdscript_builtin("export var lifetime: float = 3.0", "export var lifetime: float = 3.0", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);943valid = valid && test_conversion_gdscript_builtin("export var lifetime := 3.0", "export var lifetime := 3.0", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);944valid = valid && test_conversion_gdscript_builtin("export(float) var lifetime := 3.0", "export var lifetime := 3.0", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);945946valid = valid && test_conversion_gdscript_builtin("var d = parse_json(roman(sfs))", "var test_json_conv = JSON.new()\ntest_json_conv.parse(roman(sfs))\nvar d = test_json_conv.get_data()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);947948valid = valid && test_conversion_gdscript_builtin("to_json( AA ) szon", "JSON.new().stringify( AA ) szon", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);949valid = valid && test_conversion_gdscript_builtin("s to_json", "s JSON.new().stringify", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);950valid = valid && test_conversion_gdscript_builtin("AF to_json2", "AF to_json2", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);951valid = valid && test_conversion_gdscript_builtin("var rr = JSON.parse(a)", "var test_json_conv = JSON.new()\ntest_json_conv.parse(a)\nvar rr = test_json_conv.get_data()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);952953valid = valid && test_conversion_gdscript_builtin("empty()", "is_empty()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);954valid = valid && test_conversion_gdscript_builtin(".empty", ".empty", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);955956valid = valid && test_conversion_gdscript_builtin(").roman(", ").roman(", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);957valid = valid && test_conversion_gdscript_builtin("\t.roman(", "\tsuper.roman(", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);958valid = valid && test_conversion_gdscript_builtin(" .roman(", " super.roman(", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);959valid = valid && test_conversion_gdscript_builtin(".1", ".1", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);960valid = valid && test_conversion_gdscript_builtin(" .1", " .1", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);961valid = valid && test_conversion_gdscript_builtin("'.'", "'.'", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);962valid = valid && test_conversion_gdscript_builtin("'.a'", "'.a'", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);963valid = valid && test_conversion_gdscript_builtin("\t._input(_event)", "\tsuper._input(_event)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);964965valid = valid && test_conversion_gdscript_builtin("(connect(A,B,C) != OK):", "(connect(A, Callable(B, C)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);966valid = valid && test_conversion_gdscript_builtin("(connect(A,B,C,D) != OK):", "(connect(A, Callable(B, C).bind(D)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);967valid = valid && test_conversion_gdscript_builtin("(connect(A,B,C,[D]) != OK):", "(connect(A, Callable(B, C).bind(D)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);968valid = valid && test_conversion_gdscript_builtin("(connect(A,B,C,[D,E]) != OK):", "(connect(A, Callable(B, C).bind(D,E)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);969valid = valid && test_conversion_gdscript_builtin("(connect(A,B,C,[D,E],F) != OK):", "(connect(A, Callable(B, C).bind(D,E), F) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);970valid = valid && test_conversion_gdscript_builtin("(connect(A,B,C,D,E) != OK):", "(connect(A, Callable(B, C).bind(D), E) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);971972valid = valid && test_conversion_gdscript_builtin(".connect(A,B,C)", ".connect(A, Callable(B, C))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);973valid = valid && test_conversion_gdscript_builtin("abc.connect(A,B,C)", "abc.connect(A, Callable(B, C))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);974valid = valid && test_conversion_gdscript_builtin("\tconnect(A,B,C)", "\tconnect(A, Callable(B, C))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);975valid = valid && test_conversion_gdscript_builtin(" connect(A,B,C)", " connect(A, Callable(B, C))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);976valid = valid && test_conversion_gdscript_builtin("_connect(A,B,C)", "_connect(A,B,C)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);977valid = valid && test_conversion_gdscript_builtin("do_connect(A,B,C)", "do_connect(A,B,C)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);978valid = valid && test_conversion_gdscript_builtin("$connect(A,B,C)", "$connect(A,B,C)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);979valid = valid && test_conversion_gdscript_builtin("@connect(A,B,C)", "@connect(A,B,C)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);980981valid = valid && test_conversion_gdscript_builtin("(start(A,B) != OK):", "(start(Callable(A, B)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);982valid = valid && test_conversion_gdscript_builtin("func start(A,B):", "func start(A,B):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);983valid = valid && test_conversion_gdscript_builtin("(start(A,B,C,D,E,F,G) != OK):", "(start(Callable(A, B).bind(C), D, E, F, G) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);984valid = valid && test_conversion_gdscript_builtin("disconnect(A,B,C) != OK):", "disconnect(A, Callable(B, C)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);985valid = valid && test_conversion_gdscript_builtin("is_connected(A,B,C) != OK):", "is_connected(A, Callable(B, C)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);986valid = valid && test_conversion_gdscript_builtin("is_connected(A,B,C))", "is_connected(A, Callable(B, C)))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);987988valid = valid && test_conversion_gdscript_builtin("(tween_method(A,B,C,D,E).foo())", "(tween_method(Callable(A, B), C, D, E).foo())", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);989valid = valid && test_conversion_gdscript_builtin("(tween_method(A,B,C,D,E,[F,G]).foo())", "(tween_method(Callable(A, B).bind(F,G), C, D, E).foo())", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);990valid = valid && test_conversion_gdscript_builtin("(tween_callback(A,B).foo())", "(tween_callback(Callable(A, B)).foo())", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);991valid = valid && test_conversion_gdscript_builtin("(tween_callback(A,B,[C,D]).foo())", "(tween_callback(Callable(A, B).bind(C,D)).foo())", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);992993valid = valid && test_conversion_gdscript_builtin("func _init(", "func _init(", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);994valid = valid && test_conversion_gdscript_builtin("func _init(a,b,c).(d,e,f):", "func _init(a,b,c):\n\tsuper(d,e,f)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);995valid = valid && test_conversion_gdscript_builtin("func _init(a,b,c).(a.b(),c.d()):", "func _init(a,b,c):\n\tsuper(a.b(),c.d())", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);996valid = valid && test_conversion_gdscript_builtin("func _init(p_x:int)->void:", "func _init(p_x:int)->void:", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);997valid = valid && test_conversion_gdscript_builtin("func _init(a: int).(d,e,f) -> void:", "func _init(a: int) -> void:\n\tsuper(d,e,f)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);998valid = valid && test_conversion_gdscript_builtin("q_PackedDataContainer._iter_init(variable1)", "q_PackedDataContainer._iter_init(variable1)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);9991000valid = valid && test_conversion_gdscript_builtin("create_from_image(aa, bb)", "create_from_image(aa) #,bb", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);1001valid = valid && test_conversion_gdscript_builtin("q_ImageTexture.create_from_image(variable1, variable2)", "q_ImageTexture.create_from_image(variable1) #,variable2", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);10021003valid = valid && test_conversion_gdscript_builtin("set_cell_item(a, b, c, d ,e) # AA", "set_cell_item(Vector3(a, b, c), d, e) # AA", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);1004valid = valid && test_conversion_gdscript_builtin("set_cell_item(a, b)", "set_cell_item(a, b)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);1005valid = valid && test_conversion_gdscript_builtin("get_cell_item_orientation(a, b,c)", "get_cell_item_orientation(Vector3i(a, b, c))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);1006valid = valid && test_conversion_gdscript_builtin("get_cell_item(a, b,c)", "get_cell_item(Vector3i(a, b, c))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);1007valid = valid && test_conversion_gdscript_builtin("map_to_world(a, b,c)", "map_to_local(Vector3i(a, b, c))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);10081009valid = valid && test_conversion_gdscript_builtin("PackedStringArray(req_godot).join('.')", "'.'.join(PackedStringArray(req_godot))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);1010valid = valid && test_conversion_gdscript_builtin("=PackedStringArray(req_godot).join('.')", "='.'.join(PackedStringArray(req_godot))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);10111012valid = valid && test_conversion_gdscript_builtin("apply_force(position, impulse)", "apply_force(impulse, position)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);1013valid = valid && test_conversion_gdscript_builtin("apply_impulse(position, impulse)", "apply_impulse(impulse, position)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);1014valid = valid && test_conversion_gdscript_builtin("draw_rect(a,b,c,d,e).abc", "draw_rect(a, b, c, d).abc# e) TODOConverter3To4 Antialiasing argument is missing", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);1015valid = valid && test_conversion_gdscript_builtin("get_focus_owner()", "get_viewport().gui_get_focus_owner()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);1016valid = valid && test_conversion_gdscript_builtin("button.pressed = 1", "button.button_pressed = 1", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);1017valid = valid && test_conversion_gdscript_builtin("button.pressed=1", "button.button_pressed=1", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);1018valid = valid && test_conversion_gdscript_builtin("button.pressed SF", "button.pressed SF", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);10191020valid = valid && test_conversion_with_regex("Color(\"#f47d\")", "Color(\"#47df\")", &ProjectConverter3To4::convert_hexadecimal_colors, "color literals", reg_container);1021valid = valid && test_conversion_with_regex("Color(\"#ff478cbf\")", "Color(\"#478cbfff\")", &ProjectConverter3To4::convert_hexadecimal_colors, "color literals", reg_container);1022valid = valid && test_conversion_with_regex("Color(\"#de32bf\")", "Color(\"#de32bf\")", &ProjectConverter3To4::convert_hexadecimal_colors, "color literals", reg_container);1023valid = valid && test_conversion_with_regex("AAA Color.white AF", "AAA Color.WHITE AF", &ProjectConverter3To4::rename_colors, "color constants", reg_container);10241025// Note: Do not change to *scancode*, it is applied before that conversion.1026valid = valid && test_conversion_with_regex("\"device\":-1,\"scancode\":16777231,\"physical_scancode\":16777232", "\"device\":-1,\"scancode\":4194319,\"physical_scancode\":4194320", &ProjectConverter3To4::rename_input_map_scancode, "custom rename", reg_container);1027valid = valid && test_conversion_with_regex("\"device\":-1,\"scancode\":65,\"physical_scancode\":66", "\"device\":-1,\"scancode\":65,\"physical_scancode\":66", &ProjectConverter3To4::rename_input_map_scancode, "custom rename", reg_container);10281029valid = valid && test_conversion_with_regex("\"device\":0,\"button_index\":5,\"pressure\":0.0,\"pressed\":false,", "\"device\":0,\"button_index\":10,\"pressure\":0.0,\"pressed\":false,", &ProjectConverter3To4::rename_joypad_buttons_and_axes, "custom rename", reg_container);1030valid = valid && test_conversion_with_regex("\"device\":0,\"axis\":6,", "\"device\":0,\"axis\":4,", &ProjectConverter3To4::rename_joypad_buttons_and_axes, "custom rename", reg_container);1031valid = valid && test_conversion_with_regex("InputEventJoypadButton,\"button_index\":7,\"pressure\":0.0,\"pressed\":false,\"script\":null", "InputEventJoypadMotion,\"axis\":5,\"axis_value\":1.0,\"script\":null", &ProjectConverter3To4::rename_joypad_buttons_and_axes, "custom rename", reg_container);10321033// Custom rule conversion1034{1035String from = "instance";1036String to = "instantiate";1037String name = "AA.instance()";10381039Vector<SourceLine> got = split_lines(name);10401041String expected = "AA.instantiate()";1042custom_rename(got, from, to);1043String got_str = collect_string_from_vector(got);1044if (got_str != expected) {1045ERR_PRINT(vformat("Failed to convert custom rename \"%s\" to \"%s\", got \"%s\", instead.", name, expected, got_str));1046}1047valid = valid && (got_str == expected);1048}10491050// get_object_of_execution1051{1052String base = "var roman = kieliszek.";1053String expected = "kieliszek.";1054String got = get_object_of_execution(base);1055if (got != expected) {1056ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size()));1057}1058valid = valid && (got == expected);1059}1060{1061String base = "r.";1062String expected = "r.";1063String got = get_object_of_execution(base);1064if (got != expected) {1065ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size()));1066}1067valid = valid && (got == expected);1068}1069{1070String base = "mortadela(";1071String expected = "";1072String got = get_object_of_execution(base);1073if (got != expected) {1074ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size()));1075}1076valid = valid && (got == expected);1077}1078{1079String base = "var node = $world/ukraine/lviv.";1080String expected = "$world/ukraine/lviv.";1081String got = get_object_of_execution(base);1082if (got != expected) {1083ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size()));1084}1085valid = valid && (got == expected);1086}10871088// get_starting_space1089{1090String base = "\t\t\t var roman = kieliszek.";1091String expected = "\t\t\t";1092String got = get_starting_space(base);1093if (got != expected) {1094ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size()));1095}1096valid = valid && (got == expected);1097}10981099// Parse Arguments1100{1101String line = "( )";1102Vector<String> got_vector = parse_arguments(line);1103String got = "";1104String expected = "";1105for (String &part : got_vector) {1106got += part + "|||";1107}1108if (got != expected) {1109ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size()));1110}1111valid = valid && (got == expected);1112}1113{1114String line = "(a , b , c)";1115Vector<String> got_vector = parse_arguments(line);1116String got = "";1117String expected = "a|||b|||c|||";1118for (String &part : got_vector) {1119got += part + "|||";1120}1121if (got != expected) {1122ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size()));1123}1124valid = valid && (got == expected);1125}1126{1127String line = "(a , \"b,\" , c)";1128Vector<String> got_vector = parse_arguments(line);1129String got = "";1130String expected = "a|||\"b,\"|||c|||";1131for (String &part : got_vector) {1132got += part + "|||";1133}1134if (got != expected) {1135ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size()));1136}1137valid = valid && (got == expected);1138}1139{1140String line = "(a , \"(,),,,,\" , c)";1141Vector<String> got_vector = parse_arguments(line);1142String got = "";1143String expected = "a|||\"(,),,,,\"|||c|||";1144for (String &part : got_vector) {1145got += part + "|||";1146}1147if (got != expected) {1148ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size()));1149}1150valid = valid && (got == expected);1151}11521153return valid;1154}11551156// Validate in all arrays if names don't do cyclic renames "Node" -> "Node2D" | "Node2D" -> "2DNode"1157bool ProjectConverter3To4::test_array_names() {1158bool valid = true;1159Vector<String> names = Vector<String>();11601161// Validate if all classes are valid.1162{1163for (unsigned int current_index = 0; RenamesMap3To4::class_renames[current_index][0]; current_index++) {1164const String old_class = RenamesMap3To4::class_renames[current_index][0];1165const String new_class = RenamesMap3To4::class_renames[current_index][1];11661167// Light2D, Texture, Viewport are special classes(probably virtual ones).1168if (ClassDB::class_exists(StringName(old_class)) && old_class != "Light2D" && old_class != "Texture" && old_class != "Viewport") {1169ERR_PRINT(vformat("Class \"%s\" exists in Godot 4, so it cannot be renamed to something else.", old_class));1170valid = false; // This probably should be only a warning, but not 100% sure - this would need to be added to CI.1171}11721173// Callable is special class, to which normal classes may be renamed.1174if (!ClassDB::class_exists(StringName(new_class)) && new_class != "Callable") {1175ERR_PRINT(vformat("Class \"%s\" does not exist in Godot 4, so it cannot be used in the conversion.", new_class));1176valid = false; // This probably should be only a warning, but not 100% sure - this would need to be added to CI.1177}1178}1179}11801181{1182HashSet<String> all_functions;11831184// List of excluded functions from builtin types and global namespace, because currently it is not possible to get list of functions from them.1185// This will be available when https://github.com/godotengine/godot/pull/49053 or similar will be included into Godot.1186static const char *builtin_types_excluded_functions[] = { "dict_to_inst", "inst_to_dict", "bytes_to_var", "bytes_to_var_with_objects", "db_to_linear", "deg_to_rad", "linear_to_db", "rad_to_deg", "randf_range", "snapped", "str_to_var", "var_to_str", "var_to_bytes", "var_to_bytes_with_objects", "move_toward", "uri_encode", "uri_decode", "remove_at", "get_rotation_quaternion", "limit_length", "grow_side", "is_absolute_path", "is_valid_int", "lerp", "to_ascii_buffer", "to_utf8_buffer", "to_utf32_buffer", "to_wchar_buffer", "snapped", "remap", "rfind", nullptr };1187for (int current_index = 0; builtin_types_excluded_functions[current_index]; current_index++) {1188all_functions.insert(builtin_types_excluded_functions[current_index]);1189}11901191//for (int type = Variant::Type::NIL + 1; type < Variant::Type::VARIANT_MAX; type++) {1192// List<MethodInfo> method_list;1193// Variant::get_method_list_by_type(&method_list, Variant::Type(type));1194// for (MethodInfo &function_data : method_list) {1195// if (!all_functions.has(function_data.name)) {1196// all_functions.insert(function_data.name);1197// }1198// }1199//}12001201LocalVector<StringName> classes_list;1202ClassDB::get_class_list(classes_list);1203for (StringName &name_of_class : classes_list) {1204List<MethodInfo> method_list;1205ClassDB::get_method_list(name_of_class, &method_list, true);1206for (MethodInfo &function_data : method_list) {1207if (!all_functions.has(function_data.name)) {1208all_functions.insert(function_data.name);1209}1210}1211}12121213int current_element = 0;1214while (RenamesMap3To4::gdscript_function_renames[current_element][0] != nullptr) {1215String name_3_x = RenamesMap3To4::gdscript_function_renames[current_element][0];1216String name_4_0 = RenamesMap3To4::gdscript_function_renames[current_element][1];1217if (!all_functions.has(name_4_0)) {1218ERR_PRINT(vformat("Missing GDScript function in pair (%s - ===> %s <===)", name_3_x, name_4_0));1219valid = false;1220}1221current_element++;1222}1223}1224if (!valid) {1225ERR_PRINT("Found function which is used in the converter, but it cannot be found in Godot 4. Rename this element or remove its entry if it's obsolete.");1226}12271228valid = valid && test_single_array(RenamesMap3To4::enum_renames);1229valid = valid && test_single_array(RenamesMap3To4::class_renames, true);1230valid = valid && test_single_array(RenamesMap3To4::gdscript_function_renames, true);1231valid = valid && test_single_array(RenamesMap3To4::csharp_function_renames, true);1232valid = valid && test_single_array(RenamesMap3To4::gdscript_properties_renames, true);1233valid = valid && test_single_array(RenamesMap3To4::csharp_properties_renames, true);1234valid = valid && test_single_array(RenamesMap3To4::shaders_renames, true);1235valid = valid && test_single_array(RenamesMap3To4::gdscript_signals_renames);1236valid = valid && test_single_array(RenamesMap3To4::project_settings_renames);1237valid = valid && test_single_array(RenamesMap3To4::project_godot_renames);1238valid = valid && test_single_array(RenamesMap3To4::input_map_renames);1239valid = valid && test_single_array(RenamesMap3To4::builtin_types_renames);1240valid = valid && test_single_array(RenamesMap3To4::color_renames);12411242return valid;1243}12441245// Validates the array to prevent cyclic renames, such as `Node` -> `Node2D`, then `Node2D` -> `2DNode`.1246// Also checks if names contain leading or trailing spaces.1247bool ProjectConverter3To4::test_single_array(const char *p_array[][2], bool p_ignore_4_0_name) {1248bool valid = true;1249Vector<String> names = Vector<String>();12501251for (unsigned int current_index = 0; p_array[current_index][0]; current_index++) {1252String name_3_x = p_array[current_index][0];1253String name_4_0 = p_array[current_index][1];1254if (name_3_x != name_3_x.strip_edges()) {1255ERR_PRINT(vformat("Invalid Entry \"%s\" contains leading or trailing spaces.", name_3_x));1256valid = false;1257}1258if (names.has(name_3_x)) {1259ERR_PRINT(vformat("Found duplicated entry, pair ( -> %s , %s)", name_3_x, name_4_0));1260valid = false;1261}1262names.append(name_3_x);12631264if (name_4_0 != name_4_0.strip_edges()) {1265ERR_PRINT(vformat("Invalid Entry \"%s\" contains leading or trailing spaces.", name_3_x));1266valid = false;1267}1268if (names.has(name_4_0)) {1269ERR_PRINT(vformat("Found duplicated entry, pair ( -> %s , %s)", name_3_x, name_4_0));1270valid = false;1271}1272if (!p_ignore_4_0_name) {1273names.append(name_4_0);1274}1275}1276return valid;1277}12781279// Returns arguments from given function execution, this cannot be really done as regex.1280// `abc(d,e(f,g),h)` -> [d], [e(f,g)], [h]1281Vector<String> ProjectConverter3To4::parse_arguments(const String &line) {1282Vector<String> parts;1283int string_size = line.length();1284int start_part = 0; // Index of beginning of start part.1285int parts_counter = 0;1286char32_t previous_character = '\0';1287bool is_inside_string = false; // If true, it ignores these 3 characters ( , ) inside string.12881289ERR_FAIL_COND_V_MSG(line.count("(") != line.count(")"), parts, vformat("Converter internal bug: substring should have equal number of open and close parentheses in line - \"%s\".", line));12901291for (int current_index = 0; current_index < string_size; current_index++) {1292char32_t character = line.get(current_index);1293switch (character) {1294case '(':1295case '[':1296case '{': {1297parts_counter++;1298if (parts_counter == 1 && !is_inside_string) {1299start_part = current_index;1300}1301break;1302};1303case ')':1304case '}': {1305parts_counter--;1306if (parts_counter == 0 && !is_inside_string) {1307parts.append(line.substr(start_part + 1, current_index - start_part - 1));1308start_part = current_index;1309}1310break;1311};1312case ']': {1313parts_counter--;1314if (parts_counter == 0 && !is_inside_string) {1315parts.append(line.substr(start_part, current_index - start_part));1316start_part = current_index;1317}1318break;1319};1320case ',': {1321if (parts_counter == 1 && !is_inside_string) {1322parts.append(line.substr(start_part + 1, current_index - start_part - 1));1323start_part = current_index;1324}1325break;1326};1327case '"': {1328if (previous_character != '\\') {1329is_inside_string = !is_inside_string;1330}1331}1332}1333previous_character = character;1334}13351336Vector<String> clean_parts;1337for (String &part : parts) {1338part = part.strip_edges();1339if (!part.is_empty()) {1340clean_parts.append(part);1341}1342}13431344return clean_parts;1345}13461347// Finds latest parenthesis owned by function.1348// `function(abc(a,b),DD)):` finds this parenthess `function(abc(a,b),DD => ) <= ):`1349int ProjectConverter3To4::get_end_parenthesis(const String &line) const {1350int current_state = 0;1351for (int current_index = 0; line.length() > current_index; current_index++) {1352char32_t character = line.get(current_index);1353if (character == '(') {1354current_state++;1355}1356if (character == ')') {1357current_state--;1358if (current_state == 0) {1359return current_index;1360}1361}1362}1363return -1;1364}13651366// Merges multiple arguments into a single String.1367// Needed when after processing e.g. 2 arguments, later arguments are not changed in any way.1368String ProjectConverter3To4::connect_arguments(const Vector<String> &arguments, int from, int to) const {1369if (to == -1) {1370to = arguments.size();1371}13721373String value;1374if (arguments.size() > 0 && from != 0 && from < to) {1375value = ", ";1376}13771378for (int i = from; i < to; i++) {1379value += arguments[i];1380if (i != to - 1) {1381value += ", ";1382}1383}1384return value;1385}13861387// Returns the indentation (spaces and tabs) at the start of the line e.g. `\t\tmove_this` returns `\t\t`.1388String ProjectConverter3To4::get_starting_space(const String &line) const {1389String empty_space;1390int current_character = 0;13911392if (line.is_empty()) {1393return empty_space;1394}13951396if (line[0] == ' ') {1397while (current_character < line.size()) {1398if (line[current_character] == ' ') {1399empty_space += ' ';1400current_character++;1401} else {1402break;1403}1404}1405}1406if (line[0] == '\t') {1407while (current_character < line.size()) {1408if (line[current_character] == '\t') {1409empty_space += '\t';1410current_character++;1411} else {1412break;1413}1414}1415}1416return empty_space;1417}14181419// Returns the object that’s executing the function in the line.1420// e.g. Passing the line "var roman = kieliszek.funkcja()" to this function returns "kieliszek".1421String ProjectConverter3To4::get_object_of_execution(const String &line) const {1422int end = line.size() - 1; // Last one is \01423int variable_start = end - 1;1424int start = end - 1;14251426bool is_possibly_nodepath = false;1427bool is_valid_nodepath = false;14281429while (start >= 0) {1430char32_t character = line[start];1431bool is_variable_char = (character >= 'A' && character <= 'Z') || (character >= 'a' && character <= 'z') || character == '.' || character == '_';1432bool is_nodepath_start = character == '$';1433bool is_nodepath_sep = character == '/';1434if (is_variable_char || is_nodepath_start || is_nodepath_sep) {1435if (start == 0) {1436break;1437} else if (is_nodepath_sep) {1438// Freeze variable_start, try to fetch more chars since this might be a Node path literal.1439is_possibly_nodepath = true;1440} else if (is_nodepath_start) {1441// Found $, this is a Node path literal.1442is_valid_nodepath = true;1443break;1444}1445if (!is_possibly_nodepath) {1446variable_start--;1447}1448start--;1449continue;1450} else {1451// Abandon all hope, this is neither a variable nor a Node path literal.1452variable_start++; // Found invalid character, needs to be ignored.1453break;1454}1455}1456if (is_valid_nodepath) {1457variable_start = start;1458}1459return line.substr(variable_start, (end - variable_start));1460}14611462void ProjectConverter3To4::rename_colors(Vector<SourceLine> &source_lines, const RegExContainer ®_container) {1463for (SourceLine &source_line : source_lines) {1464if (source_line.is_comment) {1465continue;1466}14671468String &line = source_line.line;1469if (uint64_t(line.length()) <= maximum_line_length) {1470if (line.contains("Color.")) {1471for (unsigned int current_index = 0; RenamesMap3To4::color_renames[current_index][0]; current_index++) {1472line = reg_container.color_regexes[current_index]->sub(line, reg_container.color_renamed[current_index], true);1473}1474}1475}1476}1477}14781479// Convert hexadecimal colors from ARGB to RGBA1480void ProjectConverter3To4::convert_hexadecimal_colors(Vector<SourceLine> &source_lines, const RegExContainer ®_container) {1481for (SourceLine &source_line : source_lines) {1482if (source_line.is_comment) {1483continue;1484}14851486String &line = source_line.line;1487if (uint64_t(line.length()) <= maximum_line_length) {1488if (line.contains("Color(\"")) {1489line = reg_container.color_hexadecimal_short_constructor.sub(line, "Color(\"#$2$1", true);1490line = reg_container.color_hexadecimal_full_constructor.sub(line, "Color(\"#$2$1", true);1491}1492}1493}1494}14951496Vector<String> ProjectConverter3To4::check_for_rename_colors(Vector<String> &lines, const RegExContainer ®_container) {1497Vector<String> found_renames;14981499int current_line = 1;1500for (String &line : lines) {1501if (uint64_t(line.length()) <= maximum_line_length) {1502if (line.contains("Color.")) {1503for (unsigned int current_index = 0; RenamesMap3To4::color_renames[current_index][0]; current_index++) {1504TypedArray<RegExMatch> reg_match = reg_container.color_regexes[current_index]->search_all(line);1505if (reg_match.size() > 0) {1506found_renames.append(line_formatter(current_line, RenamesMap3To4::color_renames[current_index][0], RenamesMap3To4::color_renames[current_index][1], line));1507}1508}1509}1510}1511current_line++;1512}15131514return found_renames;1515}15161517void ProjectConverter3To4::fix_tool_declaration(Vector<SourceLine> &source_lines, const RegExContainer ®_container) {1518// In godot4, "tool" became "@tool" and must be located at the top of the file.1519for (int i = 0; i < source_lines.size(); ++i) {1520if (source_lines[i].line == "tool") {1521source_lines.remove_at(i);1522source_lines.insert(0, { "@tool", false });1523return; // assuming there's at most 1 tool declaration.1524}1525}1526}15271528void ProjectConverter3To4::fix_pause_mode(Vector<SourceLine> &source_lines, const RegExContainer ®_container) {1529// In Godot 3, the pause_mode 2 equals the PAUSE_MODE_PROCESS value.1530// In Godot 4, the pause_mode PAUSE_MODE_PROCESS was renamed to PROCESS_MODE_ALWAYS and equals the number 3.1531// We therefore convert pause_mode = 2 to pause_mode = 3.1532for (SourceLine &source_line : source_lines) {1533String &line = source_line.line;15341535if (line == "pause_mode = 2") {1536// Note: pause_mode is renamed to process_mode later on, so no need to do it here.1537line = "pause_mode = 3";1538}1539}1540}15411542void ProjectConverter3To4::rename_classes(Vector<SourceLine> &source_lines, const RegExContainer ®_container) {1543for (SourceLine &source_line : source_lines) {1544if (source_line.is_comment) {1545continue;1546}15471548String &line = source_line.line;1549if (uint64_t(line.length()) <= maximum_line_length) {1550for (unsigned int current_index = 0; RenamesMap3To4::class_renames[current_index][0]; current_index++) {1551if (line.contains(RenamesMap3To4::class_renames[current_index][0])) {1552bool found_ignored_items = false;1553// Renaming Spatial.tscn to TEMP_RENAMED_CLASS.tscn.1554if (line.contains(String(RenamesMap3To4::class_renames[current_index][0]) + ".")) {1555found_ignored_items = true;1556line = reg_container.class_tscn_regexes[current_index]->sub(line, "TEMP_RENAMED_CLASS.tscn", true);1557line = reg_container.class_gd_regexes[current_index]->sub(line, "TEMP_RENAMED_CLASS.gd", true);1558line = reg_container.class_shader_regexes[current_index]->sub(line, "TEMP_RENAMED_CLASS.shader", true);1559}15601561// Causal renaming Spatial -> Node3D.1562line = reg_container.class_regexes[current_index]->sub(line, RenamesMap3To4::class_renames[current_index][1], true);15631564// Restore Spatial.tscn from TEMP_RENAMED_CLASS.tscn.1565if (found_ignored_items) {1566line = reg_container.class_temp_tscn.sub(line, reg_container.class_temp_tscn_renames[current_index], true);1567line = reg_container.class_temp_gd.sub(line, reg_container.class_temp_gd_renames[current_index], true);1568line = reg_container.class_temp_shader.sub(line, reg_container.class_temp_shader_renames[current_index], true);1569}1570}1571}1572}1573}1574}15751576Vector<String> ProjectConverter3To4::check_for_rename_classes(Vector<String> &lines, const RegExContainer ®_container) {1577Vector<String> found_renames;15781579int current_line = 1;15801581for (String &line : lines) {1582if (uint64_t(line.length()) <= maximum_line_length) {1583for (unsigned int current_index = 0; RenamesMap3To4::class_renames[current_index][0]; current_index++) {1584if (line.contains(RenamesMap3To4::class_renames[current_index][0])) {1585String old_line = line;1586bool found_ignored_items = false;1587// Renaming Spatial.tscn to TEMP_RENAMED_CLASS.tscn.1588if (line.contains(String(RenamesMap3To4::class_renames[current_index][0]) + ".")) {1589found_ignored_items = true;1590line = reg_container.class_tscn_regexes[current_index]->sub(line, "TEMP_RENAMED_CLASS.tscn", true);1591line = reg_container.class_gd_regexes[current_index]->sub(line, "TEMP_RENAMED_CLASS.gd", true);1592line = reg_container.class_shader_regexes[current_index]->sub(line, "TEMP_RENAMED_CLASS.shader", true);1593}15941595// Causal renaming Spatial -> Node3D.1596TypedArray<RegExMatch> reg_match = reg_container.class_regexes[current_index]->search_all(line);1597if (reg_match.size() > 0) {1598found_renames.append(line_formatter(current_line, RenamesMap3To4::class_renames[current_index][0], RenamesMap3To4::class_renames[current_index][1], old_line));1599}16001601// Restore Spatial.tscn from TEMP_RENAMED_CLASS.tscn.1602if (found_ignored_items) {1603line = reg_container.class_temp_tscn.sub(line, reg_container.class_temp_tscn_renames[current_index], true);1604line = reg_container.class_temp_gd.sub(line, reg_container.class_temp_gd_renames[current_index], true);1605line = reg_container.class_temp_shader.sub(line, reg_container.class_temp_shader_renames[current_index], true);1606}1607}1608}1609}1610current_line++;1611}1612return found_renames;1613}16141615void ProjectConverter3To4::rename_gdscript_functions(Vector<SourceLine> &source_lines, const RegExContainer ®_container, bool builtin) {1616for (SourceLine &source_line : source_lines) {1617if (source_line.is_comment) {1618continue;1619}16201621String &line = source_line.line;1622if (uint64_t(line.length()) <= maximum_line_length) {1623process_gdscript_line(line, reg_container, builtin);1624}1625}1626}16271628Vector<String> ProjectConverter3To4::check_for_rename_gdscript_functions(Vector<String> &lines, const RegExContainer ®_container, bool builtin) {1629int current_line = 1;16301631Vector<String> found_renames;16321633for (String &line : lines) {1634if (uint64_t(line.length()) <= maximum_line_length) {1635String old_line = line;1636process_gdscript_line(line, reg_container, builtin);1637if (old_line != line) {1638found_renames.append(simple_line_formatter(current_line, old_line, line));1639}1640}1641}16421643return found_renames;1644}16451646bool ProjectConverter3To4::contains_function_call(const String &line, const String &function) const {1647// We want to convert the function only if it is completely standalone.1648// For example, when we search for "connect(", we don't want to accidentally convert "reconnect(".1649if (!line.contains(function)) {1650return false;1651}16521653int index = line.find(function);1654if (index == 0) {1655return true;1656}16571658char32_t previous_char = line.get(index - 1);1659return (previous_char < '0' || previous_char > '9') && (previous_char < 'a' || previous_char > 'z') && (previous_char < 'A' || previous_char > 'Z') && previous_char != '_' && previous_char != '$' && previous_char != '@';1660}16611662// TODO, this function should run only on all ".gd" files and also on lines in ".tscn" files which are parts of built-in Scripts.1663void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContainer ®_container, bool builtin) {1664// In this and other functions, reg.sub() is used only after checking lines with str.contains().1665// With longer lines, doing so can sometimes be significantly faster.16661667if ((line.contains(".lock") || line.contains(".unlock")) && !line.contains("mtx") && !line.contains("mutex") && !line.contains("Mutex")) {1668line = reg_container.reg_image_lock.sub(line, "false # $1.lock() # TODOConverter3To4, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", true);1669line = reg_container.reg_image_unlock.sub(line, "false # $1.unlock() # TODOConverter3To4, Image no longer requires locking, `false` helps to not break one line if/else, so it can freely be removed", true);1670}16711672// PackedStringArray(req_godot).join('.') -> '.'.join(PackedStringArray(req_godot)) PoolStringArray1673if (line.contains(".join")) {1674line = reg_container.reg_join.sub(line, "$2.join($1)", true);1675}16761677// -- empty() -> is_empty() Pool*Array1678if (line.contains("empty")) {1679line = reg_container.reg_is_empty.sub(line, "is_empty(", true);1680}16811682// -- \t.func() -> \tsuper.func() Object1683if (line.contains_char('(') && line.contains_char('.')) {1684line = reg_container.reg_super.sub(line, "$1super.$2", true); // TODO, not sure if possible, but for now this broke String text e.g. "Chosen .gitignore" -> "Chosen super.gitignore"1685}16861687// -- JSON.parse(a) -> JSON.new().parse(a) etc. JSON1688if (line.contains("parse")) {1689line = reg_container.reg_json_non_new.sub(line, "$1var test_json_conv = JSON.new()\n$1test_json_conv.parse($3\n$1$2test_json_conv.get_data()", true);1690}16911692// -- to_json(a) -> JSON.new().stringify(a) Object1693if (line.contains("to_json")) {1694line = reg_container.reg_json_to.sub(line, "JSON.new().stringify", true);1695}1696// -- parse_json(a) -> JSON.get_data() etc. Object1697if (line.contains("parse_json")) {1698line = reg_container.reg_json_parse.sub(line, "$1var test_json_conv = JSON.new()\n$1test_json_conv.parse($3\n$1$2test_json_conv.get_data()", true);1699}1700// -- JSON.print( -> JSON.stringify(1701if (line.contains("JSON.print(")) {1702line = reg_container.reg_json_print.sub(line, "JSON.stringify(", true);1703}17041705// -- get_node(@ -> get_node( Node1706if (line.contains("get_node")) {1707line = line.replace("get_node(@", "get_node(");1708}17091710if (line.contains("export")) {1711// 1. export(float) var lifetime: float = 3.0 -> export var lifetime: float = 3.01712line = reg_container.reg_export_typed.sub(line, "export var $2: $1");1713// 2. export(float) var lifetime := 3.0 -> export var lifetime := 3.01714line = reg_container.reg_export_inferred_type.sub(line, "export var $1 :=");1715// 3. export(float) var lifetime = 3.0 -> export var lifetime: float = 3.0 GDScript1716line = reg_container.reg_export_simple.sub(line, "export var $2: $1");1717// 4. export(String, 'AnonymousPro', 'CourierPrime') var _font_name = 'AnonymousPro' -> export var _font_name = 'AnonymousPro' #(String, 'AnonymousPro', 'CourierPrime') GDScript1718line = reg_container.reg_export_advanced.sub(line, "export var $2$3 # ($1)");1719}17201721// Setget Setget1722if (line.contains("setget")) {1723line = reg_container.reg_setget_setget.sub(line, "var $1$2: get = $4, set = $3", true);1724}17251726// Setget set1727if (line.contains("setget")) {1728line = reg_container.reg_setget_set.sub(line, "var $1$2: set = $3", true);1729}17301731// Setget get1732if (line.contains("setget")) {1733line = reg_container.reg_setget_get.sub(line, "var $1$2: get = $3", true);1734}17351736if (line.contains("window_resizable")) {1737// OS.set_window_resizable(a) -> get_window().unresizable = not (a)1738line = reg_container.reg_os_set_window_resizable.sub(line, "get_window().unresizable = not ($1)", true);1739// OS.window_resizable = a -> same1740line = reg_container.reg_os_assign_window_resizable.sub(line, "get_window().unresizable = not ($1)", true);1741// OS.[is_]window_resizable() -> (not get_window().unresizable)1742line = reg_container.reg_os_is_window_resizable.sub(line, "(not get_window().unresizable)", true);1743}17441745if (line.contains("window_fullscreen")) {1746// OS.window_fullscreen(a) -> get_window().mode = Window.MODE_EXCLUSIVE_FULLSCREEN if (a) else Window.MODE_WINDOWED1747line = reg_container.reg_os_set_fullscreen.sub(line, "get_window().mode = Window.MODE_EXCLUSIVE_FULLSCREEN if ($1) else Window.MODE_WINDOWED", true);1748// window_fullscreen = a -> same1749line = reg_container.reg_os_assign_fullscreen.sub(line, "get_window().mode = Window.MODE_EXCLUSIVE_FULLSCREEN if ($1) else Window.MODE_WINDOWED", true);1750// OS.[is_]window_fullscreen() -> ((get_window().mode == Window.MODE_EXCLUSIVE_FULLSCREEN) or (get_window().mode == Window.MODE_FULLSCREEN))1751line = reg_container.reg_os_is_fullscreen.sub(line, "((get_window().mode == Window.MODE_EXCLUSIVE_FULLSCREEN) or (get_window().mode == Window.MODE_FULLSCREEN))", true);1752}17531754if (line.contains("window_maximized")) {1755// OS.window_maximized(a) -> get_window().mode = Window.MODE_MAXIMIZED if (a) else Window.MODE_WINDOWED1756line = reg_container.reg_os_set_maximized.sub(line, "get_window().mode = Window.MODE_MAXIMIZED if ($1) else Window.MODE_WINDOWED", true);1757// window_maximized = a -> same1758line = reg_container.reg_os_assign_maximized.sub(line, "get_window().mode = Window.MODE_MAXIMIZED if ($1) else Window.MODE_WINDOWED", true);1759// OS.[is_]window_maximized() -> (get_window().mode == Window.MODE_MAXIMIZED)1760line = reg_container.reg_os_is_maximized.sub(line, "(get_window().mode == Window.MODE_MAXIMIZED)", true);1761}17621763if (line.contains("window_minimized")) {1764// OS.window_minimized(a) -> get_window().mode = Window.MODE_MINIMIZED if (a) else Window.MODE_WINDOWED1765line = reg_container.reg_os_set_minimized.sub(line, "get_window().mode = Window.MODE_MINIMIZED if ($1) else Window.MODE_WINDOWED", true);1766// window_minimized = a -> same1767line = reg_container.reg_os_assign_minimized.sub(line, "get_window().mode = Window.MODE_MINIMIZED if ($1) else Window.MODE_WINDOWED", true);1768// OS.[is_]window_minimized() -> (get_window().mode == Window.MODE_MINIMIZED)1769line = reg_container.reg_os_is_minimized.sub(line, "(get_window().mode == Window.MODE_MINIMIZED)", true);1770}17711772if (line.contains("set_use_vsync")) {1773// OS.set_use_vsync(a) -> get_window().window_set_vsync_mode(DisplayServer.VSYNC_ENABLED if (a) else DisplayServer.VSYNC_DISABLED)1774line = reg_container.reg_os_set_vsync.sub(line, "DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED if ($1) else DisplayServer.VSYNC_DISABLED)", true);1775}1776if (line.contains("vsync_enabled")) {1777// vsync_enabled = a -> get_window().window_set_vsync_mode(DisplayServer.VSYNC_ENABLED if (a) else DisplayServer.VSYNC_DISABLED)1778line = reg_container.reg_os_assign_vsync.sub(line, "DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED if ($1) else DisplayServer.VSYNC_DISABLED)", true);1779// OS.[is_]vsync_enabled() -> (DisplayServer.window_get_vsync_mode() != DisplayServer.VSYNC_DISABLED)1780line = reg_container.reg_os_is_vsync.sub(line, "(DisplayServer.window_get_vsync_mode() != DisplayServer.VSYNC_DISABLED)", true);1781}17821783if (line.contains("OS.screen_orientation")) { // keep "OS." at start1784// OS.screen_orientation = a -> DisplayServer.screen_set_orientation(a)1785line = reg_container.reg_os_assign_screen_orient.sub(line, "$1DisplayServer.screen_set_orientation($2)", true); // assignment1786line = line.replace("OS.screen_orientation", "DisplayServer.screen_get_orientation()"); // value access1787}17881789if (line.contains("_window_always_on_top")) {1790// OS.set_window_always_on_top(a) -> get_window().always_on_top = (a)1791line = reg_container.reg_os_set_always_on_top.sub(line, "get_window().always_on_top = ($1)", true);1792// OS.is_window_always_on_top() -> get_window().always_on_top1793line = reg_container.reg_os_is_always_on_top.sub(line, "get_window().always_on_top", true);1794}17951796if (line.contains("et_borderless_window")) {1797// OS.set_borderless_window(a) -> get_window().borderless = (a)1798line = reg_container.reg_os_set_borderless.sub(line, "get_window().borderless = ($1)", true);1799// OS.get_borderless_window() -> get_window().borderless1800line = reg_container.reg_os_get_borderless.sub(line, "get_window().borderless", true);1801}18021803// OS.SCREEN_ORIENTATION_* -> DisplayServer.SCREEN_*1804if (line.contains("OS.SCREEN_ORIENTATION_")) {1805line = reg_container.reg_os_screen_orient_enum.sub(line, "DisplayServer.SCREEN_$1", true);1806}18071808// OS -> Window simple replacements with optional set/get.1809if (line.contains("current_screen")) {1810line = reg_container.reg_os_current_screen.sub(line, "get_window().$1current_screen", true);1811}1812if (line.contains("min_window_size")) {1813line = reg_container.reg_os_min_window_size.sub(line, "get_window().$1min_size", true);1814}1815if (line.contains("max_window_size")) {1816line = reg_container.reg_os_max_window_size.sub(line, "get_window().$1max_size", true);1817}1818if (line.contains("window_position")) {1819line = reg_container.reg_os_window_position.sub(line, "get_window().$1position", true);1820}1821if (line.contains("window_size")) {1822line = reg_container.reg_os_window_size.sub(line, "get_window().$1size", true);1823}1824if (line.contains("et_screen_orientation")) {1825line = reg_container.reg_os_getset_screen_orient.sub(line, "DisplayServer.screen_$1et_orientation", true);1826}18271828// Instantiate1829if (contains_function_call(line, "instance")) {1830line = reg_container.reg_instantiate.sub(line, ".instantiate($1)", true);1831}18321833// -- r.move_and_slide( a, b, c, d, e ) -> r.set_velocity(a) ... r.move_and_slide() KinematicBody1834if (contains_function_call(line, "move_and_slide(")) {1835int start = line.find("move_and_slide(");1836int end = get_end_parenthesis(line.substr(start)) + 1;1837if (end > -1) {1838String base_obj = get_object_of_execution(line.substr(0, start));1839String starting_space = get_starting_space(line);18401841Vector<String> parts = parse_arguments(line.substr(start, end));1842if (parts.size() >= 1) {1843String line_new;18441845// motion_velocity1846line_new += starting_space + base_obj + "set_velocity(" + parts[0] + ")\n";18471848// up_direction1849if (parts.size() >= 2) {1850line_new += starting_space + base_obj + "set_up_direction(" + parts[1] + ")\n";1851}18521853// stop_on_slope1854if (parts.size() >= 3) {1855line_new += starting_space + base_obj + "set_floor_stop_on_slope_enabled(" + parts[2] + ")\n";1856}18571858// max_slides1859if (parts.size() >= 4) {1860line_new += starting_space + base_obj + "set_max_slides(" + parts[3] + ")\n";1861}18621863// floor_max_angle1864if (parts.size() >= 5) {1865line_new += starting_space + base_obj + "set_floor_max_angle(" + parts[4] + ")\n";1866}18671868// infiinite_interia1869if (parts.size() >= 6) {1870line_new += starting_space + "# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `" + parts[5] + "`\n";1871}18721873line_new += starting_space + base_obj + "move_and_slide()";18741875if (!line.begins_with(starting_space + "move_and_slide")) {1876line = line_new + "\n" + line.substr(0, start) + "velocity" + line.substr(end + start);1877} else {1878line = line_new + line.substr(end + start);1879}1880}1881}1882}18831884// -- r.move_and_slide_with_snap( a, b, c, d, e ) -> r.set_velocity(a) ... r.move_and_slide() KinematicBody1885if (contains_function_call(line, "move_and_slide_with_snap(")) {1886int start = line.find("move_and_slide_with_snap(");1887int end = get_end_parenthesis(line.substr(start)) + 1;1888if (end > -1) {1889String base_obj = get_object_of_execution(line.substr(0, start));1890String starting_space = get_starting_space(line);18911892Vector<String> parts = parse_arguments(line.substr(start, end));1893if (parts.size() >= 1) {1894String line_new;18951896// motion_velocity1897line_new += starting_space + base_obj + "set_velocity(" + parts[0] + ")\n";18981899// snap1900if (parts.size() >= 2) {1901line_new += starting_space + "# TODOConverter3To4 looks that snap in Godot 4 is float, not vector like in Godot 3 - previous value `" + parts[1] + "`\n";1902}19031904// up_direction1905if (parts.size() >= 3) {1906line_new += starting_space + base_obj + "set_up_direction(" + parts[2] + ")\n";1907}19081909// stop_on_slope1910if (parts.size() >= 4) {1911line_new += starting_space + base_obj + "set_floor_stop_on_slope_enabled(" + parts[3] + ")\n";1912}19131914// max_slides1915if (parts.size() >= 5) {1916line_new += starting_space + base_obj + "set_max_slides(" + parts[4] + ")\n";1917}19181919// floor_max_angle1920if (parts.size() >= 6) {1921line_new += starting_space + base_obj + "set_floor_max_angle(" + parts[5] + ")\n";1922}19231924// infiinite_interia1925if (parts.size() >= 7) {1926line_new += starting_space + "# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `" + parts[6] + "`\n";1927}19281929line_new += starting_space + base_obj + "move_and_slide()";19301931if (!line.begins_with(starting_space + "move_and_slide_with_snap")) {1932line = line_new + "\n" + line.substr(0, start) + "velocity" + line.substr(end + start);1933} else {1934line = line_new + line.substr(end + start);1935}1936}1937}1938}19391940// -- sort_custom( a , b ) -> sort_custom(Callable( a , b )) Object1941if (contains_function_call(line, "sort_custom(")) {1942int start = line.find("sort_custom(");1943int end = get_end_parenthesis(line.substr(start)) + 1;1944if (end > -1) {1945Vector<String> parts = parse_arguments(line.substr(start, end));1946if (parts.size() == 2) {1947line = line.substr(0, start) + "sort_custom(Callable(" + parts[0] + ", " + parts[1] + "))" + line.substr(end + start);1948}1949}1950}19511952// -- list_dir_begin( ) -> list_dir_begin() Object1953if (contains_function_call(line, "list_dir_begin(")) {1954int start = line.find("list_dir_begin(");1955int end = get_end_parenthesis(line.substr(start)) + 1;1956if (end > -1) {1957line = line.substr(0, start) + "list_dir_begin() " + line.substr(end + start) + "# TODOConverter3To4 fill missing arguments https://github.com/godotengine/godot/pull/40547";1958}1959}19601961// -- draw_line(1,2,3,4,5) -> draw_line(1, 2, 3, 4) CanvasItem1962if (contains_function_call(line, "draw_line(")) {1963int start = line.find("draw_line(");1964int end = get_end_parenthesis(line.substr(start)) + 1;1965if (end > -1) {1966Vector<String> parts = parse_arguments(line.substr(start, end));1967if (parts.size() == 5) {1968line = line.substr(0, start) + "draw_line(" + parts[0] + ", " + parts[1] + ", " + parts[2] + ", " + parts[3] + ")" + line.substr(end + start);1969}1970}1971}19721973// -- func c(var a, var b) -> func c(a, b)1974if (line.contains("func ") && line.contains("var ")) {1975int start = line.find("func ");1976start = line.substr(start).find_char('(') + start;1977int end = get_end_parenthesis(line.substr(start)) + 1;1978if (end > -1) {1979Vector<String> parts = parse_arguments(line.substr(start, end));19801981String start_string = line.substr(0, start) + "(";1982for (int i = 0; i < parts.size(); i++) {1983start_string += parts[i].strip_edges().trim_prefix("var ");1984if (i != parts.size() - 1) {1985start_string += ", ";1986}1987}1988line = start_string + ")" + line.substr(end + start);1989}1990}19911992// -- yield(this, \"timeout\") -> await this.timeout GDScript1993if (contains_function_call(line, "yield(")) {1994int start = line.find("yield(");1995int end = get_end_parenthesis(line.substr(start)) + 1;1996if (end > -1) {1997Vector<String> parts = parse_arguments(line.substr(start, end));1998if (parts.size() == 2) {1999if (builtin) {2000line = line.substr(0, start) + "await " + parts[0] + "." + parts[1].replace("\\\"", "").replace("\\'", "").remove_char(' ') + line.substr(end + start);2001} else {2002line = line.substr(0, start) + "await " + parts[0] + "." + parts[1].remove_chars("\"' ") + line.substr(end + start);2003}2004}2005}2006}20072008// -- parse_json( AA ) -> TODO Object2009if (contains_function_call(line, "parse_json(")) {2010int start = line.find("parse_json(");2011int end = get_end_parenthesis(line.substr(start)) + 1;2012if (end > -1) {2013Vector<String> parts = parse_arguments(line.substr(start, end));2014line = line.substr(0, start) + "JSON.new().stringify(" + connect_arguments(parts, 0) + ")" + line.substr(end + start);2015}2016}20172018// -- .xform(Vector3(a,b,c)) -> * Vector3(a,b,c) Transform2019if (line.contains(".xform(")) {2020int start = line.find(".xform(");2021int end = get_end_parenthesis(line.substr(start)) + 1;2022if (end > -1) {2023Vector<String> parts = parse_arguments(line.substr(start, end));2024if (parts.size() == 1) {2025line = line.substr(0, start) + " * (" + parts[0] + ")" + line.substr(end + start);2026}2027}2028}20292030// -- .xform_inv(Vector3(a,b,c)) -> * Vector3(a,b,c) Transform2031if (line.contains(".xform_inv(")) {2032int start = line.find(".xform_inv(");2033int end = get_end_parenthesis(line.substr(start)) + 1;2034if (end > -1) {2035String object_exec = get_object_of_execution(line.substr(0, start));2036if (line.contains(object_exec + ".xform")) {2037int start2 = line.find(object_exec + ".xform");2038Vector<String> parts = parse_arguments(line.substr(start, end));2039if (parts.size() == 1) {2040line = line.substr(0, start2) + "(" + parts[0] + ") * " + object_exec + line.substr(end + start);2041}2042}2043}2044}20452046// -- "(connect(A,B,C,D,E) != OK):", "(connect(A, Callable(B, C).bind(D), E) Object2047if (contains_function_call(line, "connect(")) {2048int start = line.find("connect(");2049int end = get_end_parenthesis(line.substr(start)) + 1;2050if (end > -1) {2051Vector<String> parts = parse_arguments(line.substr(start, end));2052if (parts.size() == 3) {2053line = line.substr(0, start) + "connect(" + parts[0] + ", Callable(" + parts[1] + ", " + parts[2] + "))" + line.substr(end + start);2054} else if (parts.size() >= 4) {2055line = line.substr(0, start) + "connect(" + parts[0] + ", Callable(" + parts[1] + ", " + parts[2] + ").bind(" + parts[3].lstrip(" [").rstrip("] ") + ")" + connect_arguments(parts, 4) + ")" + line.substr(end + start);2056}2057}2058}2059// -- disconnect(a,b,c) -> disconnect(a,Callable(b,c)) Object2060if (contains_function_call(line, "disconnect(")) {2061int start = line.find("disconnect(");2062int end = get_end_parenthesis(line.substr(start)) + 1;2063if (end > -1) {2064Vector<String> parts = parse_arguments(line.substr(start, end));2065if (parts.size() == 3) {2066line = line.substr(0, start) + "disconnect(" + parts[0] + ", Callable(" + parts[1] + ", " + parts[2] + "))" + line.substr(end + start);2067}2068}2069}2070// -- is_connected(a,b,c) -> is_connected(a,Callable(b,c)) Object2071if (contains_function_call(line, "is_connected(")) {2072int start = line.find("is_connected(");2073int end = get_end_parenthesis(line.substr(start)) + 1;2074if (end > -1) {2075Vector<String> parts = parse_arguments(line.substr(start, end));2076if (parts.size() == 3) {2077line = line.substr(0, start) + "is_connected(" + parts[0] + ", Callable(" + parts[1] + ", " + parts[2] + "))" + line.substr(end + start);2078}2079}2080}2081// -- "(tween_method(A,B,C,D,E) != OK):", "(tween_method(Callable(A,B),C,D,E) Object2082// -- "(tween_method(A,B,C,D,E,[F,G]) != OK):", "(tween_method(Callable(A,B).bind(F,G),C,D,E) Object2083if (contains_function_call(line, "tween_method(")) {2084int start = line.find("tween_method(");2085int end = get_end_parenthesis(line.substr(start)) + 1;2086if (end > -1) {2087Vector<String> parts = parse_arguments(line.substr(start, end));2088if (parts.size() == 5) {2089line = line.substr(0, start) + "tween_method(Callable(" + parts[0] + ", " + parts[1] + "), " + parts[2] + ", " + parts[3] + ", " + parts[4] + ")" + line.substr(end + start);2090} else if (parts.size() >= 6) {2091line = line.substr(0, start) + "tween_method(Callable(" + parts[0] + ", " + parts[1] + ").bind(" + connect_arguments(parts, 5).substr(1).lstrip(" [").rstrip("] ") + "), " + parts[2] + ", " + parts[3] + ", " + parts[4] + ")" + line.substr(end + start);2092}2093}2094}2095// -- "(tween_callback(A,B,[C,D]) != OK):", "(connect(Callable(A,B).bind(C,D)) Object2096if (contains_function_call(line, "tween_callback(")) {2097int start = line.find("tween_callback(");2098int end = get_end_parenthesis(line.substr(start)) + 1;2099if (end > -1) {2100Vector<String> parts = parse_arguments(line.substr(start, end));2101if (parts.size() == 2) {2102line = line.substr(0, start) + "tween_callback(Callable(" + parts[0] + ", " + parts[1] + "))" + line.substr(end + start);2103} else if (parts.size() >= 3) {2104line = line.substr(0, start) + "tween_callback(Callable(" + parts[0] + ", " + parts[1] + ").bind(" + connect_arguments(parts, 2).substr(1).lstrip(" [").rstrip("] ") + "))" + line.substr(end + start);2105}2106}2107}2108// -- start(a,b) -> start(Callable(a, b)) Thread2109// -- start(a,b,c,d) -> start(Callable(a, b).bind(c), d) Thread2110if (contains_function_call(line, "start(")) {2111int start = line.find("start(");2112int end = get_end_parenthesis(line.substr(start)) + 1;2113// Protection from 'func start'2114if (!line.begins_with("func ")) {2115if (end > -1) {2116Vector<String> parts = parse_arguments(line.substr(start, end));2117if (parts.size() == 2) {2118line = line.substr(0, start) + "start(Callable(" + parts[0] + ", " + parts[1] + "))" + line.substr(end + start);2119} else if (parts.size() >= 3) {2120line = line.substr(0, start) + "start(Callable(" + parts[0] + ", " + parts[1] + ").bind(" + parts[2] + ")" + connect_arguments(parts, 3) + ")" + line.substr(end + start);2121}2122}2123}2124}2125// -- func _init(p_x:int).(p_x): -> func _init(p_x:int):\n\tsuper(p_x) Object # https://github.com/godotengine/godot/issues/705422126if (line.contains(" _init(") && line.rfind_char(':') > 0) {2127// func _init(p_arg1).(super4, super5, super6)->void:2128// ^--^indent ^super_start super_end^2129int indent = line.count("\t", 0, line.find("func"));2130int super_start = line.find(".(");2131int super_end = line.rfind_char(')');2132if (super_start > 0 && super_end > super_start) {2133line = line.substr(0, super_start) + line.substr(super_end + 1) + "\n" + String("\t").repeat(indent + 1) + "super" + line.substr(super_start + 1, super_end - super_start);2134}2135}21362137// create_from_image(aa, bb) -> create_from_image(aa) #, bb ImageTexture2138if (contains_function_call(line, "create_from_image(")) {2139int start = line.find("create_from_image(");2140int end = get_end_parenthesis(line.substr(start)) + 1;2141if (end > -1) {2142Vector<String> parts = parse_arguments(line.substr(start, end));2143if (parts.size() == 2) {2144line = line.substr(0, start) + "create_from_image(" + parts[0] + ") " + "#," + parts[1] + line.substr(end + start);2145}2146}2147}2148// set_cell_item(a, b, c, d ,e) -> set_cell_item(Vector3(a, b, c), d ,e)2149if (contains_function_call(line, "set_cell_item(")) {2150int start = line.find("set_cell_item(");2151int end = get_end_parenthesis(line.substr(start)) + 1;2152if (end > -1) {2153Vector<String> parts = parse_arguments(line.substr(start, end));2154if (parts.size() > 2) {2155line = line.substr(0, start) + "set_cell_item(Vector3(" + parts[0] + ", " + parts[1] + ", " + parts[2] + ")" + connect_arguments(parts, 3).lstrip(" ") + ")" + line.substr(end + start);2156}2157}2158}2159// get_cell_item(a, b, c) -> get_cell_item(Vector3i(a, b, c))2160if (contains_function_call(line, "get_cell_item(")) {2161int start = line.find("get_cell_item(");2162int end = get_end_parenthesis(line.substr(start)) + 1;2163if (end > -1) {2164Vector<String> parts = parse_arguments(line.substr(start, end));2165if (parts.size() == 3) {2166line = line.substr(0, start) + "get_cell_item(Vector3i(" + parts[0] + ", " + parts[1] + ", " + parts[2] + "))" + line.substr(end + start);2167}2168}2169}2170// get_cell_item_orientation(a, b, c) -> get_cell_item_orientation(Vector3i(a, b, c))2171if (contains_function_call(line, "get_cell_item_orientation(")) {2172int start = line.find("get_cell_item_orientation(");2173int end = get_end_parenthesis(line.substr(start)) + 1;2174if (end > -1) {2175Vector<String> parts = parse_arguments(line.substr(start, end));2176if (parts.size() == 3) {2177line = line.substr(0, start) + "get_cell_item_orientation(Vector3i(" + parts[0] + ", " + parts[1] + ", " + parts[2] + "))" + line.substr(end + start);2178}2179}2180}2181// apply_impulse(A, B) -> apply_impulse(B, A)2182if (contains_function_call(line, "apply_impulse(")) {2183int start = line.find("apply_impulse(");2184int end = get_end_parenthesis(line.substr(start)) + 1;2185if (end > -1) {2186Vector<String> parts = parse_arguments(line.substr(start, end));2187if (parts.size() == 2) {2188line = line.substr(0, start) + "apply_impulse(" + parts[1] + ", " + parts[0] + ")" + line.substr(end + start);2189}2190}2191}2192// apply_force(A, B) -> apply_force(B, A)2193if (contains_function_call(line, "apply_force(")) {2194int start = line.find("apply_force(");2195int end = get_end_parenthesis(line.substr(start)) + 1;2196if (end > -1) {2197Vector<String> parts = parse_arguments(line.substr(start, end));2198if (parts.size() == 2) {2199line = line.substr(0, start) + "apply_force(" + parts[1] + ", " + parts[0] + ")" + line.substr(end + start);2200}2201}2202}2203// map_to_world(a, b, c) -> map_to_local(Vector3i(a, b, c))2204if (contains_function_call(line, "map_to_world(")) {2205int start = line.find("map_to_world(");2206int end = get_end_parenthesis(line.substr(start)) + 1;2207if (end > -1) {2208Vector<String> parts = parse_arguments(line.substr(start, end));2209if (parts.size() == 3) {2210line = line.substr(0, start) + "map_to_local(Vector3i(" + parts[0] + ", " + parts[1] + ", " + parts[2] + "))" + line.substr(end + start);2211} else if (parts.size() == 1) {2212line = line.substr(0, start) + "map_to_local(" + parts[0] + ")" + line.substr(end + start);2213}2214}2215}22162217// set_rotating(true) -> set_ignore_rotation(false)2218if (contains_function_call(line, "set_rotating(")) {2219int start = line.find("set_rotating(");2220int end = get_end_parenthesis(line.substr(start)) + 1;2221if (end > -1) {2222Vector<String> parts = parse_arguments(line.substr(start, end));2223if (parts.size() == 1) {2224String opposite = parts[0] == "true" ? "false" : "true";2225line = line.substr(0, start) + "set_ignore_rotation(" + opposite + ")";2226}2227}2228}22292230// OS.get_window_safe_area() -> DisplayServer.get_display_safe_area()2231if (line.contains("OS.get_window_safe_area(")) {2232int start = line.find("OS.get_window_safe_area(");2233int end = get_end_parenthesis(line.substr(start)) + 1;2234if (end > -1) {2235Vector<String> parts = parse_arguments(line.substr(start, end));2236if (parts.is_empty()) {2237line = line.substr(0, start) + "DisplayServer.get_display_safe_area()" + line.substr(end + start);2238}2239}2240}2241// draw_rect(a,b,c,d,e) -> draw_rect(a,b,c,d)#e) TODOConverter3To4 Antialiasing argument is missing2242if (contains_function_call(line, "draw_rect(")) {2243int start = line.find("draw_rect(");2244int end = get_end_parenthesis(line.substr(start)) + 1;2245if (end > -1) {2246Vector<String> parts = parse_arguments(line.substr(start, end));2247if (parts.size() == 5) {2248line = line.substr(0, start) + "draw_rect(" + parts[0] + ", " + parts[1] + ", " + parts[2] + ", " + parts[3] + ")" + line.substr(end + start) + "# " + parts[4] + ") TODOConverter3To4 Antialiasing argument is missing";2249}2250}2251}2252// get_focus_owner() -> get_viewport().gui_get_focus_owner()2253if (contains_function_call(line, "get_focus_owner()")) {2254line = line.replace("get_focus_owner()", "get_viewport().gui_get_focus_owner()");2255}22562257// button.pressed = 1 -> button.button_pressed = 12258if (line.contains(".pressed")) {2259int start = line.find(".pressed");2260bool foundNextEqual = false;2261String line_to_check = line.substr(start + String(".pressed").length());2262for (int current_index = 0; line_to_check.length() > current_index; current_index++) {2263char32_t chr = line_to_check.get(current_index);2264if (chr == '\t' || chr == ' ') {2265continue;2266} else if (chr == '=') {2267foundNextEqual = true;2268} else {2269break;2270}2271}2272if (foundNextEqual) {2273line = line.substr(0, start) + ".button_pressed" + line.substr(start + String(".pressed").length());2274}2275}22762277// rotating = true -> ignore_rotation = false # reversed "rotating" for Camera2D2278if (contains_function_call(line, "rotating")) {2279String function_name = "rotating";2280int start = line.find(function_name);2281bool foundNextEqual = false;2282String line_to_check = line.substr(start + function_name.length());2283String assigned_value;2284for (int current_index = 0; line_to_check.length() > current_index; current_index++) {2285char32_t chr = line_to_check.get(current_index);2286if (chr == '\t' || chr == ' ') {2287continue;2288} else if (chr == '=') {2289foundNextEqual = true;2290assigned_value = line.substr(start + function_name.length() + current_index + 1).strip_edges();2291assigned_value = assigned_value == "true" ? "false" : "true";2292} else {2293break;2294}2295}2296if (foundNextEqual) {2297line = line.substr(0, start) + "ignore_rotation = " + assigned_value + " # reversed \"rotating\" for Camera2D";2298}2299}23002301// OS -> Time functions2302if (line.contains("OS.get_ticks_msec")) {2303line = line.replace("OS.get_ticks_msec", "Time.get_ticks_msec");2304}2305if (line.contains("OS.get_ticks_usec")) {2306line = line.replace("OS.get_ticks_usec", "Time.get_ticks_usec");2307}2308if (line.contains("OS.get_unix_time")) {2309line = line.replace("OS.get_unix_time", "Time.get_unix_time_from_system");2310}2311if (line.contains("OS.get_datetime")) {2312line = line.replace("OS.get_datetime", "Time.get_datetime_dict_from_system");2313}23142315// OS -> DisplayServer2316if (line.contains("OS.get_display_cutouts")) {2317line = line.replace("OS.get_display_cutouts", "DisplayServer.get_display_cutouts");2318}2319if (line.contains("OS.get_screen_count")) {2320line = line.replace("OS.get_screen_count", "DisplayServer.get_screen_count");2321}2322if (line.contains("OS.get_screen_dpi")) {2323line = line.replace("OS.get_screen_dpi", "DisplayServer.screen_get_dpi");2324}2325if (line.contains("OS.get_screen_max_scale")) {2326line = line.replace("OS.get_screen_max_scale", "DisplayServer.screen_get_max_scale");2327}2328if (line.contains("OS.get_screen_position")) {2329line = line.replace("OS.get_screen_position", "DisplayServer.screen_get_position");2330}2331if (line.contains("OS.get_screen_refresh_rate")) {2332line = line.replace("OS.get_screen_refresh_rate", "DisplayServer.screen_get_refresh_rate");2333}2334if (line.contains("OS.get_screen_scale")) {2335line = line.replace("OS.get_screen_scale", "DisplayServer.screen_get_scale");2336}2337if (line.contains("OS.get_screen_size")) {2338line = line.replace("OS.get_screen_size", "DisplayServer.screen_get_size");2339}2340if (line.contains("OS.set_icon")) {2341line = line.replace("OS.set_icon", "DisplayServer.set_icon");2342}2343if (line.contains("OS.set_native_icon")) {2344line = line.replace("OS.set_native_icon", "DisplayServer.set_native_icon");2345}23462347// OS -> Window2348if (line.contains("OS.window_borderless")) {2349line = line.replace("OS.window_borderless", "get_window().borderless");2350}2351if (line.contains("OS.get_real_window_size")) {2352line = line.replace("OS.get_real_window_size", "get_window().get_size_with_decorations");2353}2354if (line.contains("OS.is_window_focused")) {2355line = line.replace("OS.is_window_focused", "get_window().has_focus");2356}2357if (line.contains("OS.move_window_to_foreground")) {2358line = line.replace("OS.move_window_to_foreground", "get_window().grab_focus");2359}2360if (line.contains("OS.request_attention")) {2361line = line.replace("OS.request_attention", "get_window().request_attention");2362}2363if (line.contains("OS.set_window_title")) {2364line = line.replace("OS.set_window_title", "get_window().set_title");2365}23662367// get_tree().set_input_as_handled() -> get_viewport().set_input_as_handled()2368if (line.contains("get_tree().set_input_as_handled()")) {2369line = line.replace("get_tree().set_input_as_handled()", "get_viewport().set_input_as_handled()");2370}23712372// Fix the simple case of using _unhandled_key_input2373// func _unhandled_key_input(event: InputEventKey) -> _unhandled_key_input(event: InputEvent)2374if (line.contains("_unhandled_key_input(event: InputEventKey)")) {2375line = line.replace("_unhandled_key_input(event: InputEventKey)", "_unhandled_key_input(event: InputEvent)");2376}23772378if (line.contains("Engine.editor_hint")) {2379line = line.replace("Engine.editor_hint", "Engine.is_editor_hint()");2380}2381}23822383void ProjectConverter3To4::process_csharp_line(String &line, const RegExContainer ®_container) {2384line = line.replace("OS.GetWindowSafeArea()", "DisplayServer.ScreenGetUsableRect()");23852386// GetTree().SetInputAsHandled() -> GetViewport().SetInputAsHandled()2387if (line.contains("GetTree().SetInputAsHandled()")) {2388line = line.replace("GetTree().SetInputAsHandled()", "GetViewport().SetInputAsHandled()");2389}23902391// Fix the simple case of using _UnhandledKeyInput2392// func _UnhandledKeyInput(InputEventKey @event) -> _UnhandledKeyInput(InputEvent @event)2393if (line.contains("_UnhandledKeyInput(InputEventKey @event)")) {2394line = line.replace("_UnhandledKeyInput(InputEventKey @event)", "_UnhandledKeyInput(InputEvent @event)");2395}23962397// -- Connect(,,,things) -> Connect(,Callable(,),things) Object2398if (line.contains("Connect(")) {2399int start = line.find("Connect(");2400// Protection from disconnect2401if (start == 0 || line.get(start - 1) != 's') {2402int end = get_end_parenthesis(line.substr(start)) + 1;2403if (end > -1) {2404Vector<String> parts = parse_arguments(line.substr(start, end));2405if (parts.size() >= 3) {2406line = line.substr(0, start) + "Connect(" + parts[0] + ", new Callable(" + parts[1] + ", " + parts[2] + ")" + connect_arguments(parts, 3) + ")" + line.substr(end + start);2407}2408}2409}2410}2411// -- Disconnect(a,b,c) -> Disconnect(a,Callable(b,c)) Object2412if (line.contains("Disconnect(")) {2413int start = line.find("Disconnect(");2414int end = get_end_parenthesis(line.substr(start)) + 1;2415if (end > -1) {2416Vector<String> parts = parse_arguments(line.substr(start, end));2417if (parts.size() == 3) {2418line = line.substr(0, start) + "Disconnect(" + parts[0] + ", new Callable(" + parts[1] + ", " + parts[2] + "))" + line.substr(end + start);2419}2420}2421}2422// -- IsConnected(a,b,c) -> IsConnected(a,Callable(b,c)) Object2423if (line.contains("IsConnected(")) {2424int start = line.find("IsConnected(");2425int end = get_end_parenthesis(line.substr(start)) + 1;2426if (end > -1) {2427Vector<String> parts = parse_arguments(line.substr(start, end));2428if (parts.size() == 3) {2429line = line.substr(0, start) + "IsConnected(" + parts[0] + ", new Callable(" + parts[1] + ", " + parts[2] + "))" + line.substr(end + start);2430}2431}2432}2433}24342435void ProjectConverter3To4::rename_csharp_functions(Vector<SourceLine> &source_lines, const RegExContainer ®_container) {2436for (SourceLine &source_line : source_lines) {2437if (source_line.is_comment) {2438continue;2439}24402441String &line = source_line.line;2442if (uint64_t(line.length()) <= maximum_line_length) {2443process_csharp_line(line, reg_container);2444}2445}2446}24472448Vector<String> ProjectConverter3To4::check_for_rename_csharp_functions(Vector<String> &lines, const RegExContainer ®_container) {2449int current_line = 1;24502451Vector<String> found_renames;24522453for (String &line : lines) {2454if (uint64_t(line.length()) <= maximum_line_length) {2455String old_line = line;2456process_csharp_line(line, reg_container);2457if (old_line != line) {2458found_renames.append(simple_line_formatter(current_line, old_line, line));2459}2460}2461}24622463return found_renames;2464}24652466void ProjectConverter3To4::rename_csharp_attributes(Vector<SourceLine> &source_lines, const RegExContainer ®_container) {2467static String error_message = "The master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using Multiplayer.GetRemoteSenderId()\n";24682469for (SourceLine &source_line : source_lines) {2470if (source_line.is_comment) {2471continue;2472}24732474String &line = source_line.line;2475if (uint64_t(line.length()) <= maximum_line_length) {2476line = reg_container.keyword_csharp_remote.sub(line, "[RPC(MultiplayerAPI.RPCMode.AnyPeer)]", true);2477line = reg_container.keyword_csharp_remotesync.sub(line, "[RPC(MultiplayerAPI.RPCMode.AnyPeer, CallLocal = true)]", true);2478line = reg_container.keyword_csharp_puppet.sub(line, "[RPC]", true);2479line = reg_container.keyword_csharp_puppetsync.sub(line, "[RPC(CallLocal = true)]", true);2480line = reg_container.keyword_csharp_master.sub(line, error_message + "[RPC]", true);2481line = reg_container.keyword_csharp_mastersync.sub(line, error_message + "[RPC(CallLocal = true)]", true);2482}2483}2484}24852486Vector<String> ProjectConverter3To4::check_for_rename_csharp_attributes(Vector<String> &lines, const RegExContainer ®_container) {2487int current_line = 1;24882489Vector<String> found_renames;24902491for (String &line : lines) {2492if (uint64_t(line.length()) <= maximum_line_length) {2493String old;2494old = line;2495line = reg_container.keyword_csharp_remote.sub(line, "[RPC(MultiplayerAPI.RPCMode.AnyPeer)]", true);2496if (old != line) {2497found_renames.append(line_formatter(current_line, "[Remote]", "[RPC(MultiplayerAPI.RPCMode.AnyPeer)]", line));2498}24992500old = line;2501line = reg_container.keyword_csharp_remotesync.sub(line, "[RPC(MultiplayerAPI.RPCMode.AnyPeer, CallLocal = true)]", true);2502if (old != line) {2503found_renames.append(line_formatter(current_line, "[RemoteSync]", "[RPC(MultiplayerAPI.RPCMode.AnyPeer, CallLocal = true)]", line));2504}25052506old = line;2507line = reg_container.keyword_csharp_puppet.sub(line, "[RPC]", true);2508if (old != line) {2509found_renames.append(line_formatter(current_line, "[Puppet]", "[RPC]", line));2510}25112512old = line;2513line = reg_container.keyword_csharp_puppetsync.sub(line, "[RPC(CallLocal = true)]", true);2514if (old != line) {2515found_renames.append(line_formatter(current_line, "[PuppetSync]", "[RPC(CallLocal = true)]", line));2516}25172518old = line;2519line = reg_container.keyword_csharp_master.sub(line, "[RPC]", true);2520if (old != line) {2521found_renames.append(line_formatter(current_line, "[Master]", "[RPC]", line));2522}25232524old = line;2525line = reg_container.keyword_csharp_mastersync.sub(line, "[RPC(CallLocal = true)]", true);2526if (old != line) {2527found_renames.append(line_formatter(current_line, "[MasterSync]", "[RPC(CallLocal = true)]", line));2528}2529}2530current_line++;2531}25322533return found_renames;2534}25352536_FORCE_INLINE_ static String builtin_escape(const String &p_str, bool p_builtin) {2537if (p_builtin) {2538return p_str.replace("\"", "\\\"");2539} else {2540return p_str;2541}2542}25432544void ProjectConverter3To4::rename_gdscript_keywords(Vector<SourceLine> &source_lines, const RegExContainer ®_container, bool builtin) {2545static String error_message = "The master and mastersync rpc behavior is not officially supported anymore. Try using another keyword or making custom logic using get_multiplayer().get_remote_sender_id()\n";25462547for (SourceLine &source_line : source_lines) {2548if (source_line.is_comment) {2549continue;2550}25512552String &line = source_line.line;2553if (uint64_t(line.length()) <= maximum_line_length) {2554if (line.contains("export")) {2555line = reg_container.keyword_gdscript_export_single.sub(line, "@export", true);2556}2557if (line.contains("export")) {2558line = reg_container.keyword_gdscript_export_multi.sub(line, "$1@export", true);2559}2560if (line.contains("onready")) {2561line = reg_container.keyword_gdscript_onready.sub(line, "@onready", true);2562}2563if (line.contains("remote")) {2564line = reg_container.keyword_gdscript_remote.sub(line, builtin_escape("@rpc(\"any_peer\") func", builtin), true);2565}2566if (line.contains("remote")) {2567line = reg_container.keyword_gdscript_remotesync.sub(line, builtin_escape("@rpc(\"any_peer\", \"call_local\") func", builtin), true);2568}2569if (line.contains("sync")) {2570line = reg_container.keyword_gdscript_sync.sub(line, builtin_escape("@rpc(\"any_peer\", \"call_local\") func", builtin), true);2571}2572if (line.contains("slave")) {2573line = reg_container.keyword_gdscript_slave.sub(line, "@rpc func", true);2574}2575if (line.contains("puppet")) {2576line = reg_container.keyword_gdscript_puppet.sub(line, "@rpc func", true);2577}2578if (line.contains("puppet")) {2579line = reg_container.keyword_gdscript_puppetsync.sub(line, builtin_escape("@rpc(\"call_local\") func", builtin), true);2580}2581if (line.contains("master")) {2582line = reg_container.keyword_gdscript_master.sub(line, error_message + "@rpc func", true);2583}2584if (line.contains("master")) {2585line = reg_container.keyword_gdscript_mastersync.sub(line, error_message + builtin_escape("@rpc(\"call_local\") func", builtin), true);2586}2587}2588}2589}25902591Vector<String> ProjectConverter3To4::check_for_rename_gdscript_keywords(Vector<String> &lines, const RegExContainer ®_container, bool builtin) {2592Vector<String> found_renames;25932594int current_line = 1;2595for (String &line : lines) {2596if (uint64_t(line.length()) <= maximum_line_length) {2597String old;25982599if (line.contains("tool")) {2600old = line;2601line = reg_container.keyword_gdscript_tool.sub(line, "@tool", true);2602if (old != line) {2603found_renames.append(line_formatter(current_line, "tool", "@tool", line));2604}2605}26062607if (line.contains("export")) {2608old = line;2609line = reg_container.keyword_gdscript_export_single.sub(line, "$1@export", true);2610if (old != line) {2611found_renames.append(line_formatter(current_line, "export", "@export", line));2612}2613}26142615if (line.contains("export")) {2616old = line;2617line = reg_container.keyword_gdscript_export_multi.sub(line, "@export", true);2618if (old != line) {2619found_renames.append(line_formatter(current_line, "export", "@export", line));2620}2621}26222623if (line.contains("onready")) {2624old = line;2625line = reg_container.keyword_gdscript_tool.sub(line, "@onready", true);2626if (old != line) {2627found_renames.append(line_formatter(current_line, "onready", "@onready", line));2628}2629}26302631if (line.contains("remote")) {2632old = line;2633line = reg_container.keyword_gdscript_remote.sub(line, builtin_escape("@rpc(\"any_peer\") func", builtin), true);2634if (old != line) {2635found_renames.append(line_formatter(current_line, "remote func", builtin_escape("@rpc(\"any_peer\") func", builtin), line));2636}2637}26382639if (line.contains("remote")) {2640old = line;2641line = reg_container.keyword_gdscript_remotesync.sub(line, builtin_escape("@rpc(\"any_peer\", \"call_local\")) func", builtin), true);2642if (old != line) {2643found_renames.append(line_formatter(current_line, "remotesync func", builtin_escape("@rpc(\"any_peer\", \"call_local\")) func", builtin), line));2644}2645}26462647if (line.contains("sync")) {2648old = line;2649line = reg_container.keyword_gdscript_sync.sub(line, builtin_escape("@rpc(\"any_peer\", \"call_local\")) func", builtin), true);2650if (old != line) {2651found_renames.append(line_formatter(current_line, "sync func", builtin_escape("@rpc(\"any_peer\", \"call_local\")) func", builtin), line));2652}2653}26542655if (line.contains("slave")) {2656old = line;2657line = reg_container.keyword_gdscript_slave.sub(line, "@rpc func", true);2658if (old != line) {2659found_renames.append(line_formatter(current_line, "slave func", "@rpc func", line));2660}2661}26622663if (line.contains("puppet")) {2664old = line;2665line = reg_container.keyword_gdscript_puppet.sub(line, "@rpc func", true);2666if (old != line) {2667found_renames.append(line_formatter(current_line, "puppet func", "@rpc func", line));2668}2669}26702671if (line.contains("puppet")) {2672old = line;2673line = reg_container.keyword_gdscript_puppetsync.sub(line, builtin_escape("@rpc(\"call_local\") func", builtin), true);2674if (old != line) {2675found_renames.append(line_formatter(current_line, "puppetsync func", builtin_escape("@rpc(\"call_local\") func", builtin), line));2676}2677}26782679if (line.contains("master")) {2680old = line;2681line = reg_container.keyword_gdscript_master.sub(line, "@rpc func", true);2682if (old != line) {2683found_renames.append(line_formatter(current_line, "master func", "@rpc func", line));2684}2685}26862687if (line.contains("master")) {2688old = line;2689line = reg_container.keyword_gdscript_master.sub(line, builtin_escape("@rpc(\"call_local\") func", builtin), true);2690if (old != line) {2691found_renames.append(line_formatter(current_line, "mastersync func", builtin_escape("@rpc(\"call_local\") func", builtin), line));2692}2693}2694}2695current_line++;2696}26972698return found_renames;2699}27002701void ProjectConverter3To4::rename_input_map_scancode(Vector<SourceLine> &source_lines, const RegExContainer ®_container) {2702// The old Special Key, now colliding with CMD_OR_CTRL.2703const int old_spkey = (1 << 24);27042705for (SourceLine &source_line : source_lines) {2706if (source_line.is_comment) {2707continue;2708}27092710String &line = source_line.line;2711if (uint64_t(line.length()) <= maximum_line_length) {2712TypedArray<RegExMatch> reg_match = reg_container.input_map_keycode.search_all(line);27132714for (int i = 0; i < reg_match.size(); ++i) {2715Ref<RegExMatch> match = reg_match[i];2716PackedStringArray strings = match->get_strings();2717int key = strings[3].to_int();27182719if (key & old_spkey) {2720// Create new key, clearing old Special Key and setting new one.2721key = (key & ~old_spkey) | (int)Key::SPECIAL;27222723line = line.replace(strings[0], String(",\"") + strings[1] + "scancode\":" + String::num_int64(key));2724}2725}2726}2727}2728}27292730void ProjectConverter3To4::rename_joypad_buttons_and_axes(Vector<SourceLine> &source_lines, const RegExContainer ®_container) {2731for (SourceLine &source_line : source_lines) {2732if (source_line.is_comment) {2733continue;2734}2735String &line = source_line.line;2736if (uint64_t(line.length()) <= maximum_line_length) {2737// Remap button indexes.2738TypedArray<RegExMatch> reg_match = reg_container.joypad_button_index.search_all(line);2739for (int i = 0; i < reg_match.size(); ++i) {2740Ref<RegExMatch> match = reg_match[i];2741PackedStringArray strings = match->get_strings();2742const String &button_index_entry = strings[0];2743int button_index_value = strings[1].to_int();2744if (button_index_value == 6) { // L2 and R2 are mapped to joypad axes in Godot 4.2745line = line.replace("InputEventJoypadButton", "InputEventJoypadMotion");2746line = line.replace(button_index_entry, ",\"axis\":4,\"axis_value\":1.0");2747} else if (button_index_value == 7) {2748line = line.replace("InputEventJoypadButton", "InputEventJoypadMotion");2749line = line.replace(button_index_entry, ",\"axis\":5,\"axis_value\":1.0");2750} else if (button_index_value < 22) { // There are no mappings for indexes greater than 22 in both Godot 3 & 4.2751const String &pressure_and_pressed_properties = strings[2];2752line = line.replace(button_index_entry, ",\"button_index\":" + String::num_int64(reg_container.joypad_button_mappings[button_index_value]) + "," + pressure_and_pressed_properties);2753}2754}2755// Remap axes. Only L2 and R2 need remapping.2756reg_match = reg_container.joypad_axis.search_all(line);2757for (int i = 0; i < reg_match.size(); ++i) {2758Ref<RegExMatch> match = reg_match[i];2759PackedStringArray strings = match->get_strings();2760const String &axis_entry = strings[0];2761int axis_value = strings[1].to_int();2762if (axis_value == 6) {2763line = line.replace(axis_entry, ",\"axis\":4");2764} else if (axis_value == 7) {2765line = line.replace(axis_entry, ",\"axis\":5");2766}2767}2768}2769}2770}27712772Vector<String> ProjectConverter3To4::check_for_rename_joypad_buttons_and_axes(Vector<String> &lines, const RegExContainer ®_container) {2773Vector<String> found_renames;2774int current_line = 1;2775for (String &line : lines) {2776if (uint64_t(line.length()) <= maximum_line_length) {2777// Remap button indexes.2778TypedArray<RegExMatch> reg_match = reg_container.joypad_button_index.search_all(line);2779for (int i = 0; i < reg_match.size(); ++i) {2780Ref<RegExMatch> match = reg_match[i];2781PackedStringArray strings = match->get_strings();2782const String &button_index_entry = strings[0];2783int button_index_value = strings[1].to_int();2784if (button_index_value == 6) { // L2 and R2 are mapped to joypad axes in Godot 4.2785found_renames.append(line_formatter(current_line, "InputEventJoypadButton", "InputEventJoypadMotion", line));2786found_renames.append(line_formatter(current_line, button_index_entry, ",\"axis\":4", line));2787} else if (button_index_value == 7) {2788found_renames.append(line_formatter(current_line, "InputEventJoypadButton", "InputEventJoypadMotion", line));2789found_renames.append(line_formatter(current_line, button_index_entry, ",\"axis\":5", line));2790} else if (button_index_value < 22) { // There are no mappings for indexes greater than 22 in both Godot 3 & 4.2791found_renames.append(line_formatter(current_line, "\"button_index\":" + strings[1], "\"button_index\":" + String::num_int64(reg_container.joypad_button_mappings[button_index_value]), line));2792}2793}2794// Remap axes. Only L2 and R2 need remapping.2795reg_match = reg_container.joypad_axis.search_all(line);2796for (int i = 0; i < reg_match.size(); ++i) {2797Ref<RegExMatch> match = reg_match[i];2798PackedStringArray strings = match->get_strings();2799const String &axis_entry = strings[0];2800int axis_value = strings[1].to_int();2801if (axis_value == 6) {2802found_renames.append(line_formatter(current_line, axis_entry, ",\"axis\":4", line));2803} else if (axis_value == 7) {2804found_renames.append(line_formatter(current_line, axis_entry, ",\"axis\":5", line));2805}2806}2807current_line++;2808}2809}2810return found_renames;2811}28122813Vector<String> ProjectConverter3To4::check_for_rename_input_map_scancode(Vector<String> &lines, const RegExContainer ®_container) {2814Vector<String> found_renames;28152816// The old Special Key, now colliding with CMD_OR_CTRL.2817const int old_spkey = (1 << 24);28182819int current_line = 1;2820for (String &line : lines) {2821if (uint64_t(line.length()) <= maximum_line_length) {2822TypedArray<RegExMatch> reg_match = reg_container.input_map_keycode.search_all(line);28232824for (int i = 0; i < reg_match.size(); ++i) {2825Ref<RegExMatch> match = reg_match[i];2826PackedStringArray strings = match->get_strings();2827int key = strings[3].to_int();28282829if (key & old_spkey) {2830// Create new key, clearing old Special Key and setting new one.2831key = (key & ~old_spkey) | (int)Key::SPECIAL;28322833found_renames.append(line_formatter(current_line, strings[3], String::num_int64(key), line));2834}2835}2836}2837current_line++;2838}2839return found_renames;2840}28412842void ProjectConverter3To4::rename_animation_suffixes(Vector<SourceLine> &source_lines, const RegExContainer ®_container) {2843for (SourceLine &source_line : source_lines) {2844if (source_line.is_comment) {2845continue;2846}2847String &line = source_line.line;2848if (uint64_t(line.length()) <= maximum_line_length) {2849TypedArray<RegExMatch> reg_match = reg_container.animation_suffix.search_all(line);2850for (int i = 0; i < reg_match.size(); ++i) {2851Ref<RegExMatch> match = reg_match[i];2852PackedStringArray strings = match->get_strings();2853String replacement = strings[1] + strings[2] + strings[4];2854line = line.replace(strings[0], replacement);2855}2856}2857}2858}28592860Vector<String> ProjectConverter3To4::check_for_rename_animation_suffixes(Vector<String> &lines, const RegExContainer ®_container) {2861Vector<String> found_renames;2862int current_line = 1;28632864for (String &line : lines) {2865if (uint64_t(line.length()) <= maximum_line_length) {2866TypedArray<RegExMatch> reg_match = reg_container.animation_suffix.search_all(line);2867for (int i = 0; i < reg_match.size(); ++i) {2868Ref<RegExMatch> match = reg_match[i];2869PackedStringArray strings = match->get_strings();2870String replacement = strings[1] + strings[2] + strings[4];2871found_renames.append(line_formatter(current_line, strings[0], replacement, line));2872}2873}2874current_line++;2875}2876return found_renames;2877}28782879void ProjectConverter3To4::custom_rename(Vector<SourceLine> &source_lines, const String &from, const String &to) {2880RegEx reg = RegEx(String("\\b") + from + "\\b");2881CRASH_COND(!reg.is_valid());2882for (SourceLine &source_line : source_lines) {2883if (source_line.is_comment) {2884continue;2885}28862887String &line = source_line.line;2888if (uint64_t(line.length()) <= maximum_line_length) {2889line = reg.sub(line, to, true);2890}2891}2892}28932894Vector<String> ProjectConverter3To4::check_for_custom_rename(Vector<String> &lines, const String &from, const String &to) {2895Vector<String> found_renames;28962897RegEx reg = RegEx(String("\\b") + from + "\\b");2898CRASH_COND(!reg.is_valid());28992900int current_line = 1;2901for (String &line : lines) {2902if (uint64_t(line.length()) <= maximum_line_length) {2903TypedArray<RegExMatch> reg_match = reg.search_all(line);2904if (reg_match.size() > 0) {2905found_renames.append(line_formatter(current_line, from.replace("\\.", "."), to, line)); // Without replacing it will print "\.shader" instead ".shader".2906}2907}2908current_line++;2909}2910return found_renames;2911}29122913void ProjectConverter3To4::rename_common(const char *array[][2], LocalVector<RegEx *> &cached_regexes, Vector<SourceLine> &source_lines) {2914for (SourceLine &source_line : source_lines) {2915if (source_line.is_comment) {2916continue;2917}29182919String &line = source_line.line;2920if (uint64_t(line.length()) <= maximum_line_length) {2921for (unsigned int current_index = 0; current_index < cached_regexes.size(); current_index++) {2922if (line.contains(array[current_index][0])) {2923line = cached_regexes[current_index]->sub(line, array[current_index][1], true);2924}2925}2926}2927}2928}29292930Vector<String> ProjectConverter3To4::check_for_rename_common(const char *array[][2], LocalVector<RegEx *> &cached_regexes, Vector<String> &lines) {2931Vector<String> found_renames;29322933int current_line = 1;29342935for (String &line : lines) {2936if (uint64_t(line.length()) <= maximum_line_length) {2937for (unsigned int current_index = 0; current_index < cached_regexes.size(); current_index++) {2938if (line.contains(array[current_index][0])) {2939TypedArray<RegExMatch> reg_match = cached_regexes[current_index]->search_all(line);2940if (reg_match.size() > 0) {2941found_renames.append(line_formatter(current_line, array[current_index][0], array[current_index][1], line));2942}2943}2944}2945}2946current_line++;2947}29482949return found_renames;2950}29512952// Prints full info about renamed things e.g.:2953// Line (67) remove -> remove_at - LINE """ doubler._blacklist.remove(0) """2954String ProjectConverter3To4::line_formatter(int current_line, String from, String to, String line) {2955if (from.size() > 200) {2956from = from.substr(0, 197) + "...";2957}2958if (to.size() > 200) {2959to = to.substr(0, 197) + "...";2960}2961if (line.size() > 400) {2962line = line.substr(0, 397) + "...";2963}29642965from = from.strip_escapes();2966to = to.strip_escapes();2967line = line.remove_chars("\r\n").strip_edges();29682969return vformat("Line(%d), %s -> %s - LINE \"\"\" %s \"\"\"", current_line, from, to, line);2970}29712972// Prints only full lines e.g.:2973// Line (1) - FULL LINES - """yield(get_tree().create_timer(3), 'timeout')""" =====> """ await get_tree().create_timer(3).timeout """2974String ProjectConverter3To4::simple_line_formatter(int current_line, String old_line, String new_line) {2975if (old_line.size() > 1000) {2976old_line = old_line.substr(0, 997) + "...";2977}2978if (new_line.size() > 1000) {2979new_line = new_line.substr(0, 997) + "...";2980}29812982old_line = old_line.remove_chars("\r\n").strip_edges();2983new_line = new_line.remove_chars("\r\n").strip_edges();29842985return vformat("Line (%d) - FULL LINES - \"\"\" %s \"\"\" =====> \"\"\" %s \"\"\"", current_line, old_line, new_line);2986}29872988// Collects string from vector strings2989String ProjectConverter3To4::collect_string_from_vector(Vector<SourceLine> &vector) {2990String string = "";2991for (int i = 0; i < vector.size(); i++) {2992string += vector[i].line;29932994if (i != vector.size() - 1) {2995string += "\n";2996}2997}2998return string;2999}30003001#endif // DISABLE_DEPRECATED300230033004