project_converter_3_to_4.cpp 158 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964
  1. /**************************************************************************/
  2. /* project_converter_3_to_4.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "project_converter_3_to_4.h"
  31. #ifndef DISABLE_DEPRECATED
  32. #include "modules/modules_enabled.gen.h" // For regex.
  33. #ifdef MODULE_REGEX_ENABLED
  34. #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/time.h"
  39. #include "core/templates/hash_map.h"
  40. #include "core/templates/list.h"
  41. #include "editor/renames_map_3_to_4.h"
  42. #include "modules/regex/regex.h"
  43. // Find "OS.set_property(x)", capturing x into $1.
  44. static String make_regex_gds_os_property_set(const String &name_set) {
  45. return String("\\bOS\\.") + name_set + "\\s*\\((.*)\\)";
  46. }
  47. // Find "OS.property = x", capturing x into $1 or $2.
  48. static String make_regex_gds_os_property_assign(const String &name) {
  49. return String("\\bOS\\.") + name + "\\s*=\\s*([^#]+)";
  50. }
  51. // Find "OS.property" OR "OS.get_property()" / "OS.is_property()".
  52. static String make_regex_gds_os_property_get(const String &name, const String &get) {
  53. return String("\\bOS\\.(") + get + "_)?" + name + "(\\s*\\(\\s*\\))?";
  54. }
  55. class ProjectConverter3To4::RegExContainer {
  56. public:
  57. // Custom GDScript.
  58. RegEx reg_is_empty = RegEx("\\bempty\\(");
  59. RegEx reg_super = RegEx("([\t ])\\.([a-zA-Z_])");
  60. RegEx reg_json_to = RegEx("\\bto_json\\b");
  61. RegEx reg_json_parse = RegEx("([\t ]{0,})([^\n]+)parse_json\\(([^\n]+)");
  62. RegEx reg_json_non_new = RegEx("([\t ]{0,})([^\n]+)JSON\\.parse\\(([^\n]+)");
  63. RegEx reg_json_print = RegEx("\\bJSON\\b\\.print\\(");
  64. RegEx reg_export_simple = RegEx("export[ ]*\\(([a-zA-Z0-9_]+)\\)[ ]*var[ ]+([a-zA-Z0-9_]+)");
  65. RegEx reg_export_typed = RegEx("export[ ]*\\(([a-zA-Z0-9_]+)\\)[ ]*var[ ]+([a-zA-Z0-9_]+)[ ]*:[ ]*[a-zA-Z0-9_]+");
  66. RegEx reg_export_inferred_type = RegEx("export[ ]*\\([a-zA-Z0-9_]+\\)[ ]*var[ ]+([a-zA-Z0-9_]+)[ ]*:[ ]*=");
  67. RegEx reg_export_advanced = RegEx("export[ ]*\\(([^)^\n]+)\\)[ ]*var[ ]+([a-zA-Z0-9_]+)([^\n]+)");
  68. RegEx reg_setget_setget = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+?)[ \t]*setget[ \t]+([a-zA-Z0-9_]+)[ \t]*,[ \t]*([a-zA-Z0-9_]+)");
  69. RegEx reg_setget_set = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+?)[ \t]*setget[ \t]+([a-zA-Z0-9_]+)[ \t]*[,]*[^\n]*$");
  70. RegEx reg_setget_get = RegEx("var[ ]+([a-zA-Z0-9_]+)([^\n]+?)[ \t]*setget[ \t]+,[ \t]*([a-zA-Z0-9_]+)[ \t]*$");
  71. RegEx reg_join = RegEx("([\\(\\)a-zA-Z0-9_]+)\\.join\\(([^\n^\\)]+)\\)");
  72. RegEx reg_image_lock = RegEx("([a-zA-Z0-9_\\.]+)\\.lock\\(\\)");
  73. RegEx reg_image_unlock = RegEx("([a-zA-Z0-9_\\.]+)\\.unlock\\(\\)");
  74. RegEx reg_instantiate = RegEx("\\.instance\\(([^\\)]*)\\)");
  75. // Simple OS properties with getters/setters.
  76. RegEx reg_os_current_screen = RegEx("\\bOS\\.((set_|get_)?)current_screen\\b");
  77. RegEx reg_os_min_window_size = RegEx("\\bOS\\.((set_|get_)?)min_window_size\\b");
  78. RegEx reg_os_max_window_size = RegEx("\\bOS\\.((set_|get_)?)max_window_size\\b");
  79. RegEx reg_os_window_position = RegEx("\\bOS\\.((set_|get_)?)window_position\\b");
  80. RegEx reg_os_window_size = RegEx("\\bOS\\.((set_|get_)?)window_size\\b");
  81. RegEx reg_os_getset_screen_orient = RegEx("\\bOS\\.(s|g)et_screen_orientation\\b");
  82. // OS property getters/setters for non trivial replacements.
  83. RegEx reg_os_set_window_resizable = RegEx(make_regex_gds_os_property_set("set_window_resizable"));
  84. RegEx reg_os_assign_window_resizable = RegEx(make_regex_gds_os_property_assign("window_resizable"));
  85. RegEx reg_os_is_window_resizable = RegEx(make_regex_gds_os_property_get("window_resizable", "is"));
  86. RegEx reg_os_set_fullscreen = RegEx(make_regex_gds_os_property_set("set_window_fullscreen"));
  87. RegEx reg_os_assign_fullscreen = RegEx(make_regex_gds_os_property_assign("window_fullscreen"));
  88. RegEx reg_os_is_fullscreen = RegEx(make_regex_gds_os_property_get("window_fullscreen", "is"));
  89. RegEx reg_os_set_maximized = RegEx(make_regex_gds_os_property_set("set_window_maximized"));
  90. RegEx reg_os_assign_maximized = RegEx(make_regex_gds_os_property_assign("window_maximized"));
  91. RegEx reg_os_is_maximized = RegEx(make_regex_gds_os_property_get("window_maximized", "is"));
  92. RegEx reg_os_set_minimized = RegEx(make_regex_gds_os_property_set("set_window_minimized"));
  93. RegEx reg_os_assign_minimized = RegEx(make_regex_gds_os_property_assign("window_minimized"));
  94. RegEx reg_os_is_minimized = RegEx(make_regex_gds_os_property_get("window_minimized", "is"));
  95. RegEx reg_os_set_vsync = RegEx(make_regex_gds_os_property_set("set_use_vsync"));
  96. RegEx reg_os_assign_vsync = RegEx(make_regex_gds_os_property_assign("vsync_enabled"));
  97. RegEx reg_os_is_vsync = RegEx(make_regex_gds_os_property_get("vsync_enabled", "is"));
  98. // OS properties specific cases & specific replacements.
  99. RegEx reg_os_assign_screen_orient = RegEx("^(\\s*)OS\\.screen_orientation\\s*=\\s*([^#]+)"); // $1 - indent, $2 - value
  100. RegEx reg_os_set_always_on_top = RegEx(make_regex_gds_os_property_set("set_window_always_on_top"));
  101. RegEx reg_os_is_always_on_top = RegEx("\\bOS\\.is_window_always_on_top\\s*\\(.*\\)");
  102. RegEx reg_os_set_borderless = RegEx(make_regex_gds_os_property_set("set_borderless_window"));
  103. RegEx reg_os_get_borderless = RegEx("\\bOS\\.get_borderless_window\\s*\\(\\s*\\)");
  104. RegEx reg_os_screen_orient_enum = RegEx("\\bOS\\.SCREEN_ORIENTATION_(\\w+)\\b"); // $1 - constant suffix
  105. // GDScript keywords.
  106. RegEx keyword_gdscript_tool = RegEx("^tool");
  107. RegEx keyword_gdscript_export_single = RegEx("^export");
  108. RegEx keyword_gdscript_export_multi = RegEx("([\t]+)export\\b");
  109. RegEx keyword_gdscript_onready = RegEx("^onready");
  110. RegEx keyword_gdscript_remote = RegEx("^remote func");
  111. RegEx keyword_gdscript_remotesync = RegEx("^remotesync func");
  112. RegEx keyword_gdscript_sync = RegEx("^sync func");
  113. RegEx keyword_gdscript_slave = RegEx("^slave func");
  114. RegEx keyword_gdscript_puppet = RegEx("^puppet func");
  115. RegEx keyword_gdscript_puppetsync = RegEx("^puppetsync func");
  116. RegEx keyword_gdscript_master = RegEx("^master func");
  117. RegEx keyword_gdscript_mastersync = RegEx("^mastersync func");
  118. RegEx gdscript_comment = RegEx("^\\s*#");
  119. RegEx csharp_comment = RegEx("^\\s*\\/\\/");
  120. // CSharp keywords.
  121. RegEx keyword_csharp_remote = RegEx("\\[Remote(Attribute)?(\\(\\))?\\]");
  122. RegEx keyword_csharp_remotesync = RegEx("\\[(Remote)?Sync(Attribute)?(\\(\\))?\\]");
  123. RegEx keyword_csharp_puppet = RegEx("\\[(Puppet|Slave)(Attribute)?(\\(\\))?\\]");
  124. RegEx keyword_csharp_puppetsync = RegEx("\\[PuppetSync(Attribute)?(\\(\\))?\\]");
  125. RegEx keyword_csharp_master = RegEx("\\[Master(Attribute)?(\\(\\))?\\]");
  126. RegEx keyword_csharp_mastersync = RegEx("\\[MasterSync(Attribute)?(\\(\\))?\\]");
  127. // Colors.
  128. LocalVector<RegEx *> color_regexes;
  129. LocalVector<String> color_renamed;
  130. RegEx color_hexadecimal_short_constructor = RegEx("Color\\(\"#?([a-fA-F0-9]{1})([a-fA-F0-9]{3})\\b");
  131. RegEx color_hexadecimal_full_constructor = RegEx("Color\\(\"#?([a-fA-F0-9]{2})([a-fA-F0-9]{6})\\b");
  132. // Classes.
  133. LocalVector<RegEx *> class_tscn_regexes;
  134. LocalVector<RegEx *> class_gd_regexes;
  135. LocalVector<RegEx *> class_shader_regexes;
  136. // Keycode.
  137. RegEx input_map_keycode = RegEx("\\b,\"((physical_)?)scancode\":(\\d+)\\b");
  138. // Button index and joypad axis.
  139. RegEx joypad_button_index = RegEx("\\b,\"button_index\":(\\d+),(\"pressure\":\\d+\\.\\d+,\"pressed\":(false|true))\\b");
  140. RegEx joypad_axis = RegEx("\\b,\"axis\":(\\d+)\\b");
  141. // Index represents Godot 3's value, entry represents Godot 4 value equivalency.
  142. // i.e: Button4(L1 - Godot3) -> joypad_button_mappings[4]=9 -> Button9(L1 - Godot4).
  143. int 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 };
  144. // Entries for L2 and R2 are -1 since they match to joypad axes and no longer to joypad buttons in Godot 4.
  145. LocalVector<RegEx *> class_regexes;
  146. RegEx class_temp_tscn = RegEx("\\bTEMP_RENAMED_CLASS.tscn\\b");
  147. RegEx class_temp_gd = RegEx("\\bTEMP_RENAMED_CLASS.gd\\b");
  148. RegEx class_temp_shader = RegEx("\\bTEMP_RENAMED_CLASS.shader\\b");
  149. LocalVector<String> class_temp_tscn_renames;
  150. LocalVector<String> class_temp_gd_renames;
  151. LocalVector<String> class_temp_shader_renames;
  152. // Common.
  153. LocalVector<RegEx *> enum_regexes;
  154. LocalVector<RegEx *> gdscript_function_regexes;
  155. LocalVector<RegEx *> project_settings_regexes;
  156. LocalVector<RegEx *> project_godot_regexes;
  157. LocalVector<RegEx *> input_map_regexes;
  158. LocalVector<RegEx *> gdscript_properties_regexes;
  159. LocalVector<RegEx *> gdscript_signals_regexes;
  160. LocalVector<RegEx *> shaders_regexes;
  161. LocalVector<RegEx *> builtin_types_regexes;
  162. LocalVector<RegEx *> theme_override_regexes;
  163. LocalVector<RegEx *> csharp_function_regexes;
  164. LocalVector<RegEx *> csharp_properties_regexes;
  165. LocalVector<RegEx *> csharp_signal_regexes;
  166. RegExContainer() {
  167. // Common.
  168. {
  169. // Enum.
  170. for (unsigned int current_index = 0; RenamesMap3To4::enum_renames[current_index][0]; current_index++) {
  171. enum_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::enum_renames[current_index][0] + "\\b")));
  172. }
  173. // GDScript functions.
  174. for (unsigned int current_index = 0; RenamesMap3To4::gdscript_function_renames[current_index][0]; current_index++) {
  175. gdscript_function_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::gdscript_function_renames[current_index][0] + "\\b")));
  176. }
  177. // Project Settings in scripts.
  178. for (unsigned int current_index = 0; RenamesMap3To4::project_settings_renames[current_index][0]; current_index++) {
  179. project_settings_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::project_settings_renames[current_index][0] + "\\b")));
  180. }
  181. // Project Settings in project.godot.
  182. for (unsigned int current_index = 0; RenamesMap3To4::project_godot_renames[current_index][0]; current_index++) {
  183. project_godot_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::project_godot_renames[current_index][0] + "\\b")));
  184. }
  185. // Input Map.
  186. for (unsigned int current_index = 0; RenamesMap3To4::input_map_renames[current_index][0]; current_index++) {
  187. input_map_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::input_map_renames[current_index][0] + "\\b")));
  188. }
  189. // GDScript properties.
  190. for (unsigned int current_index = 0; RenamesMap3To4::gdscript_properties_renames[current_index][0]; current_index++) {
  191. gdscript_properties_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::gdscript_properties_renames[current_index][0] + "\\b")));
  192. }
  193. // GDScript Signals.
  194. for (unsigned int current_index = 0; RenamesMap3To4::gdscript_signals_renames[current_index][0]; current_index++) {
  195. gdscript_signals_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::gdscript_signals_renames[current_index][0] + "\\b")));
  196. }
  197. // Shaders.
  198. for (unsigned int current_index = 0; RenamesMap3To4::shaders_renames[current_index][0]; current_index++) {
  199. shaders_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::shaders_renames[current_index][0] + "\\b")));
  200. }
  201. // Builtin types.
  202. for (unsigned int current_index = 0; RenamesMap3To4::builtin_types_renames[current_index][0]; current_index++) {
  203. builtin_types_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::builtin_types_renames[current_index][0] + "\\b")));
  204. }
  205. // Theme overrides.
  206. for (unsigned int current_index = 0; RenamesMap3To4::theme_override_renames[current_index][0]; current_index++) {
  207. theme_override_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::theme_override_renames[current_index][0] + "\\b")));
  208. }
  209. // CSharp function renames.
  210. for (unsigned int current_index = 0; RenamesMap3To4::csharp_function_renames[current_index][0]; current_index++) {
  211. csharp_function_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::csharp_function_renames[current_index][0] + "\\b")));
  212. }
  213. // CSharp properties renames.
  214. for (unsigned int current_index = 0; RenamesMap3To4::csharp_properties_renames[current_index][0]; current_index++) {
  215. csharp_properties_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::csharp_properties_renames[current_index][0] + "\\b")));
  216. }
  217. // CSharp signals renames.
  218. for (unsigned int current_index = 0; RenamesMap3To4::csharp_signals_renames[current_index][0]; current_index++) {
  219. csharp_signal_regexes.push_back(memnew(RegEx(String("\\b") + RenamesMap3To4::csharp_signals_renames[current_index][0] + "\\b")));
  220. }
  221. }
  222. // Colors.
  223. {
  224. for (unsigned int current_index = 0; RenamesMap3To4::color_renames[current_index][0]; current_index++) {
  225. color_regexes.push_back(memnew(RegEx(String("\\bColor.") + RenamesMap3To4::color_renames[current_index][0] + "\\b")));
  226. color_renamed.push_back(String("Color.") + RenamesMap3To4::color_renames[current_index][1]);
  227. }
  228. }
  229. // Classes.
  230. {
  231. for (unsigned int current_index = 0; RenamesMap3To4::class_renames[current_index][0]; current_index++) {
  232. const String class_name = RenamesMap3To4::class_renames[current_index][0];
  233. class_tscn_regexes.push_back(memnew(RegEx(String("\\b") + class_name + ".tscn\\b")));
  234. class_gd_regexes.push_back(memnew(RegEx(String("\\b") + class_name + ".gd\\b")));
  235. class_shader_regexes.push_back(memnew(RegEx(String("\\b") + class_name + ".shader\\b")));
  236. class_regexes.push_back(memnew(RegEx(String("\\b") + class_name + "\\b")));
  237. class_temp_tscn_renames.push_back(class_name + ".tscn");
  238. class_temp_gd_renames.push_back(class_name + ".gd");
  239. class_temp_shader_renames.push_back(class_name + ".shader");
  240. }
  241. }
  242. }
  243. ~RegExContainer() {
  244. for (RegEx *regex : color_regexes) {
  245. memdelete(regex);
  246. }
  247. for (unsigned int i = 0; i < class_tscn_regexes.size(); i++) {
  248. memdelete(class_tscn_regexes[i]);
  249. memdelete(class_gd_regexes[i]);
  250. memdelete(class_shader_regexes[i]);
  251. memdelete(class_regexes[i]);
  252. }
  253. for (RegEx *regex : enum_regexes) {
  254. memdelete(regex);
  255. }
  256. for (RegEx *regex : gdscript_function_regexes) {
  257. memdelete(regex);
  258. }
  259. for (RegEx *regex : project_settings_regexes) {
  260. memdelete(regex);
  261. }
  262. for (RegEx *regex : project_godot_regexes) {
  263. memdelete(regex);
  264. }
  265. for (RegEx *regex : input_map_regexes) {
  266. memdelete(regex);
  267. }
  268. for (RegEx *regex : gdscript_properties_regexes) {
  269. memdelete(regex);
  270. }
  271. for (RegEx *regex : gdscript_signals_regexes) {
  272. memdelete(regex);
  273. }
  274. for (RegEx *regex : shaders_regexes) {
  275. memdelete(regex);
  276. }
  277. for (RegEx *regex : builtin_types_regexes) {
  278. memdelete(regex);
  279. }
  280. for (RegEx *regex : theme_override_regexes) {
  281. memdelete(regex);
  282. }
  283. for (RegEx *regex : csharp_function_regexes) {
  284. memdelete(regex);
  285. }
  286. for (RegEx *regex : csharp_properties_regexes) {
  287. memdelete(regex);
  288. }
  289. for (RegEx *regex : csharp_signal_regexes) {
  290. memdelete(regex);
  291. }
  292. }
  293. };
  294. ProjectConverter3To4::ProjectConverter3To4(int p_maximum_file_size_kb, int p_maximum_line_length) {
  295. maximum_file_size = p_maximum_file_size_kb * 1024;
  296. maximum_line_length = p_maximum_line_length;
  297. }
  298. // Function responsible for converting project.
  299. bool ProjectConverter3To4::convert() {
  300. print_line("Starting conversion.");
  301. uint64_t conversion_start_time = Time::get_singleton()->get_ticks_msec();
  302. RegExContainer reg_container = RegExContainer();
  303. int cached_maximum_line_length = maximum_line_length;
  304. maximum_line_length = 10000; // Use only for tests bigger value, to not break them.
  305. ERR_FAIL_COND_V_MSG(!test_array_names(), false, "Cannot start converting due to problems with data in arrays.");
  306. ERR_FAIL_COND_V_MSG(!test_conversion(reg_container), false, "Aborting conversion due to validation tests failing");
  307. maximum_line_length = cached_maximum_line_length;
  308. // Checking if folder contains valid Godot 3 project.
  309. // Project should not be converted more than once.
  310. {
  311. String converter_text = "; Project was converted by built-in tool to Godot 4";
  312. ERR_FAIL_COND_V_MSG(!FileAccess::exists("project.godot"), false, "Current working directory doesn't contain a \"project.godot\" file for a Godot 3 project.");
  313. Error err = OK;
  314. String project_godot_content = FileAccess::get_file_as_string("project.godot", &err);
  315. ERR_FAIL_COND_V_MSG(err != OK, false, "Unable to read \"project.godot\".");
  316. ERR_FAIL_COND_V_MSG(project_godot_content.contains(converter_text), false, "Project was already converted with this tool.");
  317. Ref<FileAccess> file = FileAccess::open("project.godot", FileAccess::WRITE);
  318. ERR_FAIL_COND_V_MSG(file.is_null(), false, "Unable to open \"project.godot\".");
  319. file->store_string(converter_text + "\n" + project_godot_content);
  320. }
  321. Vector<String> collected_files = check_for_files();
  322. uint32_t converted_files = 0;
  323. // Check file by file.
  324. for (int i = 0; i < collected_files.size(); i++) {
  325. String file_name = collected_files[i];
  326. Vector<SourceLine> source_lines;
  327. uint32_t ignored_lines = 0;
  328. {
  329. Ref<FileAccess> file = FileAccess::open(file_name, FileAccess::READ);
  330. ERR_CONTINUE_MSG(file.is_null(), vformat("Unable to read content of \"%s\".", file_name));
  331. while (!file->eof_reached()) {
  332. String line = file->get_line();
  333. SourceLine source_line;
  334. source_line.line = line;
  335. source_line.is_comment = reg_container.gdscript_comment.search_all(line).size() > 0 || reg_container.csharp_comment.search_all(line).size() > 0;
  336. source_lines.append(source_line);
  337. }
  338. }
  339. String file_content_before = collect_string_from_vector(source_lines);
  340. uint64_t hash_before = file_content_before.hash();
  341. uint64_t file_size = file_content_before.size();
  342. print_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));
  343. Vector<String> reason;
  344. bool is_ignored = false;
  345. uint64_t start_time = Time::get_singleton()->get_ticks_msec();
  346. if (file_name.ends_with(".shader")) {
  347. DirAccess::remove_file_or_error(file_name.trim_prefix("res://"));
  348. file_name = file_name.replace(".shader", ".gdshader");
  349. }
  350. if (file_size < uint64_t(maximum_file_size)) {
  351. // ".tscn" must work exactly the same as ".gd" files because they may contain built-in Scripts.
  352. if (file_name.ends_with(".gd")) {
  353. fix_tool_declaration(source_lines, reg_container);
  354. rename_classes(source_lines, reg_container); // Using only specialized function.
  355. rename_common(RenamesMap3To4::enum_renames, reg_container.enum_regexes, source_lines);
  356. rename_colors(source_lines, reg_container); // Require to additional rename.
  357. rename_common(RenamesMap3To4::gdscript_function_renames, reg_container.gdscript_function_regexes, source_lines);
  358. rename_gdscript_functions(source_lines, reg_container, false); // Require to additional rename.
  359. rename_common(RenamesMap3To4::project_settings_renames, reg_container.project_settings_regexes, source_lines);
  360. rename_gdscript_keywords(source_lines, reg_container, false);
  361. rename_common(RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, source_lines);
  362. rename_common(RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, source_lines);
  363. rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, source_lines);
  364. rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, source_lines);
  365. rename_common(RenamesMap3To4::theme_override_renames, reg_container.theme_override_regexes, source_lines);
  366. custom_rename(source_lines, "\\.shader", ".gdshader");
  367. convert_hexadecimal_colors(source_lines, reg_container);
  368. } else if (file_name.ends_with(".tscn")) {
  369. fix_pause_mode(source_lines, reg_container);
  370. rename_classes(source_lines, reg_container); // Using only specialized function.
  371. rename_common(RenamesMap3To4::enum_renames, reg_container.enum_regexes, source_lines);
  372. rename_colors(source_lines, reg_container); // Require to do additional renames.
  373. rename_common(RenamesMap3To4::gdscript_function_renames, reg_container.gdscript_function_regexes, source_lines);
  374. rename_gdscript_functions(source_lines, reg_container, true); // Require to do additional renames.
  375. rename_common(RenamesMap3To4::project_settings_renames, reg_container.project_settings_regexes, source_lines);
  376. rename_gdscript_keywords(source_lines, reg_container, true);
  377. rename_common(RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, source_lines);
  378. rename_common(RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, source_lines);
  379. rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, source_lines);
  380. rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, source_lines);
  381. rename_common(RenamesMap3To4::theme_override_renames, reg_container.theme_override_regexes, source_lines);
  382. custom_rename(source_lines, "\\.shader", ".gdshader");
  383. convert_hexadecimal_colors(source_lines, reg_container);
  384. } else if (file_name.ends_with(".cs")) { // TODO, C# should use different methods.
  385. rename_classes(source_lines, reg_container); // Using only specialized function.
  386. rename_common(RenamesMap3To4::csharp_function_renames, reg_container.csharp_function_regexes, source_lines);
  387. rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, source_lines);
  388. rename_common(RenamesMap3To4::csharp_properties_renames, reg_container.csharp_properties_regexes, source_lines);
  389. rename_common(RenamesMap3To4::csharp_signals_renames, reg_container.csharp_signal_regexes, source_lines);
  390. rename_csharp_functions(source_lines, reg_container);
  391. rename_csharp_attributes(source_lines, reg_container);
  392. custom_rename(source_lines, "public class ", "public partial class ");
  393. convert_hexadecimal_colors(source_lines, reg_container);
  394. } else if (file_name.ends_with(".gdshader") || file_name.ends_with(".shader")) {
  395. rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, source_lines);
  396. } else if (file_name.ends_with("tres")) {
  397. rename_classes(source_lines, reg_container); // Using only specialized function.
  398. rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, source_lines);
  399. rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, source_lines);
  400. custom_rename(source_lines, "\\.shader", ".gdshader");
  401. } else if (file_name.ends_with("project.godot")) {
  402. rename_common(RenamesMap3To4::project_godot_renames, reg_container.project_godot_regexes, source_lines);
  403. rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, source_lines);
  404. rename_input_map_scancode(source_lines, reg_container);
  405. rename_joypad_buttons_and_axes(source_lines, reg_container);
  406. rename_common(RenamesMap3To4::input_map_renames, reg_container.input_map_regexes, source_lines);
  407. custom_rename(source_lines, "config_version=4", "config_version=5");
  408. } else if (file_name.ends_with(".csproj")) {
  409. // TODO
  410. } else if (file_name.ends_with(".import")) {
  411. for (SourceLine &source_line : source_lines) {
  412. String &line = source_line.line;
  413. if (line.contains("nodes/root_type=\"Spatial\"")) {
  414. line = "nodes/root_type=\"Node3D\"";
  415. } else if (line == "importer=\"ogg_vorbis\"") {
  416. line = "importer=\"oggvorbisstr\"";
  417. }
  418. }
  419. } else {
  420. ERR_PRINT(file_name + " is not supported!");
  421. continue;
  422. }
  423. for (SourceLine &source_line : source_lines) {
  424. if (source_line.is_comment) {
  425. continue;
  426. }
  427. String &line = source_line.line;
  428. if (uint64_t(line.length()) > maximum_line_length) {
  429. ignored_lines += 1;
  430. }
  431. }
  432. } else {
  433. reason.append(vformat(" ERROR: File has exceeded the maximum size allowed - %d KB", maximum_file_size / 1024));
  434. is_ignored = true;
  435. }
  436. uint64_t end_time = Time::get_singleton()->get_ticks_msec();
  437. if (is_ignored) {
  438. String end_message = vformat(" Checking file took %d ms.", end_time - start_time);
  439. print_line(end_message);
  440. } else {
  441. String file_content_after = collect_string_from_vector(source_lines);
  442. uint64_t hash_after = file_content_after.hash64();
  443. // Don't need to save file without any changes.
  444. // Save if this is a shader, because it was renamed.
  445. if (hash_before != hash_after || file_name.ends_with(".gdshader")) {
  446. converted_files++;
  447. Ref<FileAccess> file = FileAccess::open(file_name, FileAccess::WRITE);
  448. ERR_CONTINUE_MSG(file.is_null(), vformat("Unable to apply changes to \"%s\", no writing access.", file_name));
  449. file->store_string(file_content_after);
  450. reason.append(vformat(" File was changed, conversion took %d ms.", end_time - start_time));
  451. } else {
  452. reason.append(vformat(" File was left unchanged, checking took %d ms.", end_time - start_time));
  453. }
  454. if (ignored_lines != 0) {
  455. reason.append(vformat(" Ignored %d lines, because their length exceeds maximum allowed characters - %d.", ignored_lines, maximum_line_length));
  456. }
  457. }
  458. for (int k = 0; k < reason.size(); k++) {
  459. print_line(reason[k]);
  460. }
  461. }
  462. print_line(vformat("Conversion ended - all files(%d), converted files: (%d), not converted files: (%d).", collected_files.size(), converted_files, collected_files.size() - converted_files));
  463. uint64_t conversion_end_time = Time::get_singleton()->get_ticks_msec();
  464. print_line(vformat("Conversion of all files took %10.3f seconds.", (conversion_end_time - conversion_start_time) / 1000.0));
  465. return true;
  466. }
  467. // Function responsible for validating project conversion.
  468. bool ProjectConverter3To4::validate_conversion() {
  469. print_line("Starting checking if project conversion can be done.");
  470. uint64_t conversion_start_time = Time::get_singleton()->get_ticks_msec();
  471. RegExContainer reg_container = RegExContainer();
  472. int cached_maximum_line_length = maximum_line_length;
  473. maximum_line_length = 10000; // To avoid breaking the tests, only use this for the their larger value.
  474. ERR_FAIL_COND_V_MSG(!test_array_names(), false, "Cannot start converting due to problems with data in arrays.");
  475. ERR_FAIL_COND_V_MSG(!test_conversion(reg_container), false, "Aborting conversion due to validation tests failing");
  476. maximum_line_length = cached_maximum_line_length;
  477. // Checking if folder contains valid Godot 3 project.
  478. // Project should not be converted more than once.
  479. {
  480. String conventer_text = "; Project was converted by built-in tool to Godot 4";
  481. ERR_FAIL_COND_V_MSG(!FileAccess::exists("project.godot"), false, "Current directory doesn't contain any Godot 3 project");
  482. Error err = OK;
  483. String project_godot_content = FileAccess::get_file_as_string("project.godot", &err);
  484. ERR_FAIL_COND_V_MSG(err != OK, false, "Failed to read content of \"project.godot\" file.");
  485. ERR_FAIL_COND_V_MSG(project_godot_content.contains(conventer_text), false, "Project already was converted with this tool.");
  486. }
  487. Vector<String> collected_files = check_for_files();
  488. uint32_t converted_files = 0;
  489. // Check file by file.
  490. for (int i = 0; i < collected_files.size(); i++) {
  491. const String &file_name = collected_files[i];
  492. Vector<String> lines;
  493. uint32_t ignored_lines = 0;
  494. uint64_t file_size = 0;
  495. {
  496. Ref<FileAccess> file = FileAccess::open(file_name, FileAccess::READ);
  497. ERR_CONTINUE_MSG(file.is_null(), vformat("Unable to read content of \"%s\".", file_name));
  498. while (!file->eof_reached()) {
  499. String line = file->get_line();
  500. file_size += line.size();
  501. lines.append(line);
  502. }
  503. }
  504. print_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));
  505. Vector<String> changed_elements;
  506. Vector<String> reason;
  507. bool is_ignored = false;
  508. uint64_t start_time = Time::get_singleton()->get_ticks_msec();
  509. if (file_name.ends_with(".shader")) {
  510. reason.append("\tFile extension will be renamed from \"shader\" to \"gdshader\".");
  511. }
  512. if (file_size < uint64_t(maximum_file_size)) {
  513. if (file_name.ends_with(".gd")) {
  514. changed_elements.append_array(check_for_rename_classes(lines, reg_container));
  515. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::enum_renames, reg_container.enum_regexes, lines));
  516. changed_elements.append_array(check_for_rename_colors(lines, reg_container));
  517. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_function_renames, reg_container.gdscript_function_regexes, lines));
  518. changed_elements.append_array(check_for_rename_gdscript_functions(lines, reg_container, false));
  519. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::project_settings_renames, reg_container.project_settings_regexes, lines));
  520. changed_elements.append_array(check_for_rename_gdscript_keywords(lines, reg_container, false));
  521. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, lines));
  522. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, lines));
  523. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, lines));
  524. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, lines));
  525. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::theme_override_renames, reg_container.theme_override_regexes, lines));
  526. changed_elements.append_array(check_for_custom_rename(lines, "\\.shader", ".gdshader"));
  527. } else if (file_name.ends_with(".tscn")) {
  528. changed_elements.append_array(check_for_rename_classes(lines, reg_container));
  529. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::enum_renames, reg_container.enum_regexes, lines));
  530. changed_elements.append_array(check_for_rename_colors(lines, reg_container));
  531. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_function_renames, reg_container.gdscript_function_regexes, lines));
  532. changed_elements.append_array(check_for_rename_gdscript_functions(lines, reg_container, true));
  533. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::project_settings_renames, reg_container.project_settings_regexes, lines));
  534. changed_elements.append_array(check_for_rename_gdscript_keywords(lines, reg_container, true));
  535. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, lines));
  536. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, lines));
  537. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, lines));
  538. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, lines));
  539. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::theme_override_renames, reg_container.theme_override_regexes, lines));
  540. changed_elements.append_array(check_for_custom_rename(lines, "\\.shader", ".gdshader"));
  541. } else if (file_name.ends_with(".cs")) {
  542. changed_elements.append_array(check_for_rename_classes(lines, reg_container));
  543. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::csharp_function_renames, reg_container.csharp_function_regexes, lines));
  544. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, lines));
  545. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::csharp_properties_renames, reg_container.csharp_properties_regexes, lines));
  546. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::csharp_signals_renames, reg_container.csharp_signal_regexes, lines));
  547. changed_elements.append_array(check_for_rename_csharp_functions(lines, reg_container));
  548. changed_elements.append_array(check_for_rename_csharp_attributes(lines, reg_container));
  549. changed_elements.append_array(check_for_custom_rename(lines, "public class ", "public partial class "));
  550. } else if (file_name.ends_with(".gdshader") || file_name.ends_with(".shader")) {
  551. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, lines));
  552. } else if (file_name.ends_with("tres")) {
  553. changed_elements.append_array(check_for_rename_classes(lines, reg_container));
  554. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, lines));
  555. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, lines));
  556. changed_elements.append_array(check_for_custom_rename(lines, "\\.shader", ".gdshader"));
  557. } else if (file_name.ends_with("project.godot")) {
  558. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::project_godot_renames, reg_container.project_godot_regexes, lines));
  559. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, lines));
  560. changed_elements.append_array(check_for_rename_input_map_scancode(lines, reg_container));
  561. changed_elements.append_array(check_for_rename_joypad_buttons_and_axes(lines, reg_container));
  562. changed_elements.append_array(check_for_rename_common(RenamesMap3To4::input_map_renames, reg_container.input_map_regexes, lines));
  563. } else if (file_name.ends_with(".csproj")) {
  564. // TODO
  565. } else {
  566. ERR_PRINT(vformat("\"%s\", is not supported!", file_name));
  567. continue;
  568. }
  569. for (String &line : lines) {
  570. if (uint64_t(line.length()) > maximum_line_length) {
  571. ignored_lines += 1;
  572. }
  573. }
  574. } else {
  575. reason.append(vformat("\tERROR: File has exceeded the maximum size allowed - %d KB.", maximum_file_size / 1024));
  576. is_ignored = true;
  577. }
  578. uint64_t end_time = Time::get_singleton()->get_ticks_msec();
  579. String end_message = vformat(" Checking file took %10.3f ms.", (end_time - start_time) / 1000.0);
  580. if (ignored_lines != 0) {
  581. end_message += vformat(" Ignored %d lines, because their length exceeds maximum allowed characters - %d.", ignored_lines, maximum_line_length);
  582. }
  583. print_line(end_message);
  584. for (int k = 0; k < reason.size(); k++) {
  585. print_line(reason[k]);
  586. }
  587. if (changed_elements.size() > 0 && !is_ignored) {
  588. converted_files++;
  589. for (int k = 0; k < changed_elements.size(); k++) {
  590. print_line(String("\t\t") + changed_elements[k]);
  591. }
  592. }
  593. }
  594. print_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));
  595. uint64_t conversion_end_time = Time::get_singleton()->get_ticks_msec();
  596. print_line(vformat("Conversion of all files took %10.3f seconds.", (conversion_end_time - conversion_start_time) / 1000.0));
  597. return true;
  598. }
  599. // Collect files which will be checked, excluding ".txt", ".mp4", ".wav" etc. files.
  600. Vector<String> ProjectConverter3To4::check_for_files() {
  601. Vector<String> collected_files = Vector<String>();
  602. Vector<String> directories_to_check = Vector<String>();
  603. directories_to_check.push_back("res://");
  604. while (!directories_to_check.is_empty()) {
  605. String path = directories_to_check.get(directories_to_check.size() - 1); // Is there any pop_back function?
  606. directories_to_check.resize(directories_to_check.size() - 1); // Remove last element
  607. Ref<DirAccess> dir = DirAccess::open(path);
  608. if (dir.is_valid()) {
  609. dir->set_include_hidden(true);
  610. dir->list_dir_begin();
  611. String current_dir = dir->get_current_dir();
  612. String file_name = dir->_get_next();
  613. while (file_name != "") {
  614. if (file_name == ".git" || file_name == ".godot") {
  615. file_name = dir->_get_next();
  616. continue;
  617. }
  618. if (dir->current_is_dir()) {
  619. directories_to_check.append(current_dir.path_join(file_name) + "/");
  620. } else {
  621. bool proper_extension = false;
  622. if (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")) {
  623. proper_extension = true;
  624. }
  625. if (proper_extension) {
  626. collected_files.append(current_dir.path_join(file_name));
  627. }
  628. }
  629. file_name = dir->_get_next();
  630. }
  631. } else {
  632. print_verbose("Failed to open " + path);
  633. }
  634. }
  635. return collected_files;
  636. }
  637. Vector<SourceLine> ProjectConverter3To4::split_lines(const String &text) {
  638. Vector<String> lines = text.split("\n");
  639. Vector<SourceLine> source_lines;
  640. for (String &line : lines) {
  641. SourceLine source_line;
  642. source_line.line = line;
  643. source_line.is_comment = false;
  644. source_lines.append(source_line);
  645. }
  646. return source_lines;
  647. }
  648. // Test expected results of gdscript
  649. bool ProjectConverter3To4::test_conversion_gdscript_builtin(const String &name, const String &expected, void (ProjectConverter3To4::*func)(Vector<SourceLine> &, const RegExContainer &, bool), const String &what, const RegExContainer &reg_container, bool builtin_script) {
  650. Vector<SourceLine> got = split_lines(name);
  651. (this->*func)(got, reg_container, builtin_script);
  652. String got_str = collect_string_from_vector(got);
  653. ERR_FAIL_COND_V_MSG(expected != got_str, false, vformat("Failed to convert %s \"%s\" to \"%s\", got instead \"%s\"", what, name, expected, got_str));
  654. return true;
  655. }
  656. bool ProjectConverter3To4::test_conversion_with_regex(const String &name, const String &expected, void (ProjectConverter3To4::*func)(Vector<SourceLine> &, const RegExContainer &), const String &what, const RegExContainer &reg_container) {
  657. Vector<SourceLine> got = split_lines(name);
  658. (this->*func)(got, reg_container);
  659. String got_str = collect_string_from_vector(got);
  660. ERR_FAIL_COND_V_MSG(expected != got_str, false, vformat("Failed to convert %s \"%s\" to \"%s\", got instead \"%s\"", what, name, expected, got_str));
  661. return true;
  662. }
  663. bool ProjectConverter3To4::test_conversion_basic(const String &name, const String &expected, const char *array[][2], LocalVector<RegEx *> &regex_cache, const String &what) {
  664. Vector<SourceLine> got = split_lines(name);
  665. rename_common(array, regex_cache, got);
  666. String got_str = collect_string_from_vector(got);
  667. ERR_FAIL_COND_V_MSG(expected != got_str, false, vformat("Failed to convert %s \"%s\" to \"%s\", got instead \"%s\"", what, name, expected, got_str));
  668. return true;
  669. }
  670. // Validate if conversions are proper.
  671. bool ProjectConverter3To4::test_conversion(RegExContainer &reg_container) {
  672. bool valid = true;
  673. valid = valid && test_conversion_with_regex("tool", "@tool", &ProjectConverter3To4::fix_tool_declaration, "gdscript keyword", reg_container);
  674. valid = valid && test_conversion_with_regex("\n tool", "\n tool", &ProjectConverter3To4::fix_tool_declaration, "gdscript keyword", reg_container);
  675. valid = valid && test_conversion_with_regex("\n\ntool", "@tool\n\n", &ProjectConverter3To4::fix_tool_declaration, "gdscript keyword", reg_container);
  676. valid = valid && test_conversion_with_regex("pause_mode = 2", "pause_mode = 3", &ProjectConverter3To4::fix_pause_mode, "pause_mode", reg_container);
  677. valid = valid && test_conversion_with_regex("pause_mode = 1", "pause_mode = 1", &ProjectConverter3To4::fix_pause_mode, "pause_mode", reg_container);
  678. valid = valid && test_conversion_with_regex("pause_mode = 3", "pause_mode = 3", &ProjectConverter3To4::fix_pause_mode, "pause_mode", reg_container);
  679. valid = valid && test_conversion_with_regex("somepause_mode = 2", "somepause_mode = 2", &ProjectConverter3To4::fix_pause_mode, "pause_mode", reg_container);
  680. valid = valid && test_conversion_with_regex("pause_mode_ext = 2", "pause_mode_ext = 2", &ProjectConverter3To4::fix_pause_mode, "pause_mode", reg_container);
  681. valid = valid && test_conversion_basic("TYPE_REAL", "TYPE_FLOAT", RenamesMap3To4::enum_renames, reg_container.enum_regexes, "enum");
  682. valid = valid && test_conversion_basic("can_instance", "can_instantiate", RenamesMap3To4::gdscript_function_renames, reg_container.gdscript_function_regexes, "gdscript function");
  683. valid = valid && test_conversion_basic("CanInstance", "CanInstantiate", RenamesMap3To4::csharp_function_renames, reg_container.csharp_function_regexes, "csharp function");
  684. valid = valid && test_conversion_basic("translation", "position", RenamesMap3To4::gdscript_properties_renames, reg_container.gdscript_properties_regexes, "gdscript property");
  685. valid = valid && test_conversion_basic("Translation", "Position", RenamesMap3To4::csharp_properties_renames, reg_container.csharp_properties_regexes, "csharp property");
  686. valid = valid && test_conversion_basic("NORMALMAP", "NORMAL_MAP", RenamesMap3To4::shaders_renames, reg_container.shaders_regexes, "shader");
  687. valid = valid && test_conversion_basic("text_entered", "text_submitted", RenamesMap3To4::gdscript_signals_renames, reg_container.gdscript_signals_regexes, "gdscript signal");
  688. valid = valid && test_conversion_basic("TextEntered", "TextSubmitted", RenamesMap3To4::csharp_signals_renames, reg_container.csharp_signal_regexes, "csharp signal");
  689. valid = 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");
  690. valid = 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");
  691. valid = valid && test_conversion_basic("Transform", "Transform3D", RenamesMap3To4::builtin_types_renames, reg_container.builtin_types_regexes, "builtin type");
  692. valid = valid && test_conversion_basic("custom_constants/margin_right", "theme_override_constants/margin_right", RenamesMap3To4::theme_override_renames, reg_container.theme_override_regexes, "theme overrides");
  693. // Custom Renames.
  694. valid = 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);
  695. valid = 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);
  696. valid = 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);
  697. valid = valid && test_conversion_with_regex("[Remote]", "[RPC(MultiplayerAPI.RPCMode.AnyPeer)]", &ProjectConverter3To4::rename_csharp_attributes, "custom rename csharp", reg_container);
  698. valid = valid && test_conversion_with_regex("[RemoteSync]", "[RPC(MultiplayerAPI.RPCMode.AnyPeer, CallLocal = true)]", &ProjectConverter3To4::rename_csharp_attributes, "custom rename csharp", reg_container);
  699. valid = valid && test_conversion_with_regex("[Sync]", "[RPC(MultiplayerAPI.RPCMode.AnyPeer, CallLocal = true)]", &ProjectConverter3To4::rename_csharp_attributes, "custom rename csharp", reg_container);
  700. valid = valid && test_conversion_with_regex("[Slave]", "[RPC]", &ProjectConverter3To4::rename_csharp_attributes, "custom rename csharp", reg_container);
  701. valid = valid && test_conversion_with_regex("[Puppet]", "[RPC]", &ProjectConverter3To4::rename_csharp_attributes, "custom rename csharp", reg_container);
  702. valid = valid && test_conversion_with_regex("[PuppetSync]", "[RPC(CallLocal = true)]", &ProjectConverter3To4::rename_csharp_attributes, "custom rename csharp", reg_container);
  703. valid = 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);
  704. valid = 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);
  705. valid = 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);
  706. valid = 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);
  707. valid = 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);
  708. valid = 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);
  709. valid = 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);
  710. valid = 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);
  711. valid = 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);
  712. valid = 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);
  713. valid = 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);
  714. valid = 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);
  715. valid = 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);
  716. valid = 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);
  717. valid = 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);
  718. valid = 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);
  719. valid = 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);
  720. valid = 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);
  721. valid = 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);
  722. valid = 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);
  723. valid = 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);
  724. valid = 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);
  725. valid = 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);
  726. valid = 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);
  727. valid = 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);
  728. valid = 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);
  729. valid = 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);
  730. valid = 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);
  731. valid = 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);
  732. valid = 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);
  733. valid = 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);
  734. valid = 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);
  735. valid = 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);
  736. valid = 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);
  737. valid = 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);
  738. valid = 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);
  739. valid = 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);
  740. valid = 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);
  741. valid = valid && test_conversion_gdscript_builtin("sort_custom( a , b )", "sort_custom(Callable(a, b))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  742. valid = valid && test_conversion_gdscript_builtin("func c(var a, var b)", "func c(a, b)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  743. valid = 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);
  744. valid = 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);
  745. valid = 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);
  746. valid = 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);
  747. valid = valid && test_conversion_gdscript_builtin("\tmtx.lock()", "\tmtx.lock()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  748. valid = valid && test_conversion_gdscript_builtin("\tmutex.unlock()", "\tmutex.unlock()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  749. valid = valid && test_conversion_with_regex("extends CSGBox", "extends CSGBox3D", &ProjectConverter3To4::rename_classes, "classes", reg_container);
  750. valid = valid && test_conversion_with_regex("CSGBox", "CSGBox3D", &ProjectConverter3To4::rename_classes, "classes", reg_container);
  751. valid = valid && test_conversion_with_regex("Spatial", "Node3D", &ProjectConverter3To4::rename_classes, "classes", reg_container);
  752. valid = valid && test_conversion_with_regex("Spatial.tscn", "Spatial.tscn", &ProjectConverter3To4::rename_classes, "classes", reg_container);
  753. valid = valid && test_conversion_with_regex("Spatial.gd", "Spatial.gd", &ProjectConverter3To4::rename_classes, "classes", reg_container);
  754. valid = valid && test_conversion_with_regex("Spatial.shader", "Spatial.shader", &ProjectConverter3To4::rename_classes, "classes", reg_container);
  755. valid = valid && test_conversion_with_regex("Spatial.other", "Node3D.other", &ProjectConverter3To4::rename_classes, "classes", reg_container);
  756. valid = valid && test_conversion_gdscript_builtin("\nonready", "\n@onready", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
  757. valid = valid && test_conversion_gdscript_builtin("onready", "@onready", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
  758. valid = valid && test_conversion_gdscript_builtin(" onready", " onready", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
  759. valid = valid && test_conversion_gdscript_builtin("\nexport", "\n@export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
  760. valid = valid && test_conversion_gdscript_builtin("\texport", "\t@export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
  761. valid = valid && test_conversion_gdscript_builtin("\texport_dialog", "\texport_dialog", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
  762. valid = valid && test_conversion_gdscript_builtin("export", "@export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
  763. valid = valid && test_conversion_gdscript_builtin(" export", " export", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
  764. valid = valid && test_conversion_gdscript_builtin("\n\nremote func", "\n\n@rpc(\"any_peer\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
  765. valid = valid && test_conversion_gdscript_builtin("\n\nremote func", "\n\n@rpc(\\\"any_peer\\\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, true);
  766. valid = 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);
  767. valid = 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);
  768. valid = 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);
  769. valid = 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);
  770. valid = valid && test_conversion_gdscript_builtin("\n\nslave func", "\n\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
  771. valid = valid && test_conversion_gdscript_builtin("\n\npuppet func", "\n\n@rpc func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
  772. valid = valid && test_conversion_gdscript_builtin("\n\npuppetsync func", "\n\n@rpc(\"call_local\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, false);
  773. valid = valid && test_conversion_gdscript_builtin("\n\npuppetsync func", "\n\n@rpc(\\\"call_local\\\") func", &ProjectConverter3To4::rename_gdscript_keywords, "gdscript keyword", reg_container, true);
  774. valid = 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);
  775. valid = 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);
  776. valid = 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);
  777. valid = 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);
  778. valid = 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);
  779. valid = 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);
  780. valid = valid && test_conversion_gdscript_builtin("get_node(@", "get_node(", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  781. valid = valid && test_conversion_gdscript_builtin("yield(this, \"timeout\")", "await this.timeout", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  782. valid = valid && test_conversion_gdscript_builtin("yield(this, \\\"timeout\\\")", "await this.timeout", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, true);
  783. valid = 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);
  784. valid = 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);
  785. valid = 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);
  786. valid = valid && test_conversion_gdscript_builtin("export (int)var spaces=1", "export var spaces: int=1", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  787. valid = 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 workaround
  788. valid = valid && test_conversion_gdscript_builtin("export(PackedScene) var mob_scene", "export var mob_scene: PackedScene", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  789. valid = 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);
  790. valid = 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);
  791. valid = valid && test_conversion_gdscript_builtin("export var lifetime := 3.0", "export var lifetime := 3.0", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  792. valid = 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);
  793. valid = 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);
  794. valid = valid && test_conversion_gdscript_builtin("to_json( AA ) szon", "JSON.new().stringify( AA ) szon", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  795. valid = valid && test_conversion_gdscript_builtin("s to_json", "s JSON.new().stringify", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  796. valid = valid && test_conversion_gdscript_builtin("AF to_json2", "AF to_json2", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  797. valid = 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);
  798. valid = valid && test_conversion_gdscript_builtin("empty()", "is_empty()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  799. valid = valid && test_conversion_gdscript_builtin(".empty", ".empty", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  800. valid = valid && test_conversion_gdscript_builtin(").roman(", ").roman(", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  801. valid = valid && test_conversion_gdscript_builtin("\t.roman(", "\tsuper.roman(", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  802. valid = valid && test_conversion_gdscript_builtin(" .roman(", " super.roman(", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  803. valid = valid && test_conversion_gdscript_builtin(".1", ".1", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  804. valid = valid && test_conversion_gdscript_builtin(" .1", " .1", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  805. valid = valid && test_conversion_gdscript_builtin("'.'", "'.'", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  806. valid = valid && test_conversion_gdscript_builtin("'.a'", "'.a'", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  807. valid = valid && test_conversion_gdscript_builtin("\t._input(_event)", "\tsuper._input(_event)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  808. valid = 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);
  809. valid = 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);
  810. valid = 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);
  811. valid = 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);
  812. valid = 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);
  813. valid = 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);
  814. valid = valid && test_conversion_gdscript_builtin(".connect(A,B,C)", ".connect(A, Callable(B, C))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  815. valid = 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);
  816. valid = valid && test_conversion_gdscript_builtin("\tconnect(A,B,C)", "\tconnect(A, Callable(B, C))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  817. valid = valid && test_conversion_gdscript_builtin(" connect(A,B,C)", " connect(A, Callable(B, C))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  818. valid = valid && test_conversion_gdscript_builtin("_connect(A,B,C)", "_connect(A,B,C)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  819. valid = valid && test_conversion_gdscript_builtin("do_connect(A,B,C)", "do_connect(A,B,C)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  820. valid = valid && test_conversion_gdscript_builtin("$connect(A,B,C)", "$connect(A,B,C)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  821. valid = valid && test_conversion_gdscript_builtin("@connect(A,B,C)", "@connect(A,B,C)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  822. valid = valid && test_conversion_gdscript_builtin("(start(A,B) != OK):", "(start(Callable(A, B)) != OK):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  823. valid = valid && test_conversion_gdscript_builtin("func start(A,B):", "func start(A,B):", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  824. valid = 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);
  825. valid = 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);
  826. valid = 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);
  827. valid = 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);
  828. valid = 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);
  829. valid = 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);
  830. valid = 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);
  831. valid = 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);
  832. valid = valid && test_conversion_gdscript_builtin("func _init(", "func _init(", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  833. valid = 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);
  834. valid = 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);
  835. valid = 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);
  836. valid = 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);
  837. valid = valid && test_conversion_gdscript_builtin("q_PackedDataContainer._iter_init(variable1)", "q_PackedDataContainer._iter_init(variable1)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  838. valid = valid && test_conversion_gdscript_builtin("create_from_image(aa, bb)", "create_from_image(aa) #,bb", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  839. valid = 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);
  840. valid = 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);
  841. valid = valid && test_conversion_gdscript_builtin("set_cell_item(a, b)", "set_cell_item(a, b)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  842. valid = 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);
  843. valid = 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);
  844. valid = 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);
  845. valid = valid && test_conversion_gdscript_builtin("PackedStringArray(req_godot).join('.')", "'.'.join(PackedStringArray(req_godot))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  846. valid = valid && test_conversion_gdscript_builtin("=PackedStringArray(req_godot).join('.')", "='.'.join(PackedStringArray(req_godot))", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  847. valid = valid && test_conversion_gdscript_builtin("apply_force(position, impulse)", "apply_force(impulse, position)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  848. valid = valid && test_conversion_gdscript_builtin("apply_impulse(position, impulse)", "apply_impulse(impulse, position)", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  849. valid = 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);
  850. valid = valid && test_conversion_gdscript_builtin("get_focus_owner()", "get_viewport().gui_get_focus_owner()", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  851. valid = valid && test_conversion_gdscript_builtin("button.pressed = 1", "button.button_pressed = 1", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  852. valid = valid && test_conversion_gdscript_builtin("button.pressed=1", "button.button_pressed=1", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  853. valid = valid && test_conversion_gdscript_builtin("button.pressed SF", "button.pressed SF", &ProjectConverter3To4::rename_gdscript_functions, "custom rename", reg_container, false);
  854. valid = valid && test_conversion_with_regex("Color(\"#f47d\")", "Color(\"#47df\")", &ProjectConverter3To4::convert_hexadecimal_colors, "color literals", reg_container);
  855. valid = valid && test_conversion_with_regex("Color(\"#ff478cbf\")", "Color(\"#478cbfff\")", &ProjectConverter3To4::convert_hexadecimal_colors, "color literals", reg_container);
  856. valid = valid && test_conversion_with_regex("Color(\"#de32bf\")", "Color(\"#de32bf\")", &ProjectConverter3To4::convert_hexadecimal_colors, "color literals", reg_container);
  857. valid = valid && test_conversion_with_regex("AAA Color.white AF", "AAA Color.WHITE AF", &ProjectConverter3To4::rename_colors, "color constants", reg_container);
  858. // Note: Do not change to *scancode*, it is applied before that conversion.
  859. valid = 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);
  860. valid = 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);
  861. valid = 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);
  862. valid = valid && test_conversion_with_regex("\"device\":0,\"axis\":6,", "\"device\":0,\"axis\":4,", &ProjectConverter3To4::rename_joypad_buttons_and_axes, "custom rename", reg_container);
  863. valid = 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);
  864. // Custom rule conversion
  865. {
  866. String from = "instance";
  867. String to = "instantiate";
  868. String name = "AA.instance()";
  869. Vector<SourceLine> got = split_lines(name);
  870. String expected = "AA.instantiate()";
  871. custom_rename(got, from, to);
  872. String got_str = collect_string_from_vector(got);
  873. if (got_str != expected) {
  874. ERR_PRINT(vformat("Failed to convert custom rename \"%s\" to \"%s\", got \"%s\", instead.", name, expected, got_str));
  875. }
  876. valid = valid && (got_str == expected);
  877. }
  878. // get_object_of_execution
  879. {
  880. String base = "var roman = kieliszek.";
  881. String expected = "kieliszek.";
  882. String got = get_object_of_execution(base);
  883. if (got != expected) {
  884. ERR_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()));
  885. }
  886. valid = valid && (got == expected);
  887. }
  888. {
  889. String base = "r.";
  890. String expected = "r.";
  891. String got = get_object_of_execution(base);
  892. if (got != expected) {
  893. ERR_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()));
  894. }
  895. valid = valid && (got == expected);
  896. }
  897. {
  898. String base = "mortadela(";
  899. String expected = "";
  900. String got = get_object_of_execution(base);
  901. if (got != expected) {
  902. ERR_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()));
  903. }
  904. valid = valid && (got == expected);
  905. }
  906. {
  907. String base = "var node = $world/ukraine/lviv.";
  908. String expected = "$world/ukraine/lviv.";
  909. String got = get_object_of_execution(base);
  910. if (got != expected) {
  911. ERR_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()));
  912. }
  913. valid = valid && (got == expected);
  914. }
  915. // get_starting_space
  916. {
  917. String base = "\t\t\t var roman = kieliszek.";
  918. String expected = "\t\t\t";
  919. String got = get_starting_space(base);
  920. if (got != expected) {
  921. ERR_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()));
  922. }
  923. valid = valid && (got == expected);
  924. }
  925. // Parse Arguments
  926. {
  927. String line = "( )";
  928. Vector<String> got_vector = parse_arguments(line);
  929. String got = "";
  930. String expected = "";
  931. for (String &part : got_vector) {
  932. got += part + "|||";
  933. }
  934. if (got != expected) {
  935. ERR_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()));
  936. }
  937. valid = valid && (got == expected);
  938. }
  939. {
  940. String line = "(a , b , c)";
  941. Vector<String> got_vector = parse_arguments(line);
  942. String got = "";
  943. String expected = "a|||b|||c|||";
  944. for (String &part : got_vector) {
  945. got += part + "|||";
  946. }
  947. if (got != expected) {
  948. ERR_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()));
  949. }
  950. valid = valid && (got == expected);
  951. }
  952. {
  953. String line = "(a , \"b,\" , c)";
  954. Vector<String> got_vector = parse_arguments(line);
  955. String got = "";
  956. String expected = "a|||\"b,\"|||c|||";
  957. for (String &part : got_vector) {
  958. got += part + "|||";
  959. }
  960. if (got != expected) {
  961. ERR_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()));
  962. }
  963. valid = valid && (got == expected);
  964. }
  965. {
  966. String line = "(a , \"(,),,,,\" , c)";
  967. Vector<String> got_vector = parse_arguments(line);
  968. String got = "";
  969. String expected = "a|||\"(,),,,,\"|||c|||";
  970. for (String &part : got_vector) {
  971. got += part + "|||";
  972. }
  973. if (got != expected) {
  974. ERR_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()));
  975. }
  976. valid = valid && (got == expected);
  977. }
  978. return valid;
  979. }
  980. // Validate in all arrays if names don't do cyclic renames "Node" -> "Node2D" | "Node2D" -> "2DNode"
  981. bool ProjectConverter3To4::test_array_names() {
  982. bool valid = true;
  983. Vector<String> names = Vector<String>();
  984. // Validate if all classes are valid.
  985. {
  986. for (unsigned int current_index = 0; RenamesMap3To4::class_renames[current_index][0]; current_index++) {
  987. const String old_class = RenamesMap3To4::class_renames[current_index][0];
  988. const String new_class = RenamesMap3To4::class_renames[current_index][1];
  989. // Light2D, Texture, Viewport are special classes(probably virtual ones).
  990. if (ClassDB::class_exists(StringName(old_class)) && old_class != "Light2D" && old_class != "Texture" && old_class != "Viewport") {
  991. ERR_PRINT(vformat("Class \"%s\" exists in Godot 4, so it cannot be renamed to something else.", old_class));
  992. valid = false; // This probably should be only a warning, but not 100% sure - this would need to be added to CI.
  993. }
  994. // Callable is special class, to which normal classes may be renamed.
  995. if (!ClassDB::class_exists(StringName(new_class)) && new_class != "Callable") {
  996. ERR_PRINT(vformat("Class \"%s\" does not exist in Godot 4, so it cannot be used in the conversion.", new_class));
  997. valid = false; // This probably should be only a warning, but not 100% sure - this would need to be added to CI.
  998. }
  999. }
  1000. }
  1001. {
  1002. HashSet<String> all_functions;
  1003. // List of excluded functions from builtin types and global namespace, because currently it is not possible to get list of functions from them.
  1004. // This will be available when https://github.com/godotengine/godot/pull/49053 or similar will be included into Godot.
  1005. static 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 };
  1006. for (int current_index = 0; builtin_types_excluded_functions[current_index]; current_index++) {
  1007. all_functions.insert(builtin_types_excluded_functions[current_index]);
  1008. }
  1009. //for (int type = Variant::Type::NIL + 1; type < Variant::Type::VARIANT_MAX; type++) {
  1010. // List<MethodInfo> method_list;
  1011. // Variant::get_method_list_by_type(&method_list, Variant::Type(type));
  1012. // for (MethodInfo &function_data : method_list) {
  1013. // if (!all_functions.has(function_data.name)) {
  1014. // all_functions.insert(function_data.name);
  1015. // }
  1016. // }
  1017. //}
  1018. List<StringName> classes_list;
  1019. ClassDB::get_class_list(&classes_list);
  1020. for (StringName &name_of_class : classes_list) {
  1021. List<MethodInfo> method_list;
  1022. ClassDB::get_method_list(name_of_class, &method_list, true);
  1023. for (MethodInfo &function_data : method_list) {
  1024. if (!all_functions.has(function_data.name)) {
  1025. all_functions.insert(function_data.name);
  1026. }
  1027. }
  1028. }
  1029. int current_element = 0;
  1030. while (RenamesMap3To4::gdscript_function_renames[current_element][0] != nullptr) {
  1031. String name_3_x = RenamesMap3To4::gdscript_function_renames[current_element][0];
  1032. String name_4_0 = RenamesMap3To4::gdscript_function_renames[current_element][1];
  1033. if (!all_functions.has(name_4_0)) {
  1034. ERR_PRINT(vformat("Missing GDScript function in pair (%s - ===> %s <===)", name_3_x, name_4_0));
  1035. valid = false;
  1036. }
  1037. current_element++;
  1038. }
  1039. }
  1040. if (!valid) {
  1041. ERR_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.");
  1042. }
  1043. valid = valid && test_single_array(RenamesMap3To4::enum_renames);
  1044. valid = valid && test_single_array(RenamesMap3To4::class_renames, true);
  1045. valid = valid && test_single_array(RenamesMap3To4::gdscript_function_renames, true);
  1046. valid = valid && test_single_array(RenamesMap3To4::csharp_function_renames, true);
  1047. valid = valid && test_single_array(RenamesMap3To4::gdscript_properties_renames, true);
  1048. valid = valid && test_single_array(RenamesMap3To4::csharp_properties_renames, true);
  1049. valid = valid && test_single_array(RenamesMap3To4::shaders_renames, true);
  1050. valid = valid && test_single_array(RenamesMap3To4::gdscript_signals_renames);
  1051. valid = valid && test_single_array(RenamesMap3To4::project_settings_renames);
  1052. valid = valid && test_single_array(RenamesMap3To4::project_godot_renames);
  1053. valid = valid && test_single_array(RenamesMap3To4::input_map_renames);
  1054. valid = valid && test_single_array(RenamesMap3To4::builtin_types_renames);
  1055. valid = valid && test_single_array(RenamesMap3To4::color_renames);
  1056. return valid;
  1057. }
  1058. // Validates the array to prevent cyclic renames, such as `Node` -> `Node2D`, then `Node2D` -> `2DNode`.
  1059. // Also checks if names contain leading or trailing spaces.
  1060. bool ProjectConverter3To4::test_single_array(const char *p_array[][2], bool p_ignore_4_0_name) {
  1061. bool valid = true;
  1062. Vector<String> names = Vector<String>();
  1063. for (unsigned int current_index = 0; p_array[current_index][0]; current_index++) {
  1064. String name_3_x = p_array[current_index][0];
  1065. String name_4_0 = p_array[current_index][1];
  1066. if (name_3_x != name_3_x.strip_edges()) {
  1067. ERR_PRINT(vformat("Invalid Entry \"%s\" contains leading or trailing spaces.", name_3_x));
  1068. valid = false;
  1069. }
  1070. if (names.has(name_3_x)) {
  1071. ERR_PRINT(vformat("Found duplicated entry, pair ( -> %s , %s)", name_3_x, name_4_0));
  1072. valid = false;
  1073. }
  1074. names.append(name_3_x);
  1075. if (name_4_0 != name_4_0.strip_edges()) {
  1076. ERR_PRINT(vformat("Invalid Entry \"%s\" contains leading or trailing spaces.", name_3_x));
  1077. valid = false;
  1078. }
  1079. if (names.has(name_4_0)) {
  1080. ERR_PRINT(vformat("Found duplicated entry, pair ( -> %s , %s)", name_3_x, name_4_0));
  1081. valid = false;
  1082. }
  1083. if (!p_ignore_4_0_name) {
  1084. names.append(name_4_0);
  1085. }
  1086. }
  1087. return valid;
  1088. }
  1089. // Returns arguments from given function execution, this cannot be really done as regex.
  1090. // `abc(d,e(f,g),h)` -> [d], [e(f,g)], [h]
  1091. Vector<String> ProjectConverter3To4::parse_arguments(const String &line) {
  1092. Vector<String> parts;
  1093. int string_size = line.length();
  1094. int start_part = 0; // Index of beginning of start part.
  1095. int parts_counter = 0;
  1096. char32_t previous_character = '\0';
  1097. bool is_inside_string = false; // If true, it ignores these 3 characters ( , ) inside string.
  1098. ERR_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));
  1099. for (int current_index = 0; current_index < string_size; current_index++) {
  1100. char32_t character = line.get(current_index);
  1101. switch (character) {
  1102. case '(':
  1103. case '[':
  1104. case '{': {
  1105. parts_counter++;
  1106. if (parts_counter == 1 && !is_inside_string) {
  1107. start_part = current_index;
  1108. }
  1109. break;
  1110. };
  1111. case ')':
  1112. case '}': {
  1113. parts_counter--;
  1114. if (parts_counter == 0 && !is_inside_string) {
  1115. parts.append(line.substr(start_part + 1, current_index - start_part - 1));
  1116. start_part = current_index;
  1117. }
  1118. break;
  1119. };
  1120. case ']': {
  1121. parts_counter--;
  1122. if (parts_counter == 0 && !is_inside_string) {
  1123. parts.append(line.substr(start_part, current_index - start_part));
  1124. start_part = current_index;
  1125. }
  1126. break;
  1127. };
  1128. case ',': {
  1129. if (parts_counter == 1 && !is_inside_string) {
  1130. parts.append(line.substr(start_part + 1, current_index - start_part - 1));
  1131. start_part = current_index;
  1132. }
  1133. break;
  1134. };
  1135. case '"': {
  1136. if (previous_character != '\\') {
  1137. is_inside_string = !is_inside_string;
  1138. }
  1139. }
  1140. }
  1141. previous_character = character;
  1142. }
  1143. Vector<String> clean_parts;
  1144. for (String &part : parts) {
  1145. part = part.strip_edges();
  1146. if (!part.is_empty()) {
  1147. clean_parts.append(part);
  1148. }
  1149. }
  1150. return clean_parts;
  1151. }
  1152. // Finds latest parenthesis owned by function.
  1153. // `function(abc(a,b),DD)):` finds this parenthess `function(abc(a,b),DD => ) <= ):`
  1154. int ProjectConverter3To4::get_end_parenthesis(const String &line) const {
  1155. int current_state = 0;
  1156. for (int current_index = 0; line.length() > current_index; current_index++) {
  1157. char32_t character = line.get(current_index);
  1158. if (character == '(') {
  1159. current_state++;
  1160. }
  1161. if (character == ')') {
  1162. current_state--;
  1163. if (current_state == 0) {
  1164. return current_index;
  1165. }
  1166. }
  1167. }
  1168. return -1;
  1169. }
  1170. // Merges multiple arguments into a single String.
  1171. // Needed when after processing e.g. 2 arguments, later arguments are not changed in any way.
  1172. String ProjectConverter3To4::connect_arguments(const Vector<String> &arguments, int from, int to) const {
  1173. if (to == -1) {
  1174. to = arguments.size();
  1175. }
  1176. String value;
  1177. if (arguments.size() > 0 && from != 0 && from < to) {
  1178. value = ", ";
  1179. }
  1180. for (int i = from; i < to; i++) {
  1181. value += arguments[i];
  1182. if (i != to - 1) {
  1183. value += ", ";
  1184. }
  1185. }
  1186. return value;
  1187. }
  1188. // Returns the indentation (spaces and tabs) at the start of the line e.g. `\t\tmove_this` returns `\t\t`.
  1189. String ProjectConverter3To4::get_starting_space(const String &line) const {
  1190. String empty_space;
  1191. int current_character = 0;
  1192. if (line.is_empty()) {
  1193. return empty_space;
  1194. }
  1195. if (line[0] == ' ') {
  1196. while (current_character < line.size()) {
  1197. if (line[current_character] == ' ') {
  1198. empty_space += ' ';
  1199. current_character++;
  1200. } else {
  1201. break;
  1202. }
  1203. }
  1204. }
  1205. if (line[0] == '\t') {
  1206. while (current_character < line.size()) {
  1207. if (line[current_character] == '\t') {
  1208. empty_space += '\t';
  1209. current_character++;
  1210. } else {
  1211. break;
  1212. }
  1213. }
  1214. }
  1215. return empty_space;
  1216. }
  1217. // Returns the object that’s executing the function in the line.
  1218. // e.g. Passing the line "var roman = kieliszek.funkcja()" to this function returns "kieliszek".
  1219. String ProjectConverter3To4::get_object_of_execution(const String &line) const {
  1220. int end = line.size() - 1; // Last one is \0
  1221. int variable_start = end - 1;
  1222. int start = end - 1;
  1223. bool is_possibly_nodepath = false;
  1224. bool is_valid_nodepath = false;
  1225. while (start >= 0) {
  1226. char32_t character = line[start];
  1227. bool is_variable_char = (character >= 'A' && character <= 'Z') || (character >= 'a' && character <= 'z') || character == '.' || character == '_';
  1228. bool is_nodepath_start = character == '$';
  1229. bool is_nodepath_sep = character == '/';
  1230. if (is_variable_char || is_nodepath_start || is_nodepath_sep) {
  1231. if (start == 0) {
  1232. break;
  1233. } else if (is_nodepath_sep) {
  1234. // Freeze variable_start, try to fetch more chars since this might be a Node path literal.
  1235. is_possibly_nodepath = true;
  1236. } else if (is_nodepath_start) {
  1237. // Found $, this is a Node path literal.
  1238. is_valid_nodepath = true;
  1239. break;
  1240. }
  1241. if (!is_possibly_nodepath) {
  1242. variable_start--;
  1243. }
  1244. start--;
  1245. continue;
  1246. } else {
  1247. // Abandon all hope, this is neither a variable nor a Node path literal.
  1248. variable_start++; // Found invalid character, needs to be ignored.
  1249. break;
  1250. }
  1251. }
  1252. if (is_valid_nodepath) {
  1253. variable_start = start;
  1254. }
  1255. return line.substr(variable_start, (end - variable_start));
  1256. }
  1257. void ProjectConverter3To4::rename_colors(Vector<SourceLine> &source_lines, const RegExContainer &reg_container) {
  1258. for (SourceLine &source_line : source_lines) {
  1259. if (source_line.is_comment) {
  1260. continue;
  1261. }
  1262. String &line = source_line.line;
  1263. if (uint64_t(line.length()) <= maximum_line_length) {
  1264. if (line.contains("Color.")) {
  1265. for (unsigned int current_index = 0; RenamesMap3To4::color_renames[current_index][0]; current_index++) {
  1266. line = reg_container.color_regexes[current_index]->sub(line, reg_container.color_renamed[current_index], true);
  1267. }
  1268. }
  1269. }
  1270. }
  1271. }
  1272. // Convert hexadecimal colors from ARGB to RGBA
  1273. void ProjectConverter3To4::convert_hexadecimal_colors(Vector<SourceLine> &source_lines, const RegExContainer &reg_container) {
  1274. for (SourceLine &source_line : source_lines) {
  1275. if (source_line.is_comment) {
  1276. continue;
  1277. }
  1278. String &line = source_line.line;
  1279. if (uint64_t(line.length()) <= maximum_line_length) {
  1280. if (line.contains("Color(\"")) {
  1281. line = reg_container.color_hexadecimal_short_constructor.sub(line, "Color(\"#$2$1", true);
  1282. line = reg_container.color_hexadecimal_full_constructor.sub(line, "Color(\"#$2$1", true);
  1283. }
  1284. }
  1285. }
  1286. }
  1287. Vector<String> ProjectConverter3To4::check_for_rename_colors(Vector<String> &lines, const RegExContainer &reg_container) {
  1288. Vector<String> found_renames;
  1289. int current_line = 1;
  1290. for (String &line : lines) {
  1291. if (uint64_t(line.length()) <= maximum_line_length) {
  1292. if (line.contains("Color.")) {
  1293. for (unsigned int current_index = 0; RenamesMap3To4::color_renames[current_index][0]; current_index++) {
  1294. TypedArray<RegExMatch> reg_match = reg_container.color_regexes[current_index]->search_all(line);
  1295. if (reg_match.size() > 0) {
  1296. found_renames.append(line_formatter(current_line, RenamesMap3To4::color_renames[current_index][0], RenamesMap3To4::color_renames[current_index][1], line));
  1297. }
  1298. }
  1299. }
  1300. }
  1301. current_line++;
  1302. }
  1303. return found_renames;
  1304. }
  1305. void ProjectConverter3To4::fix_tool_declaration(Vector<SourceLine> &source_lines, const RegExContainer &reg_container) {
  1306. // In godot4, "tool" became "@tool" and must be located at the top of the file.
  1307. for (int i = 0; i < source_lines.size(); ++i) {
  1308. if (source_lines[i].line == "tool") {
  1309. source_lines.remove_at(i);
  1310. source_lines.insert(0, { "@tool", false });
  1311. return; // assuming there's at most 1 tool declaration.
  1312. }
  1313. }
  1314. }
  1315. void ProjectConverter3To4::fix_pause_mode(Vector<SourceLine> &source_lines, const RegExContainer &reg_container) {
  1316. // In Godot 3, the pause_mode 2 equals the PAUSE_MODE_PROCESS value.
  1317. // In Godot 4, the pause_mode PAUSE_MODE_PROCESS was renamed to PROCESS_MODE_ALWAYS and equals the number 3.
  1318. // We therefore convert pause_mode = 2 to pause_mode = 3.
  1319. for (SourceLine &source_line : source_lines) {
  1320. String &line = source_line.line;
  1321. if (line == "pause_mode = 2") {
  1322. // Note: pause_mode is renamed to process_mode later on, so no need to do it here.
  1323. line = "pause_mode = 3";
  1324. }
  1325. }
  1326. }
  1327. void ProjectConverter3To4::rename_classes(Vector<SourceLine> &source_lines, const RegExContainer &reg_container) {
  1328. for (SourceLine &source_line : source_lines) {
  1329. if (source_line.is_comment) {
  1330. continue;
  1331. }
  1332. String &line = source_line.line;
  1333. if (uint64_t(line.length()) <= maximum_line_length) {
  1334. for (unsigned int current_index = 0; RenamesMap3To4::class_renames[current_index][0]; current_index++) {
  1335. if (line.contains(RenamesMap3To4::class_renames[current_index][0])) {
  1336. bool found_ignored_items = false;
  1337. // Renaming Spatial.tscn to TEMP_RENAMED_CLASS.tscn.
  1338. if (line.contains(String(RenamesMap3To4::class_renames[current_index][0]) + ".")) {
  1339. found_ignored_items = true;
  1340. line = reg_container.class_tscn_regexes[current_index]->sub(line, "TEMP_RENAMED_CLASS.tscn", true);
  1341. line = reg_container.class_gd_regexes[current_index]->sub(line, "TEMP_RENAMED_CLASS.gd", true);
  1342. line = reg_container.class_shader_regexes[current_index]->sub(line, "TEMP_RENAMED_CLASS.shader", true);
  1343. }
  1344. // Causal renaming Spatial -> Node3D.
  1345. line = reg_container.class_regexes[current_index]->sub(line, RenamesMap3To4::class_renames[current_index][1], true);
  1346. // Restore Spatial.tscn from TEMP_RENAMED_CLASS.tscn.
  1347. if (found_ignored_items) {
  1348. line = reg_container.class_temp_tscn.sub(line, reg_container.class_temp_tscn_renames[current_index], true);
  1349. line = reg_container.class_temp_gd.sub(line, reg_container.class_temp_gd_renames[current_index], true);
  1350. line = reg_container.class_temp_shader.sub(line, reg_container.class_temp_shader_renames[current_index], true);
  1351. }
  1352. }
  1353. }
  1354. }
  1355. }
  1356. }
  1357. Vector<String> ProjectConverter3To4::check_for_rename_classes(Vector<String> &lines, const RegExContainer &reg_container) {
  1358. Vector<String> found_renames;
  1359. int current_line = 1;
  1360. for (String &line : lines) {
  1361. if (uint64_t(line.length()) <= maximum_line_length) {
  1362. for (unsigned int current_index = 0; RenamesMap3To4::class_renames[current_index][0]; current_index++) {
  1363. if (line.contains(RenamesMap3To4::class_renames[current_index][0])) {
  1364. String old_line = line;
  1365. bool found_ignored_items = false;
  1366. // Renaming Spatial.tscn to TEMP_RENAMED_CLASS.tscn.
  1367. if (line.contains(String(RenamesMap3To4::class_renames[current_index][0]) + ".")) {
  1368. found_ignored_items = true;
  1369. line = reg_container.class_tscn_regexes[current_index]->sub(line, "TEMP_RENAMED_CLASS.tscn", true);
  1370. line = reg_container.class_gd_regexes[current_index]->sub(line, "TEMP_RENAMED_CLASS.gd", true);
  1371. line = reg_container.class_shader_regexes[current_index]->sub(line, "TEMP_RENAMED_CLASS.shader", true);
  1372. }
  1373. // Causal renaming Spatial -> Node3D.
  1374. TypedArray<RegExMatch> reg_match = reg_container.class_regexes[current_index]->search_all(line);
  1375. if (reg_match.size() > 0) {
  1376. found_renames.append(line_formatter(current_line, RenamesMap3To4::class_renames[current_index][0], RenamesMap3To4::class_renames[current_index][1], old_line));
  1377. }
  1378. // Restore Spatial.tscn from TEMP_RENAMED_CLASS.tscn.
  1379. if (found_ignored_items) {
  1380. line = reg_container.class_temp_tscn.sub(line, reg_container.class_temp_tscn_renames[current_index], true);
  1381. line = reg_container.class_temp_gd.sub(line, reg_container.class_temp_gd_renames[current_index], true);
  1382. line = reg_container.class_temp_shader.sub(line, reg_container.class_temp_shader_renames[current_index], true);
  1383. }
  1384. }
  1385. }
  1386. }
  1387. current_line++;
  1388. }
  1389. return found_renames;
  1390. }
  1391. void ProjectConverter3To4::rename_gdscript_functions(Vector<SourceLine> &source_lines, const RegExContainer &reg_container, bool builtin) {
  1392. for (SourceLine &source_line : source_lines) {
  1393. if (source_line.is_comment) {
  1394. continue;
  1395. }
  1396. String &line = source_line.line;
  1397. if (uint64_t(line.length()) <= maximum_line_length) {
  1398. process_gdscript_line(line, reg_container, builtin);
  1399. }
  1400. }
  1401. }
  1402. Vector<String> ProjectConverter3To4::check_for_rename_gdscript_functions(Vector<String> &lines, const RegExContainer &reg_container, bool builtin) {
  1403. int current_line = 1;
  1404. Vector<String> found_renames;
  1405. for (String &line : lines) {
  1406. if (uint64_t(line.length()) <= maximum_line_length) {
  1407. String old_line = line;
  1408. process_gdscript_line(line, reg_container, builtin);
  1409. if (old_line != line) {
  1410. found_renames.append(simple_line_formatter(current_line, old_line, line));
  1411. }
  1412. }
  1413. }
  1414. return found_renames;
  1415. }
  1416. bool ProjectConverter3To4::contains_function_call(const String &line, const String &function) const {
  1417. // We want to convert the function only if it is completely standalone.
  1418. // For example, when we search for "connect(", we don't want to accidentally convert "reconnect(".
  1419. if (!line.contains(function)) {
  1420. return false;
  1421. }
  1422. int index = line.find(function);
  1423. if (index == 0) {
  1424. return true;
  1425. }
  1426. char32_t previous_char = line.get(index - 1);
  1427. return (previous_char < '0' || previous_char > '9') && (previous_char < 'a' || previous_char > 'z') && (previous_char < 'A' || previous_char > 'Z') && previous_char != '_' && previous_char != '$' && previous_char != '@';
  1428. }
  1429. // TODO, this function should run only on all ".gd" files and also on lines in ".tscn" files which are parts of built-in Scripts.
  1430. void ProjectConverter3To4::process_gdscript_line(String &line, const RegExContainer &reg_container, bool builtin) {
  1431. // In this and other functions, reg.sub() is used only after checking lines with str.contains().
  1432. // With longer lines, doing so can sometimes be significantly faster.
  1433. if ((line.contains(".lock") || line.contains(".unlock")) && !line.contains("mtx") && !line.contains("mutex") && !line.contains("Mutex")) {
  1434. line = 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);
  1435. line = 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);
  1436. }
  1437. // PackedStringArray(req_godot).join('.') -> '.'.join(PackedStringArray(req_godot)) PoolStringArray
  1438. if (line.contains(".join")) {
  1439. line = reg_container.reg_join.sub(line, "$2.join($1)", true);
  1440. }
  1441. // -- empty() -> is_empty() Pool*Array
  1442. if (line.contains("empty")) {
  1443. line = reg_container.reg_is_empty.sub(line, "is_empty(", true);
  1444. }
  1445. // -- \t.func() -> \tsuper.func() Object
  1446. if (line.contains_char('(') && line.contains_char('.')) {
  1447. line = 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"
  1448. }
  1449. // -- JSON.parse(a) -> JSON.new().parse(a) etc. JSON
  1450. if (line.contains("parse")) {
  1451. line = 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);
  1452. }
  1453. // -- to_json(a) -> JSON.new().stringify(a) Object
  1454. if (line.contains("to_json")) {
  1455. line = reg_container.reg_json_to.sub(line, "JSON.new().stringify", true);
  1456. }
  1457. // -- parse_json(a) -> JSON.get_data() etc. Object
  1458. if (line.contains("parse_json")) {
  1459. line = 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);
  1460. }
  1461. // -- JSON.print( -> JSON.stringify(
  1462. if (line.contains("JSON.print(")) {
  1463. line = reg_container.reg_json_print.sub(line, "JSON.stringify(", true);
  1464. }
  1465. // -- get_node(@ -> get_node( Node
  1466. if (line.contains("get_node")) {
  1467. line = line.replace("get_node(@", "get_node(");
  1468. }
  1469. if (line.contains("export")) {
  1470. // 1. export(float) var lifetime: float = 3.0 -> export var lifetime: float = 3.0
  1471. line = reg_container.reg_export_typed.sub(line, "export var $2: $1");
  1472. // 2. export(float) var lifetime := 3.0 -> export var lifetime := 3.0
  1473. line = reg_container.reg_export_inferred_type.sub(line, "export var $1 :=");
  1474. // 3. export(float) var lifetime = 3.0 -> export var lifetime: float = 3.0 GDScript
  1475. line = reg_container.reg_export_simple.sub(line, "export var $2: $1");
  1476. // 4. export(String, 'AnonymousPro', 'CourierPrime') var _font_name = 'AnonymousPro' -> export var _font_name = 'AnonymousPro' #(String, 'AnonymousPro', 'CourierPrime') GDScript
  1477. line = reg_container.reg_export_advanced.sub(line, "export var $2$3 # ($1)");
  1478. }
  1479. // Setget Setget
  1480. if (line.contains("setget")) {
  1481. line = reg_container.reg_setget_setget.sub(line, "var $1$2: get = $4, set = $3", true);
  1482. }
  1483. // Setget set
  1484. if (line.contains("setget")) {
  1485. line = reg_container.reg_setget_set.sub(line, "var $1$2: set = $3", true);
  1486. }
  1487. // Setget get
  1488. if (line.contains("setget")) {
  1489. line = reg_container.reg_setget_get.sub(line, "var $1$2: get = $3", true);
  1490. }
  1491. if (line.contains("window_resizable")) {
  1492. // OS.set_window_resizable(a) -> get_window().unresizable = not (a)
  1493. line = reg_container.reg_os_set_window_resizable.sub(line, "get_window().unresizable = not ($1)", true);
  1494. // OS.window_resizable = a -> same
  1495. line = reg_container.reg_os_assign_window_resizable.sub(line, "get_window().unresizable = not ($1)", true);
  1496. // OS.[is_]window_resizable() -> (not get_window().unresizable)
  1497. line = reg_container.reg_os_is_window_resizable.sub(line, "(not get_window().unresizable)", true);
  1498. }
  1499. if (line.contains("window_fullscreen")) {
  1500. // OS.window_fullscreen(a) -> get_window().mode = Window.MODE_EXCLUSIVE_FULLSCREEN if (a) else Window.MODE_WINDOWED
  1501. line = reg_container.reg_os_set_fullscreen.sub(line, "get_window().mode = Window.MODE_EXCLUSIVE_FULLSCREEN if ($1) else Window.MODE_WINDOWED", true);
  1502. // window_fullscreen = a -> same
  1503. line = reg_container.reg_os_assign_fullscreen.sub(line, "get_window().mode = Window.MODE_EXCLUSIVE_FULLSCREEN if ($1) else Window.MODE_WINDOWED", true);
  1504. // OS.[is_]window_fullscreen() -> ((get_window().mode == Window.MODE_EXCLUSIVE_FULLSCREEN) or (get_window().mode == Window.MODE_FULLSCREEN))
  1505. line = reg_container.reg_os_is_fullscreen.sub(line, "((get_window().mode == Window.MODE_EXCLUSIVE_FULLSCREEN) or (get_window().mode == Window.MODE_FULLSCREEN))", true);
  1506. }
  1507. if (line.contains("window_maximized")) {
  1508. // OS.window_maximized(a) -> get_window().mode = Window.MODE_MAXIMIZED if (a) else Window.MODE_WINDOWED
  1509. line = reg_container.reg_os_set_maximized.sub(line, "get_window().mode = Window.MODE_MAXIMIZED if ($1) else Window.MODE_WINDOWED", true);
  1510. // window_maximized = a -> same
  1511. line = reg_container.reg_os_assign_maximized.sub(line, "get_window().mode = Window.MODE_MAXIMIZED if ($1) else Window.MODE_WINDOWED", true);
  1512. // OS.[is_]window_maximized() -> (get_window().mode == Window.MODE_MAXIMIZED)
  1513. line = reg_container.reg_os_is_maximized.sub(line, "(get_window().mode == Window.MODE_MAXIMIZED)", true);
  1514. }
  1515. if (line.contains("window_minimized")) {
  1516. // OS.window_minimized(a) -> get_window().mode = Window.MODE_MINIMIZED if (a) else Window.MODE_WINDOWED
  1517. line = reg_container.reg_os_set_minimized.sub(line, "get_window().mode = Window.MODE_MINIMIZED if ($1) else Window.MODE_WINDOWED", true);
  1518. // window_minimized = a -> same
  1519. line = reg_container.reg_os_assign_minimized.sub(line, "get_window().mode = Window.MODE_MINIMIZED if ($1) else Window.MODE_WINDOWED", true);
  1520. // OS.[is_]window_minimized() -> (get_window().mode == Window.MODE_MINIMIZED)
  1521. line = reg_container.reg_os_is_minimized.sub(line, "(get_window().mode == Window.MODE_MINIMIZED)", true);
  1522. }
  1523. if (line.contains("set_use_vsync")) {
  1524. // OS.set_use_vsync(a) -> get_window().window_set_vsync_mode(DisplayServer.VSYNC_ENABLED if (a) else DisplayServer.VSYNC_DISABLED)
  1525. line = reg_container.reg_os_set_vsync.sub(line, "DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED if ($1) else DisplayServer.VSYNC_DISABLED)", true);
  1526. }
  1527. if (line.contains("vsync_enabled")) {
  1528. // vsync_enabled = a -> get_window().window_set_vsync_mode(DisplayServer.VSYNC_ENABLED if (a) else DisplayServer.VSYNC_DISABLED)
  1529. line = reg_container.reg_os_assign_vsync.sub(line, "DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_ENABLED if ($1) else DisplayServer.VSYNC_DISABLED)", true);
  1530. // OS.[is_]vsync_enabled() -> (DisplayServer.window_get_vsync_mode() != DisplayServer.VSYNC_DISABLED)
  1531. line = reg_container.reg_os_is_vsync.sub(line, "(DisplayServer.window_get_vsync_mode() != DisplayServer.VSYNC_DISABLED)", true);
  1532. }
  1533. if (line.contains("OS.screen_orientation")) { // keep "OS." at start
  1534. // OS.screen_orientation = a -> DisplayServer.screen_set_orientation(a)
  1535. line = reg_container.reg_os_assign_screen_orient.sub(line, "$1DisplayServer.screen_set_orientation($2)", true); // assignment
  1536. line = line.replace("OS.screen_orientation", "DisplayServer.screen_get_orientation()"); // value access
  1537. }
  1538. if (line.contains("_window_always_on_top")) {
  1539. // OS.set_window_always_on_top(a) -> get_window().always_on_top = (a)
  1540. line = reg_container.reg_os_set_always_on_top.sub(line, "get_window().always_on_top = ($1)", true);
  1541. // OS.is_window_always_on_top() -> get_window().always_on_top
  1542. line = reg_container.reg_os_is_always_on_top.sub(line, "get_window().always_on_top", true);
  1543. }
  1544. if (line.contains("et_borderless_window")) {
  1545. // OS.set_borderless_window(a) -> get_window().borderless = (a)
  1546. line = reg_container.reg_os_set_borderless.sub(line, "get_window().borderless = ($1)", true);
  1547. // OS.get_borderless_window() -> get_window().borderless
  1548. line = reg_container.reg_os_get_borderless.sub(line, "get_window().borderless", true);
  1549. }
  1550. // OS.SCREEN_ORIENTATION_* -> DisplayServer.SCREEN_*
  1551. if (line.contains("OS.SCREEN_ORIENTATION_")) {
  1552. line = reg_container.reg_os_screen_orient_enum.sub(line, "DisplayServer.SCREEN_$1", true);
  1553. }
  1554. // OS -> Window simple replacements with optional set/get.
  1555. if (line.contains("current_screen")) {
  1556. line = reg_container.reg_os_current_screen.sub(line, "get_window().$1current_screen", true);
  1557. }
  1558. if (line.contains("min_window_size")) {
  1559. line = reg_container.reg_os_min_window_size.sub(line, "get_window().$1min_size", true);
  1560. }
  1561. if (line.contains("max_window_size")) {
  1562. line = reg_container.reg_os_max_window_size.sub(line, "get_window().$1max_size", true);
  1563. }
  1564. if (line.contains("window_position")) {
  1565. line = reg_container.reg_os_window_position.sub(line, "get_window().$1position", true);
  1566. }
  1567. if (line.contains("window_size")) {
  1568. line = reg_container.reg_os_window_size.sub(line, "get_window().$1size", true);
  1569. }
  1570. if (line.contains("et_screen_orientation")) {
  1571. line = reg_container.reg_os_getset_screen_orient.sub(line, "DisplayServer.screen_$1et_orientation", true);
  1572. }
  1573. // Instantiate
  1574. if (contains_function_call(line, "instance")) {
  1575. line = reg_container.reg_instantiate.sub(line, ".instantiate($1)", true);
  1576. }
  1577. // -- r.move_and_slide( a, b, c, d, e ) -> r.set_velocity(a) ... r.move_and_slide() KinematicBody
  1578. if (contains_function_call(line, "move_and_slide(")) {
  1579. int start = line.find("move_and_slide(");
  1580. int end = get_end_parenthesis(line.substr(start)) + 1;
  1581. if (end > -1) {
  1582. String base_obj = get_object_of_execution(line.substr(0, start));
  1583. String starting_space = get_starting_space(line);
  1584. Vector<String> parts = parse_arguments(line.substr(start, end));
  1585. if (parts.size() >= 1) {
  1586. String line_new;
  1587. // motion_velocity
  1588. line_new += starting_space + base_obj + "set_velocity(" + parts[0] + ")\n";
  1589. // up_direction
  1590. if (parts.size() >= 2) {
  1591. line_new += starting_space + base_obj + "set_up_direction(" + parts[1] + ")\n";
  1592. }
  1593. // stop_on_slope
  1594. if (parts.size() >= 3) {
  1595. line_new += starting_space + base_obj + "set_floor_stop_on_slope_enabled(" + parts[2] + ")\n";
  1596. }
  1597. // max_slides
  1598. if (parts.size() >= 4) {
  1599. line_new += starting_space + base_obj + "set_max_slides(" + parts[3] + ")\n";
  1600. }
  1601. // floor_max_angle
  1602. if (parts.size() >= 5) {
  1603. line_new += starting_space + base_obj + "set_floor_max_angle(" + parts[4] + ")\n";
  1604. }
  1605. // infiinite_interia
  1606. if (parts.size() >= 6) {
  1607. line_new += starting_space + "# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `" + parts[5] + "`\n";
  1608. }
  1609. line_new += starting_space + base_obj + "move_and_slide()";
  1610. if (!line.begins_with(starting_space + "move_and_slide")) {
  1611. line = line_new + "\n" + line.substr(0, start) + "velocity" + line.substr(end + start);
  1612. } else {
  1613. line = line_new + line.substr(end + start);
  1614. }
  1615. }
  1616. }
  1617. }
  1618. // -- r.move_and_slide_with_snap( a, b, c, d, e ) -> r.set_velocity(a) ... r.move_and_slide() KinematicBody
  1619. if (contains_function_call(line, "move_and_slide_with_snap(")) {
  1620. int start = line.find("move_and_slide_with_snap(");
  1621. int end = get_end_parenthesis(line.substr(start)) + 1;
  1622. if (end > -1) {
  1623. String base_obj = get_object_of_execution(line.substr(0, start));
  1624. String starting_space = get_starting_space(line);
  1625. Vector<String> parts = parse_arguments(line.substr(start, end));
  1626. if (parts.size() >= 1) {
  1627. String line_new;
  1628. // motion_velocity
  1629. line_new += starting_space + base_obj + "set_velocity(" + parts[0] + ")\n";
  1630. // snap
  1631. if (parts.size() >= 2) {
  1632. line_new += starting_space + "# TODOConverter3To4 looks that snap in Godot 4 is float, not vector like in Godot 3 - previous value `" + parts[1] + "`\n";
  1633. }
  1634. // up_direction
  1635. if (parts.size() >= 3) {
  1636. line_new += starting_space + base_obj + "set_up_direction(" + parts[2] + ")\n";
  1637. }
  1638. // stop_on_slope
  1639. if (parts.size() >= 4) {
  1640. line_new += starting_space + base_obj + "set_floor_stop_on_slope_enabled(" + parts[3] + ")\n";
  1641. }
  1642. // max_slides
  1643. if (parts.size() >= 5) {
  1644. line_new += starting_space + base_obj + "set_max_slides(" + parts[4] + ")\n";
  1645. }
  1646. // floor_max_angle
  1647. if (parts.size() >= 6) {
  1648. line_new += starting_space + base_obj + "set_floor_max_angle(" + parts[5] + ")\n";
  1649. }
  1650. // infiinite_interia
  1651. if (parts.size() >= 7) {
  1652. line_new += starting_space + "# TODOConverter3To4 infinite_inertia were removed in Godot 4 - previous value `" + parts[6] + "`\n";
  1653. }
  1654. line_new += starting_space + base_obj + "move_and_slide()";
  1655. if (!line.begins_with(starting_space + "move_and_slide_with_snap")) {
  1656. line = line_new + "\n" + line.substr(0, start) + "velocity" + line.substr(end + start);
  1657. } else {
  1658. line = line_new + line.substr(end + start);
  1659. }
  1660. }
  1661. }
  1662. }
  1663. // -- sort_custom( a , b ) -> sort_custom(Callable( a , b )) Object
  1664. if (contains_function_call(line, "sort_custom(")) {
  1665. int start = line.find("sort_custom(");
  1666. int end = get_end_parenthesis(line.substr(start)) + 1;
  1667. if (end > -1) {
  1668. Vector<String> parts = parse_arguments(line.substr(start, end));
  1669. if (parts.size() == 2) {
  1670. line = line.substr(0, start) + "sort_custom(Callable(" + parts[0] + ", " + parts[1] + "))" + line.substr(end + start);
  1671. }
  1672. }
  1673. }
  1674. // -- list_dir_begin( ) -> list_dir_begin() Object
  1675. if (contains_function_call(line, "list_dir_begin(")) {
  1676. int start = line.find("list_dir_begin(");
  1677. int end = get_end_parenthesis(line.substr(start)) + 1;
  1678. if (end > -1) {
  1679. line = line.substr(0, start) + "list_dir_begin() " + line.substr(end + start) + "# TODOConverter3To4 fill missing arguments https://github.com/godotengine/godot/pull/40547";
  1680. }
  1681. }
  1682. // -- draw_line(1,2,3,4,5) -> draw_line(1, 2, 3, 4) CanvasItem
  1683. if (contains_function_call(line, "draw_line(")) {
  1684. int start = line.find("draw_line(");
  1685. int end = get_end_parenthesis(line.substr(start)) + 1;
  1686. if (end > -1) {
  1687. Vector<String> parts = parse_arguments(line.substr(start, end));
  1688. if (parts.size() == 5) {
  1689. line = line.substr(0, start) + "draw_line(" + parts[0] + ", " + parts[1] + ", " + parts[2] + ", " + parts[3] + ")" + line.substr(end + start);
  1690. }
  1691. }
  1692. }
  1693. // -- func c(var a, var b) -> func c(a, b)
  1694. if (line.contains("func ") && line.contains("var ")) {
  1695. int start = line.find("func ");
  1696. start = line.substr(start).find_char('(') + start;
  1697. int end = get_end_parenthesis(line.substr(start)) + 1;
  1698. if (end > -1) {
  1699. Vector<String> parts = parse_arguments(line.substr(start, end));
  1700. String start_string = line.substr(0, start) + "(";
  1701. for (int i = 0; i < parts.size(); i++) {
  1702. start_string += parts[i].strip_edges().trim_prefix("var ");
  1703. if (i != parts.size() - 1) {
  1704. start_string += ", ";
  1705. }
  1706. }
  1707. line = start_string + ")" + line.substr(end + start);
  1708. }
  1709. }
  1710. // -- yield(this, \"timeout\") -> await this.timeout GDScript
  1711. if (contains_function_call(line, "yield(")) {
  1712. int start = line.find("yield(");
  1713. int end = get_end_parenthesis(line.substr(start)) + 1;
  1714. if (end > -1) {
  1715. Vector<String> parts = parse_arguments(line.substr(start, end));
  1716. if (parts.size() == 2) {
  1717. if (builtin) {
  1718. line = line.substr(0, start) + "await " + parts[0] + "." + parts[1].replace("\\\"", "").replace("\\'", "").replace(" ", "") + line.substr(end + start);
  1719. } else {
  1720. line = line.substr(0, start) + "await " + parts[0] + "." + parts[1].replace("\"", "").replace("\'", "").replace(" ", "") + line.substr(end + start);
  1721. }
  1722. }
  1723. }
  1724. }
  1725. // -- parse_json( AA ) -> TODO Object
  1726. if (contains_function_call(line, "parse_json(")) {
  1727. int start = line.find("parse_json(");
  1728. int end = get_end_parenthesis(line.substr(start)) + 1;
  1729. if (end > -1) {
  1730. Vector<String> parts = parse_arguments(line.substr(start, end));
  1731. line = line.substr(0, start) + "JSON.new().stringify(" + connect_arguments(parts, 0) + ")" + line.substr(end + start);
  1732. }
  1733. }
  1734. // -- .xform(Vector3(a,b,c)) -> * Vector3(a,b,c) Transform
  1735. if (line.contains(".xform(")) {
  1736. int start = line.find(".xform(");
  1737. int end = get_end_parenthesis(line.substr(start)) + 1;
  1738. if (end > -1) {
  1739. Vector<String> parts = parse_arguments(line.substr(start, end));
  1740. if (parts.size() == 1) {
  1741. line = line.substr(0, start) + " * (" + parts[0] + ")" + line.substr(end + start);
  1742. }
  1743. }
  1744. }
  1745. // -- .xform_inv(Vector3(a,b,c)) -> * Vector3(a,b,c) Transform
  1746. if (line.contains(".xform_inv(")) {
  1747. int start = line.find(".xform_inv(");
  1748. int end = get_end_parenthesis(line.substr(start)) + 1;
  1749. if (end > -1) {
  1750. String object_exec = get_object_of_execution(line.substr(0, start));
  1751. if (line.contains(object_exec + ".xform")) {
  1752. int start2 = line.find(object_exec + ".xform");
  1753. Vector<String> parts = parse_arguments(line.substr(start, end));
  1754. if (parts.size() == 1) {
  1755. line = line.substr(0, start2) + "(" + parts[0] + ") * " + object_exec + line.substr(end + start);
  1756. }
  1757. }
  1758. }
  1759. }
  1760. // -- "(connect(A,B,C,D,E) != OK):", "(connect(A, Callable(B, C).bind(D), E) Object
  1761. if (contains_function_call(line, "connect(")) {
  1762. int start = line.find("connect(");
  1763. int end = get_end_parenthesis(line.substr(start)) + 1;
  1764. if (end > -1) {
  1765. Vector<String> parts = parse_arguments(line.substr(start, end));
  1766. if (parts.size() == 3) {
  1767. line = line.substr(0, start) + "connect(" + parts[0] + ", Callable(" + parts[1] + ", " + parts[2] + "))" + line.substr(end + start);
  1768. } else if (parts.size() >= 4) {
  1769. line = 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);
  1770. }
  1771. }
  1772. }
  1773. // -- disconnect(a,b,c) -> disconnect(a,Callable(b,c)) Object
  1774. if (contains_function_call(line, "disconnect(")) {
  1775. int start = line.find("disconnect(");
  1776. int end = get_end_parenthesis(line.substr(start)) + 1;
  1777. if (end > -1) {
  1778. Vector<String> parts = parse_arguments(line.substr(start, end));
  1779. if (parts.size() == 3) {
  1780. line = line.substr(0, start) + "disconnect(" + parts[0] + ", Callable(" + parts[1] + ", " + parts[2] + "))" + line.substr(end + start);
  1781. }
  1782. }
  1783. }
  1784. // -- is_connected(a,b,c) -> is_connected(a,Callable(b,c)) Object
  1785. if (contains_function_call(line, "is_connected(")) {
  1786. int start = line.find("is_connected(");
  1787. int end = get_end_parenthesis(line.substr(start)) + 1;
  1788. if (end > -1) {
  1789. Vector<String> parts = parse_arguments(line.substr(start, end));
  1790. if (parts.size() == 3) {
  1791. line = line.substr(0, start) + "is_connected(" + parts[0] + ", Callable(" + parts[1] + ", " + parts[2] + "))" + line.substr(end + start);
  1792. }
  1793. }
  1794. }
  1795. // -- "(tween_method(A,B,C,D,E) != OK):", "(tween_method(Callable(A,B),C,D,E) Object
  1796. // -- "(tween_method(A,B,C,D,E,[F,G]) != OK):", "(tween_method(Callable(A,B).bind(F,G),C,D,E) Object
  1797. if (contains_function_call(line, "tween_method(")) {
  1798. int start = line.find("tween_method(");
  1799. int end = get_end_parenthesis(line.substr(start)) + 1;
  1800. if (end > -1) {
  1801. Vector<String> parts = parse_arguments(line.substr(start, end));
  1802. if (parts.size() == 5) {
  1803. line = line.substr(0, start) + "tween_method(Callable(" + parts[0] + ", " + parts[1] + "), " + parts[2] + ", " + parts[3] + ", " + parts[4] + ")" + line.substr(end + start);
  1804. } else if (parts.size() >= 6) {
  1805. line = 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);
  1806. }
  1807. }
  1808. }
  1809. // -- "(tween_callback(A,B,[C,D]) != OK):", "(connect(Callable(A,B).bind(C,D)) Object
  1810. if (contains_function_call(line, "tween_callback(")) {
  1811. int start = line.find("tween_callback(");
  1812. int end = get_end_parenthesis(line.substr(start)) + 1;
  1813. if (end > -1) {
  1814. Vector<String> parts = parse_arguments(line.substr(start, end));
  1815. if (parts.size() == 2) {
  1816. line = line.substr(0, start) + "tween_callback(Callable(" + parts[0] + ", " + parts[1] + "))" + line.substr(end + start);
  1817. } else if (parts.size() >= 3) {
  1818. line = line.substr(0, start) + "tween_callback(Callable(" + parts[0] + ", " + parts[1] + ").bind(" + connect_arguments(parts, 2).substr(1).lstrip(" [").rstrip("] ") + "))" + line.substr(end + start);
  1819. }
  1820. }
  1821. }
  1822. // -- start(a,b) -> start(Callable(a, b)) Thread
  1823. // -- start(a,b,c,d) -> start(Callable(a, b).bind(c), d) Thread
  1824. if (contains_function_call(line, "start(")) {
  1825. int start = line.find("start(");
  1826. int end = get_end_parenthesis(line.substr(start)) + 1;
  1827. // Protection from 'func start'
  1828. if (!line.begins_with("func ")) {
  1829. if (end > -1) {
  1830. Vector<String> parts = parse_arguments(line.substr(start, end));
  1831. if (parts.size() == 2) {
  1832. line = line.substr(0, start) + "start(Callable(" + parts[0] + ", " + parts[1] + "))" + line.substr(end + start);
  1833. } else if (parts.size() >= 3) {
  1834. line = line.substr(0, start) + "start(Callable(" + parts[0] + ", " + parts[1] + ").bind(" + parts[2] + ")" + connect_arguments(parts, 3) + ")" + line.substr(end + start);
  1835. }
  1836. }
  1837. }
  1838. }
  1839. // -- func _init(p_x:int).(p_x): -> func _init(p_x:int):\n\tsuper(p_x) Object # https://github.com/godotengine/godot/issues/70542
  1840. if (line.contains(" _init(") && line.rfind_char(':') > 0) {
  1841. // func _init(p_arg1).(super4, super5, super6)->void:
  1842. // ^--^indent ^super_start super_end^
  1843. int indent = line.count("\t", 0, line.find("func"));
  1844. int super_start = line.find(".(");
  1845. int super_end = line.rfind_char(')');
  1846. if (super_start > 0 && super_end > super_start) {
  1847. line = 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);
  1848. }
  1849. }
  1850. // create_from_image(aa, bb) -> create_from_image(aa) #, bb ImageTexture
  1851. if (contains_function_call(line, "create_from_image(")) {
  1852. int start = line.find("create_from_image(");
  1853. int end = get_end_parenthesis(line.substr(start)) + 1;
  1854. if (end > -1) {
  1855. Vector<String> parts = parse_arguments(line.substr(start, end));
  1856. if (parts.size() == 2) {
  1857. line = line.substr(0, start) + "create_from_image(" + parts[0] + ") " + "#," + parts[1] + line.substr(end + start);
  1858. }
  1859. }
  1860. }
  1861. // set_cell_item(a, b, c, d ,e) -> set_cell_item(Vector3(a, b, c), d ,e)
  1862. if (contains_function_call(line, "set_cell_item(")) {
  1863. int start = line.find("set_cell_item(");
  1864. int end = get_end_parenthesis(line.substr(start)) + 1;
  1865. if (end > -1) {
  1866. Vector<String> parts = parse_arguments(line.substr(start, end));
  1867. if (parts.size() > 2) {
  1868. line = line.substr(0, start) + "set_cell_item(Vector3(" + parts[0] + ", " + parts[1] + ", " + parts[2] + ")" + connect_arguments(parts, 3).lstrip(" ") + ")" + line.substr(end + start);
  1869. }
  1870. }
  1871. }
  1872. // get_cell_item(a, b, c) -> get_cell_item(Vector3i(a, b, c))
  1873. if (contains_function_call(line, "get_cell_item(")) {
  1874. int start = line.find("get_cell_item(");
  1875. int end = get_end_parenthesis(line.substr(start)) + 1;
  1876. if (end > -1) {
  1877. Vector<String> parts = parse_arguments(line.substr(start, end));
  1878. if (parts.size() == 3) {
  1879. line = line.substr(0, start) + "get_cell_item(Vector3i(" + parts[0] + ", " + parts[1] + ", " + parts[2] + "))" + line.substr(end + start);
  1880. }
  1881. }
  1882. }
  1883. // get_cell_item_orientation(a, b, c) -> get_cell_item_orientation(Vector3i(a, b, c))
  1884. if (contains_function_call(line, "get_cell_item_orientation(")) {
  1885. int start = line.find("get_cell_item_orientation(");
  1886. int end = get_end_parenthesis(line.substr(start)) + 1;
  1887. if (end > -1) {
  1888. Vector<String> parts = parse_arguments(line.substr(start, end));
  1889. if (parts.size() == 3) {
  1890. line = line.substr(0, start) + "get_cell_item_orientation(Vector3i(" + parts[0] + ", " + parts[1] + ", " + parts[2] + "))" + line.substr(end + start);
  1891. }
  1892. }
  1893. }
  1894. // apply_impulse(A, B) -> apply_impulse(B, A)
  1895. if (contains_function_call(line, "apply_impulse(")) {
  1896. int start = line.find("apply_impulse(");
  1897. int end = get_end_parenthesis(line.substr(start)) + 1;
  1898. if (end > -1) {
  1899. Vector<String> parts = parse_arguments(line.substr(start, end));
  1900. if (parts.size() == 2) {
  1901. line = line.substr(0, start) + "apply_impulse(" + parts[1] + ", " + parts[0] + ")" + line.substr(end + start);
  1902. }
  1903. }
  1904. }
  1905. // apply_force(A, B) -> apply_force(B, A)
  1906. if (contains_function_call(line, "apply_force(")) {
  1907. int start = line.find("apply_force(");
  1908. int end = get_end_parenthesis(line.substr(start)) + 1;
  1909. if (end > -1) {
  1910. Vector<String> parts = parse_arguments(line.substr(start, end));
  1911. if (parts.size() == 2) {
  1912. line = line.substr(0, start) + "apply_force(" + parts[1] + ", " + parts[0] + ")" + line.substr(end + start);
  1913. }
  1914. }
  1915. }
  1916. // map_to_world(a, b, c) -> map_to_local(Vector3i(a, b, c))
  1917. if (contains_function_call(line, "map_to_world(")) {
  1918. int start = line.find("map_to_world(");
  1919. int end = get_end_parenthesis(line.substr(start)) + 1;
  1920. if (end > -1) {
  1921. Vector<String> parts = parse_arguments(line.substr(start, end));
  1922. if (parts.size() == 3) {
  1923. line = line.substr(0, start) + "map_to_local(Vector3i(" + parts[0] + ", " + parts[1] + ", " + parts[2] + "))" + line.substr(end + start);
  1924. } else if (parts.size() == 1) {
  1925. line = line.substr(0, start) + "map_to_local(" + parts[0] + ")" + line.substr(end + start);
  1926. }
  1927. }
  1928. }
  1929. // set_rotating(true) -> set_ignore_rotation(false)
  1930. if (contains_function_call(line, "set_rotating(")) {
  1931. int start = line.find("set_rotating(");
  1932. int end = get_end_parenthesis(line.substr(start)) + 1;
  1933. if (end > -1) {
  1934. Vector<String> parts = parse_arguments(line.substr(start, end));
  1935. if (parts.size() == 1) {
  1936. String opposite = parts[0] == "true" ? "false" : "true";
  1937. line = line.substr(0, start) + "set_ignore_rotation(" + opposite + ")";
  1938. }
  1939. }
  1940. }
  1941. // OS.get_window_safe_area() -> DisplayServer.get_display_safe_area()
  1942. if (line.contains("OS.get_window_safe_area(")) {
  1943. int start = line.find("OS.get_window_safe_area(");
  1944. int end = get_end_parenthesis(line.substr(start)) + 1;
  1945. if (end > -1) {
  1946. Vector<String> parts = parse_arguments(line.substr(start, end));
  1947. if (parts.size() == 0) {
  1948. line = line.substr(0, start) + "DisplayServer.get_display_safe_area()" + line.substr(end + start);
  1949. }
  1950. }
  1951. }
  1952. // draw_rect(a,b,c,d,e) -> draw_rect(a,b,c,d)#e) TODOConverter3To4 Antialiasing argument is missing
  1953. if (contains_function_call(line, "draw_rect(")) {
  1954. int start = line.find("draw_rect(");
  1955. int end = get_end_parenthesis(line.substr(start)) + 1;
  1956. if (end > -1) {
  1957. Vector<String> parts = parse_arguments(line.substr(start, end));
  1958. if (parts.size() == 5) {
  1959. line = 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";
  1960. }
  1961. }
  1962. }
  1963. // get_focus_owner() -> get_viewport().gui_get_focus_owner()
  1964. if (contains_function_call(line, "get_focus_owner()")) {
  1965. line = line.replace("get_focus_owner()", "get_viewport().gui_get_focus_owner()");
  1966. }
  1967. // button.pressed = 1 -> button.button_pressed = 1
  1968. if (line.contains(".pressed")) {
  1969. int start = line.find(".pressed");
  1970. bool foundNextEqual = false;
  1971. String line_to_check = line.substr(start + String(".pressed").length());
  1972. for (int current_index = 0; line_to_check.length() > current_index; current_index++) {
  1973. char32_t chr = line_to_check.get(current_index);
  1974. if (chr == '\t' || chr == ' ') {
  1975. continue;
  1976. } else if (chr == '=') {
  1977. foundNextEqual = true;
  1978. } else {
  1979. break;
  1980. }
  1981. }
  1982. if (foundNextEqual) {
  1983. line = line.substr(0, start) + ".button_pressed" + line.substr(start + String(".pressed").length());
  1984. }
  1985. }
  1986. // rotating = true -> ignore_rotation = false # reversed "rotating" for Camera2D
  1987. if (contains_function_call(line, "rotating")) {
  1988. String function_name = "rotating";
  1989. int start = line.find(function_name);
  1990. bool foundNextEqual = false;
  1991. String line_to_check = line.substr(start + function_name.length());
  1992. String assigned_value;
  1993. for (int current_index = 0; line_to_check.length() > current_index; current_index++) {
  1994. char32_t chr = line_to_check.get(current_index);
  1995. if (chr == '\t' || chr == ' ') {
  1996. continue;
  1997. } else if (chr == '=') {
  1998. foundNextEqual = true;
  1999. assigned_value = line.substr(start + function_name.length() + current_index + 1).strip_edges();
  2000. assigned_value = assigned_value == "true" ? "false" : "true";
  2001. } else {
  2002. break;
  2003. }
  2004. }
  2005. if (foundNextEqual) {
  2006. line = line.substr(0, start) + "ignore_rotation = " + assigned_value + " # reversed \"rotating\" for Camera2D";
  2007. }
  2008. }
  2009. // OS -> Time functions
  2010. if (line.contains("OS.get_ticks_msec")) {
  2011. line = line.replace("OS.get_ticks_msec", "Time.get_ticks_msec");
  2012. }
  2013. if (line.contains("OS.get_ticks_usec")) {
  2014. line = line.replace("OS.get_ticks_usec", "Time.get_ticks_usec");
  2015. }
  2016. if (line.contains("OS.get_unix_time")) {
  2017. line = line.replace("OS.get_unix_time", "Time.get_unix_time_from_system");
  2018. }
  2019. if (line.contains("OS.get_datetime")) {
  2020. line = line.replace("OS.get_datetime", "Time.get_datetime_dict_from_system");
  2021. }
  2022. // OS -> DisplayServer
  2023. if (line.contains("OS.get_display_cutouts")) {
  2024. line = line.replace("OS.get_display_cutouts", "DisplayServer.get_display_cutouts");
  2025. }
  2026. if (line.contains("OS.get_screen_count")) {
  2027. line = line.replace("OS.get_screen_count", "DisplayServer.get_screen_count");
  2028. }
  2029. if (line.contains("OS.get_screen_dpi")) {
  2030. line = line.replace("OS.get_screen_dpi", "DisplayServer.screen_get_dpi");
  2031. }
  2032. if (line.contains("OS.get_screen_max_scale")) {
  2033. line = line.replace("OS.get_screen_max_scale", "DisplayServer.screen_get_max_scale");
  2034. }
  2035. if (line.contains("OS.get_screen_position")) {
  2036. line = line.replace("OS.get_screen_position", "DisplayServer.screen_get_position");
  2037. }
  2038. if (line.contains("OS.get_screen_refresh_rate")) {
  2039. line = line.replace("OS.get_screen_refresh_rate", "DisplayServer.screen_get_refresh_rate");
  2040. }
  2041. if (line.contains("OS.get_screen_scale")) {
  2042. line = line.replace("OS.get_screen_scale", "DisplayServer.screen_get_scale");
  2043. }
  2044. if (line.contains("OS.get_screen_size")) {
  2045. line = line.replace("OS.get_screen_size", "DisplayServer.screen_get_size");
  2046. }
  2047. if (line.contains("OS.set_icon")) {
  2048. line = line.replace("OS.set_icon", "DisplayServer.set_icon");
  2049. }
  2050. if (line.contains("OS.set_native_icon")) {
  2051. line = line.replace("OS.set_native_icon", "DisplayServer.set_native_icon");
  2052. }
  2053. // OS -> Window
  2054. if (line.contains("OS.window_borderless")) {
  2055. line = line.replace("OS.window_borderless", "get_window().borderless");
  2056. }
  2057. if (line.contains("OS.get_real_window_size")) {
  2058. line = line.replace("OS.get_real_window_size", "get_window().get_size_with_decorations");
  2059. }
  2060. if (line.contains("OS.is_window_focused")) {
  2061. line = line.replace("OS.is_window_focused", "get_window().has_focus");
  2062. }
  2063. if (line.contains("OS.move_window_to_foreground")) {
  2064. line = line.replace("OS.move_window_to_foreground", "get_window().grab_focus");
  2065. }
  2066. if (line.contains("OS.request_attention")) {
  2067. line = line.replace("OS.request_attention", "get_window().request_attention");
  2068. }
  2069. if (line.contains("OS.set_window_title")) {
  2070. line = line.replace("OS.set_window_title", "get_window().set_title");
  2071. }
  2072. // get_tree().set_input_as_handled() -> get_viewport().set_input_as_handled()
  2073. if (line.contains("get_tree().set_input_as_handled()")) {
  2074. line = line.replace("get_tree().set_input_as_handled()", "get_viewport().set_input_as_handled()");
  2075. }
  2076. // Fix the simple case of using _unhandled_key_input
  2077. // func _unhandled_key_input(event: InputEventKey) -> _unhandled_key_input(event: InputEvent)
  2078. if (line.contains("_unhandled_key_input(event: InputEventKey)")) {
  2079. line = line.replace("_unhandled_key_input(event: InputEventKey)", "_unhandled_key_input(event: InputEvent)");
  2080. }
  2081. if (line.contains("Engine.editor_hint")) {
  2082. line = line.replace("Engine.editor_hint", "Engine.is_editor_hint()");
  2083. }
  2084. }
  2085. void ProjectConverter3To4::process_csharp_line(String &line, const RegExContainer &reg_container) {
  2086. line = line.replace("OS.GetWindowSafeArea()", "DisplayServer.ScreenGetUsableRect()");
  2087. // GetTree().SetInputAsHandled() -> GetViewport().SetInputAsHandled()
  2088. if (line.contains("GetTree().SetInputAsHandled()")) {
  2089. line = line.replace("GetTree().SetInputAsHandled()", "GetViewport().SetInputAsHandled()");
  2090. }
  2091. // Fix the simple case of using _UnhandledKeyInput
  2092. // func _UnhandledKeyInput(InputEventKey @event) -> _UnhandledKeyInput(InputEvent @event)
  2093. if (line.contains("_UnhandledKeyInput(InputEventKey @event)")) {
  2094. line = line.replace("_UnhandledKeyInput(InputEventKey @event)", "_UnhandledKeyInput(InputEvent @event)");
  2095. }
  2096. // -- Connect(,,,things) -> Connect(,Callable(,),things) Object
  2097. if (line.contains("Connect(")) {
  2098. int start = line.find("Connect(");
  2099. // Protection from disconnect
  2100. if (start == 0 || line.get(start - 1) != 's') {
  2101. int end = get_end_parenthesis(line.substr(start)) + 1;
  2102. if (end > -1) {
  2103. Vector<String> parts = parse_arguments(line.substr(start, end));
  2104. if (parts.size() >= 3) {
  2105. line = line.substr(0, start) + "Connect(" + parts[0] + ", new Callable(" + parts[1] + ", " + parts[2] + ")" + connect_arguments(parts, 3) + ")" + line.substr(end + start);
  2106. }
  2107. }
  2108. }
  2109. }
  2110. // -- Disconnect(a,b,c) -> Disconnect(a,Callable(b,c)) Object
  2111. if (line.contains("Disconnect(")) {
  2112. int start = line.find("Disconnect(");
  2113. int end = get_end_parenthesis(line.substr(start)) + 1;
  2114. if (end > -1) {
  2115. Vector<String> parts = parse_arguments(line.substr(start, end));
  2116. if (parts.size() == 3) {
  2117. line = line.substr(0, start) + "Disconnect(" + parts[0] + ", new Callable(" + parts[1] + ", " + parts[2] + "))" + line.substr(end + start);
  2118. }
  2119. }
  2120. }
  2121. // -- IsConnected(a,b,c) -> IsConnected(a,Callable(b,c)) Object
  2122. if (line.contains("IsConnected(")) {
  2123. int start = line.find("IsConnected(");
  2124. int end = get_end_parenthesis(line.substr(start)) + 1;
  2125. if (end > -1) {
  2126. Vector<String> parts = parse_arguments(line.substr(start, end));
  2127. if (parts.size() == 3) {
  2128. line = line.substr(0, start) + "IsConnected(" + parts[0] + ", new Callable(" + parts[1] + ", " + parts[2] + "))" + line.substr(end + start);
  2129. }
  2130. }
  2131. }
  2132. }
  2133. void ProjectConverter3To4::rename_csharp_functions(Vector<SourceLine> &source_lines, const RegExContainer &reg_container) {
  2134. for (SourceLine &source_line : source_lines) {
  2135. if (source_line.is_comment) {
  2136. continue;
  2137. }
  2138. String &line = source_line.line;
  2139. if (uint64_t(line.length()) <= maximum_line_length) {
  2140. process_csharp_line(line, reg_container);
  2141. }
  2142. }
  2143. }
  2144. Vector<String> ProjectConverter3To4::check_for_rename_csharp_functions(Vector<String> &lines, const RegExContainer &reg_container) {
  2145. int current_line = 1;
  2146. Vector<String> found_renames;
  2147. for (String &line : lines) {
  2148. if (uint64_t(line.length()) <= maximum_line_length) {
  2149. String old_line = line;
  2150. process_csharp_line(line, reg_container);
  2151. if (old_line != line) {
  2152. found_renames.append(simple_line_formatter(current_line, old_line, line));
  2153. }
  2154. }
  2155. }
  2156. return found_renames;
  2157. }
  2158. void ProjectConverter3To4::rename_csharp_attributes(Vector<SourceLine> &source_lines, const RegExContainer &reg_container) {
  2159. static 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";
  2160. for (SourceLine &source_line : source_lines) {
  2161. if (source_line.is_comment) {
  2162. continue;
  2163. }
  2164. String &line = source_line.line;
  2165. if (uint64_t(line.length()) <= maximum_line_length) {
  2166. line = reg_container.keyword_csharp_remote.sub(line, "[RPC(MultiplayerAPI.RPCMode.AnyPeer)]", true);
  2167. line = reg_container.keyword_csharp_remotesync.sub(line, "[RPC(MultiplayerAPI.RPCMode.AnyPeer, CallLocal = true)]", true);
  2168. line = reg_container.keyword_csharp_puppet.sub(line, "[RPC]", true);
  2169. line = reg_container.keyword_csharp_puppetsync.sub(line, "[RPC(CallLocal = true)]", true);
  2170. line = reg_container.keyword_csharp_master.sub(line, error_message + "[RPC]", true);
  2171. line = reg_container.keyword_csharp_mastersync.sub(line, error_message + "[RPC(CallLocal = true)]", true);
  2172. }
  2173. }
  2174. }
  2175. Vector<String> ProjectConverter3To4::check_for_rename_csharp_attributes(Vector<String> &lines, const RegExContainer &reg_container) {
  2176. int current_line = 1;
  2177. Vector<String> found_renames;
  2178. for (String &line : lines) {
  2179. if (uint64_t(line.length()) <= maximum_line_length) {
  2180. String old;
  2181. old = line;
  2182. line = reg_container.keyword_csharp_remote.sub(line, "[RPC(MultiplayerAPI.RPCMode.AnyPeer)]", true);
  2183. if (old != line) {
  2184. found_renames.append(line_formatter(current_line, "[Remote]", "[RPC(MultiplayerAPI.RPCMode.AnyPeer)]", line));
  2185. }
  2186. old = line;
  2187. line = reg_container.keyword_csharp_remotesync.sub(line, "[RPC(MultiplayerAPI.RPCMode.AnyPeer, CallLocal = true)]", true);
  2188. if (old != line) {
  2189. found_renames.append(line_formatter(current_line, "[RemoteSync]", "[RPC(MultiplayerAPI.RPCMode.AnyPeer, CallLocal = true)]", line));
  2190. }
  2191. old = line;
  2192. line = reg_container.keyword_csharp_puppet.sub(line, "[RPC]", true);
  2193. if (old != line) {
  2194. found_renames.append(line_formatter(current_line, "[Puppet]", "[RPC]", line));
  2195. }
  2196. old = line;
  2197. line = reg_container.keyword_csharp_puppetsync.sub(line, "[RPC(CallLocal = true)]", true);
  2198. if (old != line) {
  2199. found_renames.append(line_formatter(current_line, "[PuppetSync]", "[RPC(CallLocal = true)]", line));
  2200. }
  2201. old = line;
  2202. line = reg_container.keyword_csharp_master.sub(line, "[RPC]", true);
  2203. if (old != line) {
  2204. found_renames.append(line_formatter(current_line, "[Master]", "[RPC]", line));
  2205. }
  2206. old = line;
  2207. line = reg_container.keyword_csharp_mastersync.sub(line, "[RPC(CallLocal = true)]", true);
  2208. if (old != line) {
  2209. found_renames.append(line_formatter(current_line, "[MasterSync]", "[RPC(CallLocal = true)]", line));
  2210. }
  2211. }
  2212. current_line++;
  2213. }
  2214. return found_renames;
  2215. }
  2216. _FORCE_INLINE_ static String builtin_escape(const String &p_str, bool p_builtin) {
  2217. if (p_builtin) {
  2218. return p_str.replace("\"", "\\\"");
  2219. } else {
  2220. return p_str;
  2221. }
  2222. }
  2223. void ProjectConverter3To4::rename_gdscript_keywords(Vector<SourceLine> &source_lines, const RegExContainer &reg_container, bool builtin) {
  2224. static 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";
  2225. for (SourceLine &source_line : source_lines) {
  2226. if (source_line.is_comment) {
  2227. continue;
  2228. }
  2229. String &line = source_line.line;
  2230. if (uint64_t(line.length()) <= maximum_line_length) {
  2231. if (line.contains("export")) {
  2232. line = reg_container.keyword_gdscript_export_single.sub(line, "@export", true);
  2233. }
  2234. if (line.contains("export")) {
  2235. line = reg_container.keyword_gdscript_export_multi.sub(line, "$1@export", true);
  2236. }
  2237. if (line.contains("onready")) {
  2238. line = reg_container.keyword_gdscript_onready.sub(line, "@onready", true);
  2239. }
  2240. if (line.contains("remote")) {
  2241. line = reg_container.keyword_gdscript_remote.sub(line, builtin_escape("@rpc(\"any_peer\") func", builtin), true);
  2242. }
  2243. if (line.contains("remote")) {
  2244. line = reg_container.keyword_gdscript_remotesync.sub(line, builtin_escape("@rpc(\"any_peer\", \"call_local\") func", builtin), true);
  2245. }
  2246. if (line.contains("sync")) {
  2247. line = reg_container.keyword_gdscript_sync.sub(line, builtin_escape("@rpc(\"any_peer\", \"call_local\") func", builtin), true);
  2248. }
  2249. if (line.contains("slave")) {
  2250. line = reg_container.keyword_gdscript_slave.sub(line, "@rpc func", true);
  2251. }
  2252. if (line.contains("puppet")) {
  2253. line = reg_container.keyword_gdscript_puppet.sub(line, "@rpc func", true);
  2254. }
  2255. if (line.contains("puppet")) {
  2256. line = reg_container.keyword_gdscript_puppetsync.sub(line, builtin_escape("@rpc(\"call_local\") func", builtin), true);
  2257. }
  2258. if (line.contains("master")) {
  2259. line = reg_container.keyword_gdscript_master.sub(line, error_message + "@rpc func", true);
  2260. }
  2261. if (line.contains("master")) {
  2262. line = reg_container.keyword_gdscript_mastersync.sub(line, error_message + builtin_escape("@rpc(\"call_local\") func", builtin), true);
  2263. }
  2264. }
  2265. }
  2266. }
  2267. Vector<String> ProjectConverter3To4::check_for_rename_gdscript_keywords(Vector<String> &lines, const RegExContainer &reg_container, bool builtin) {
  2268. Vector<String> found_renames;
  2269. int current_line = 1;
  2270. for (String &line : lines) {
  2271. if (uint64_t(line.length()) <= maximum_line_length) {
  2272. String old;
  2273. if (line.contains("tool")) {
  2274. old = line;
  2275. line = reg_container.keyword_gdscript_tool.sub(line, "@tool", true);
  2276. if (old != line) {
  2277. found_renames.append(line_formatter(current_line, "tool", "@tool", line));
  2278. }
  2279. }
  2280. if (line.contains("export")) {
  2281. old = line;
  2282. line = reg_container.keyword_gdscript_export_single.sub(line, "$1@export", true);
  2283. if (old != line) {
  2284. found_renames.append(line_formatter(current_line, "export", "@export", line));
  2285. }
  2286. }
  2287. if (line.contains("export")) {
  2288. old = line;
  2289. line = reg_container.keyword_gdscript_export_multi.sub(line, "@export", true);
  2290. if (old != line) {
  2291. found_renames.append(line_formatter(current_line, "export", "@export", line));
  2292. }
  2293. }
  2294. if (line.contains("onready")) {
  2295. old = line;
  2296. line = reg_container.keyword_gdscript_tool.sub(line, "@onready", true);
  2297. if (old != line) {
  2298. found_renames.append(line_formatter(current_line, "onready", "@onready", line));
  2299. }
  2300. }
  2301. if (line.contains("remote")) {
  2302. old = line;
  2303. line = reg_container.keyword_gdscript_remote.sub(line, builtin_escape("@rpc(\"any_peer\") func", builtin), true);
  2304. if (old != line) {
  2305. found_renames.append(line_formatter(current_line, "remote func", builtin_escape("@rpc(\"any_peer\") func", builtin), line));
  2306. }
  2307. }
  2308. if (line.contains("remote")) {
  2309. old = line;
  2310. line = reg_container.keyword_gdscript_remotesync.sub(line, builtin_escape("@rpc(\"any_peer\", \"call_local\")) func", builtin), true);
  2311. if (old != line) {
  2312. found_renames.append(line_formatter(current_line, "remotesync func", builtin_escape("@rpc(\"any_peer\", \"call_local\")) func", builtin), line));
  2313. }
  2314. }
  2315. if (line.contains("sync")) {
  2316. old = line;
  2317. line = reg_container.keyword_gdscript_sync.sub(line, builtin_escape("@rpc(\"any_peer\", \"call_local\")) func", builtin), true);
  2318. if (old != line) {
  2319. found_renames.append(line_formatter(current_line, "sync func", builtin_escape("@rpc(\"any_peer\", \"call_local\")) func", builtin), line));
  2320. }
  2321. }
  2322. if (line.contains("slave")) {
  2323. old = line;
  2324. line = reg_container.keyword_gdscript_slave.sub(line, "@rpc func", true);
  2325. if (old != line) {
  2326. found_renames.append(line_formatter(current_line, "slave func", "@rpc func", line));
  2327. }
  2328. }
  2329. if (line.contains("puppet")) {
  2330. old = line;
  2331. line = reg_container.keyword_gdscript_puppet.sub(line, "@rpc func", true);
  2332. if (old != line) {
  2333. found_renames.append(line_formatter(current_line, "puppet func", "@rpc func", line));
  2334. }
  2335. }
  2336. if (line.contains("puppet")) {
  2337. old = line;
  2338. line = reg_container.keyword_gdscript_puppetsync.sub(line, builtin_escape("@rpc(\"call_local\") func", builtin), true);
  2339. if (old != line) {
  2340. found_renames.append(line_formatter(current_line, "puppetsync func", builtin_escape("@rpc(\"call_local\") func", builtin), line));
  2341. }
  2342. }
  2343. if (line.contains("master")) {
  2344. old = line;
  2345. line = reg_container.keyword_gdscript_master.sub(line, "@rpc func", true);
  2346. if (old != line) {
  2347. found_renames.append(line_formatter(current_line, "master func", "@rpc func", line));
  2348. }
  2349. }
  2350. if (line.contains("master")) {
  2351. old = line;
  2352. line = reg_container.keyword_gdscript_master.sub(line, builtin_escape("@rpc(\"call_local\") func", builtin), true);
  2353. if (old != line) {
  2354. found_renames.append(line_formatter(current_line, "mastersync func", builtin_escape("@rpc(\"call_local\") func", builtin), line));
  2355. }
  2356. }
  2357. }
  2358. current_line++;
  2359. }
  2360. return found_renames;
  2361. }
  2362. void ProjectConverter3To4::rename_input_map_scancode(Vector<SourceLine> &source_lines, const RegExContainer &reg_container) {
  2363. // The old Special Key, now colliding with CMD_OR_CTRL.
  2364. const int old_spkey = (1 << 24);
  2365. for (SourceLine &source_line : source_lines) {
  2366. if (source_line.is_comment) {
  2367. continue;
  2368. }
  2369. String &line = source_line.line;
  2370. if (uint64_t(line.length()) <= maximum_line_length) {
  2371. TypedArray<RegExMatch> reg_match = reg_container.input_map_keycode.search_all(line);
  2372. for (int i = 0; i < reg_match.size(); ++i) {
  2373. Ref<RegExMatch> match = reg_match[i];
  2374. PackedStringArray strings = match->get_strings();
  2375. int key = strings[3].to_int();
  2376. if (key & old_spkey) {
  2377. // Create new key, clearing old Special Key and setting new one.
  2378. key = (key & ~old_spkey) | (int)Key::SPECIAL;
  2379. line = line.replace(strings[0], String(",\"") + strings[1] + "scancode\":" + String::num_int64(key));
  2380. }
  2381. }
  2382. }
  2383. }
  2384. }
  2385. void ProjectConverter3To4::rename_joypad_buttons_and_axes(Vector<SourceLine> &source_lines, const RegExContainer &reg_container) {
  2386. for (SourceLine &source_line : source_lines) {
  2387. if (source_line.is_comment) {
  2388. continue;
  2389. }
  2390. String &line = source_line.line;
  2391. if (uint64_t(line.length()) <= maximum_line_length) {
  2392. // Remap button indexes.
  2393. TypedArray<RegExMatch> reg_match = reg_container.joypad_button_index.search_all(line);
  2394. for (int i = 0; i < reg_match.size(); ++i) {
  2395. Ref<RegExMatch> match = reg_match[i];
  2396. PackedStringArray strings = match->get_strings();
  2397. const String &button_index_entry = strings[0];
  2398. int button_index_value = strings[1].to_int();
  2399. if (button_index_value == 6) { // L2 and R2 are mapped to joypad axes in Godot 4.
  2400. line = line.replace("InputEventJoypadButton", "InputEventJoypadMotion");
  2401. line = line.replace(button_index_entry, ",\"axis\":4,\"axis_value\":1.0");
  2402. } else if (button_index_value == 7) {
  2403. line = line.replace("InputEventJoypadButton", "InputEventJoypadMotion");
  2404. line = line.replace(button_index_entry, ",\"axis\":5,\"axis_value\":1.0");
  2405. } else if (button_index_value < 22) { // There are no mappings for indexes greater than 22 in both Godot 3 & 4.
  2406. const String &pressure_and_pressed_properties = strings[2];
  2407. line = line.replace(button_index_entry, ",\"button_index\":" + String::num_int64(reg_container.joypad_button_mappings[button_index_value]) + "," + pressure_and_pressed_properties);
  2408. }
  2409. }
  2410. // Remap axes. Only L2 and R2 need remapping.
  2411. reg_match = reg_container.joypad_axis.search_all(line);
  2412. for (int i = 0; i < reg_match.size(); ++i) {
  2413. Ref<RegExMatch> match = reg_match[i];
  2414. PackedStringArray strings = match->get_strings();
  2415. const String &axis_entry = strings[0];
  2416. int axis_value = strings[1].to_int();
  2417. if (axis_value == 6) {
  2418. line = line.replace(axis_entry, ",\"axis\":4");
  2419. } else if (axis_value == 7) {
  2420. line = line.replace(axis_entry, ",\"axis\":5");
  2421. }
  2422. }
  2423. }
  2424. }
  2425. }
  2426. Vector<String> ProjectConverter3To4::check_for_rename_joypad_buttons_and_axes(Vector<String> &lines, const RegExContainer &reg_container) {
  2427. Vector<String> found_renames;
  2428. int current_line = 1;
  2429. for (String &line : lines) {
  2430. if (uint64_t(line.length()) <= maximum_line_length) {
  2431. // Remap button indexes.
  2432. TypedArray<RegExMatch> reg_match = reg_container.joypad_button_index.search_all(line);
  2433. for (int i = 0; i < reg_match.size(); ++i) {
  2434. Ref<RegExMatch> match = reg_match[i];
  2435. PackedStringArray strings = match->get_strings();
  2436. const String &button_index_entry = strings[0];
  2437. int button_index_value = strings[1].to_int();
  2438. if (button_index_value == 6) { // L2 and R2 are mapped to joypad axes in Godot 4.
  2439. found_renames.append(line_formatter(current_line, "InputEventJoypadButton", "InputEventJoypadMotion", line));
  2440. found_renames.append(line_formatter(current_line, button_index_entry, ",\"axis\":4", line));
  2441. } else if (button_index_value == 7) {
  2442. found_renames.append(line_formatter(current_line, "InputEventJoypadButton", "InputEventJoypadMotion", line));
  2443. found_renames.append(line_formatter(current_line, button_index_entry, ",\"axis\":5", line));
  2444. } else if (button_index_value < 22) { // There are no mappings for indexes greater than 22 in both Godot 3 & 4.
  2445. found_renames.append(line_formatter(current_line, "\"button_index\":" + strings[1], "\"button_index\":" + String::num_int64(reg_container.joypad_button_mappings[button_index_value]), line));
  2446. }
  2447. }
  2448. // Remap axes. Only L2 and R2 need remapping.
  2449. reg_match = reg_container.joypad_axis.search_all(line);
  2450. for (int i = 0; i < reg_match.size(); ++i) {
  2451. Ref<RegExMatch> match = reg_match[i];
  2452. PackedStringArray strings = match->get_strings();
  2453. const String &axis_entry = strings[0];
  2454. int axis_value = strings[1].to_int();
  2455. if (axis_value == 6) {
  2456. found_renames.append(line_formatter(current_line, axis_entry, ",\"axis\":4", line));
  2457. } else if (axis_value == 7) {
  2458. found_renames.append(line_formatter(current_line, axis_entry, ",\"axis\":5", line));
  2459. }
  2460. }
  2461. current_line++;
  2462. }
  2463. }
  2464. return found_renames;
  2465. }
  2466. Vector<String> ProjectConverter3To4::check_for_rename_input_map_scancode(Vector<String> &lines, const RegExContainer &reg_container) {
  2467. Vector<String> found_renames;
  2468. // The old Special Key, now colliding with CMD_OR_CTRL.
  2469. const int old_spkey = (1 << 24);
  2470. int current_line = 1;
  2471. for (String &line : lines) {
  2472. if (uint64_t(line.length()) <= maximum_line_length) {
  2473. TypedArray<RegExMatch> reg_match = reg_container.input_map_keycode.search_all(line);
  2474. for (int i = 0; i < reg_match.size(); ++i) {
  2475. Ref<RegExMatch> match = reg_match[i];
  2476. PackedStringArray strings = match->get_strings();
  2477. int key = strings[3].to_int();
  2478. if (key & old_spkey) {
  2479. // Create new key, clearing old Special Key and setting new one.
  2480. key = (key & ~old_spkey) | (int)Key::SPECIAL;
  2481. found_renames.append(line_formatter(current_line, strings[3], String::num_int64(key), line));
  2482. }
  2483. }
  2484. }
  2485. current_line++;
  2486. }
  2487. return found_renames;
  2488. }
  2489. void ProjectConverter3To4::custom_rename(Vector<SourceLine> &source_lines, const String &from, const String &to) {
  2490. RegEx reg = RegEx(String("\\b") + from + "\\b");
  2491. CRASH_COND(!reg.is_valid());
  2492. for (SourceLine &source_line : source_lines) {
  2493. if (source_line.is_comment) {
  2494. continue;
  2495. }
  2496. String &line = source_line.line;
  2497. if (uint64_t(line.length()) <= maximum_line_length) {
  2498. line = reg.sub(line, to, true);
  2499. }
  2500. }
  2501. }
  2502. Vector<String> ProjectConverter3To4::check_for_custom_rename(Vector<String> &lines, const String &from, const String &to) {
  2503. Vector<String> found_renames;
  2504. RegEx reg = RegEx(String("\\b") + from + "\\b");
  2505. CRASH_COND(!reg.is_valid());
  2506. int current_line = 1;
  2507. for (String &line : lines) {
  2508. if (uint64_t(line.length()) <= maximum_line_length) {
  2509. TypedArray<RegExMatch> reg_match = reg.search_all(line);
  2510. if (reg_match.size() > 0) {
  2511. found_renames.append(line_formatter(current_line, from.replace("\\.", "."), to, line)); // Without replacing it will print "\.shader" instead ".shader".
  2512. }
  2513. }
  2514. current_line++;
  2515. }
  2516. return found_renames;
  2517. }
  2518. void ProjectConverter3To4::rename_common(const char *array[][2], LocalVector<RegEx *> &cached_regexes, Vector<SourceLine> &source_lines) {
  2519. for (SourceLine &source_line : source_lines) {
  2520. if (source_line.is_comment) {
  2521. continue;
  2522. }
  2523. String &line = source_line.line;
  2524. if (uint64_t(line.length()) <= maximum_line_length) {
  2525. for (unsigned int current_index = 0; current_index < cached_regexes.size(); current_index++) {
  2526. if (line.contains(array[current_index][0])) {
  2527. line = cached_regexes[current_index]->sub(line, array[current_index][1], true);
  2528. }
  2529. }
  2530. }
  2531. }
  2532. }
  2533. Vector<String> ProjectConverter3To4::check_for_rename_common(const char *array[][2], LocalVector<RegEx *> &cached_regexes, Vector<String> &lines) {
  2534. Vector<String> found_renames;
  2535. int current_line = 1;
  2536. for (String &line : lines) {
  2537. if (uint64_t(line.length()) <= maximum_line_length) {
  2538. for (unsigned int current_index = 0; current_index < cached_regexes.size(); current_index++) {
  2539. if (line.contains(array[current_index][0])) {
  2540. TypedArray<RegExMatch> reg_match = cached_regexes[current_index]->search_all(line);
  2541. if (reg_match.size() > 0) {
  2542. found_renames.append(line_formatter(current_line, array[current_index][0], array[current_index][1], line));
  2543. }
  2544. }
  2545. }
  2546. }
  2547. current_line++;
  2548. }
  2549. return found_renames;
  2550. }
  2551. // Prints full info about renamed things e.g.:
  2552. // Line (67) remove -> remove_at - LINE """ doubler._blacklist.remove(0) """
  2553. String ProjectConverter3To4::line_formatter(int current_line, String from, String to, String line) {
  2554. if (from.size() > 200) {
  2555. from = from.substr(0, 197) + "...";
  2556. }
  2557. if (to.size() > 200) {
  2558. to = to.substr(0, 197) + "...";
  2559. }
  2560. if (line.size() > 400) {
  2561. line = line.substr(0, 397) + "...";
  2562. }
  2563. from = from.strip_escapes();
  2564. to = to.strip_escapes();
  2565. line = line.replace("\r", "").replace("\n", "").strip_edges();
  2566. return vformat("Line(%d), %s -> %s - LINE \"\"\" %s \"\"\"", current_line, from, to, line);
  2567. }
  2568. // Prints only full lines e.g.:
  2569. // Line (1) - FULL LINES - """yield(get_tree().create_timer(3), 'timeout')""" =====> """ await get_tree().create_timer(3).timeout """
  2570. String ProjectConverter3To4::simple_line_formatter(int current_line, String old_line, String new_line) {
  2571. if (old_line.size() > 1000) {
  2572. old_line = old_line.substr(0, 997) + "...";
  2573. }
  2574. if (new_line.size() > 1000) {
  2575. new_line = new_line.substr(0, 997) + "...";
  2576. }
  2577. old_line = old_line.replace("\r", "").replace("\n", "").strip_edges();
  2578. new_line = new_line.replace("\r", "").replace("\n", "").strip_edges();
  2579. return vformat("Line (%d) - FULL LINES - \"\"\" %s \"\"\" =====> \"\"\" %s \"\"\"", current_line, old_line, new_line);
  2580. }
  2581. // Collects string from vector strings
  2582. String ProjectConverter3To4::collect_string_from_vector(Vector<SourceLine> &vector) {
  2583. String string = "";
  2584. for (int i = 0; i < vector.size(); i++) {
  2585. string += vector[i].line;
  2586. if (i != vector.size() - 1) {
  2587. string += "\n";
  2588. }
  2589. }
  2590. return string;
  2591. }
  2592. #endif // MODULE_REGEX_ENABLED
  2593. #endif // DISABLE_DEPRECATED