project_converter_3_to_4.cpp 137 KB

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