From c0bf5255510ed5311659ce0c4fcc403c35647c45 Mon Sep 17 00:00:00 2001 From: VladiStep Date: Sat, 6 Jan 2024 20:38:51 +0300 Subject: [PATCH 01/10] Fix #1612. --- UndertaleModLib/Compiler/BuiltinList.cs | 5 ++++- UndertaleModLib/Compiler/Compiler.cs | 2 +- UndertaleModLib/Compiler/Parser.cs | 4 ++++ UndertaleModLib/Models/UndertaleGeneralInfo.cs | 4 ++++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/UndertaleModLib/Compiler/BuiltinList.cs b/UndertaleModLib/Compiler/BuiltinList.cs index 5f02d39ba..7330949a5 100644 --- a/UndertaleModLib/Compiler/BuiltinList.cs +++ b/UndertaleModLib/Compiler/BuiltinList.cs @@ -1177,7 +1177,10 @@ public void Initialize(UndertaleData data) Functions["angle_difference"] = new FunctionInfo(this, 2); Functions["real"] = new FunctionInfo(this, 1); Functions["bool"] = new FunctionInfo(this, 1); - Functions["string"] = new FunctionInfo(this, 1); + if (data?.IsVersionAtLeast(2022, 11) == true) + Functions["string"] = new FunctionInfo(this, -1); + else + Functions["string"] = new FunctionInfo(this, 1); Functions["int64"] = new FunctionInfo(this, 1); Functions["ptr"] = new FunctionInfo(this, 1); Functions["string_format"] = new FunctionInfo(this, 3); diff --git a/UndertaleModLib/Compiler/Compiler.cs b/UndertaleModLib/Compiler/Compiler.cs index 3fa2ae6b8..3a8f4c6ed 100644 --- a/UndertaleModLib/Compiler/Compiler.cs +++ b/UndertaleModLib/Compiler/Compiler.cs @@ -17,7 +17,7 @@ public class CompileContext public Dictionary userDefinedVariables = new Dictionary(); public bool ensureFunctionsDefined = true; public bool ensureVariablesDefined = true; - public static bool GMS2_3; + public static bool GMS2_3, GM2022_11; public bool TypedAssetRefs => Data.IsVersionAtLeast(2023, 8); public int LastCompiledArgumentCount = 0; public Dictionary LocalVars = new Dictionary(); diff --git a/UndertaleModLib/Compiler/Parser.cs b/UndertaleModLib/Compiler/Parser.cs index 9638cc1de..4211ffdcc 100644 --- a/UndertaleModLib/Compiler/Parser.cs +++ b/UndertaleModLib/Compiler/Parser.cs @@ -1717,6 +1717,10 @@ public static Statement Optimize(CompileContext context, Statement s) // Ignore the optimization for GMS build versions less than 1763 and not equal to 1539. if ((context.Data?.GeneralInfo.Build >= 1763) || (context.Data?.GeneralInfo.Major >= 2) || (context.Data?.GeneralInfo.Build == 1539)) { + // If it's `string("test {0}", "format")` + if (CompileContext.GM2022_11 && result.Children.Count > 1) + return result; + string conversion; switch (child0.Constant.kind) { diff --git a/UndertaleModLib/Models/UndertaleGeneralInfo.cs b/UndertaleModLib/Models/UndertaleGeneralInfo.cs index 770657f1f..6758dff07 100644 --- a/UndertaleModLib/Models/UndertaleGeneralInfo.cs +++ b/UndertaleModLib/Models/UndertaleGeneralInfo.cs @@ -336,6 +336,10 @@ public static (uint, uint, uint, uint) TestForCommonGMSVersions(UndertaleReader CompileContext.GMS2_3 = false; DecompileContext.GMS2_3 = false; } + if (detectedVer.Major > 2022 || (detectedVer.Major == 2022 && detectedVer.Minor >= 11)) + CompileContext.GM2022_11 = true; + else + CompileContext.GM2022_11 = false; return detectedVer; } From 55a807ca663ff38bfc7cf03b94eac9065d04edbc Mon Sep 17 00:00:00 2001 From: VladiStep Date: Sat, 6 Jan 2024 20:43:05 +0300 Subject: [PATCH 02/10] Remove the code that often causes wrong argument types. --- .../Decompiler/AssetTypeResolver.cs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/UndertaleModLib/Decompiler/AssetTypeResolver.cs b/UndertaleModLib/Decompiler/AssetTypeResolver.cs index e386b21a1..a29c491ff 100644 --- a/UndertaleModLib/Decompiler/AssetTypeResolver.cs +++ b/UndertaleModLib/Decompiler/AssetTypeResolver.cs @@ -406,25 +406,6 @@ internal static bool AnnotateTypesForFunctionCall(string function_name, AssetIDT } } - if (overloaded) - { - // Copy the array to make sure we don't overwrite existing known types - func_types = (AssetIDType[]) func_types.Clone(); - AssetIDType scriptArgType; - - for (int i = 0; i < arguments.Length && i < func_types.Length && i < scriptArgs[function_name].Length; i++) - { - scriptArgType = scriptArgs[function_name][i]; - - // Merge types together - if (func_types[i] == AssetIDType.Other && scriptArgType != AssetIDType.Other) - func_types[i] = scriptArgType; - // Conflicting types - do not resolve - else if (func_types[i] != AssetIDType.Other && scriptArgType != AssetIDType.Other && func_types[i] != scriptArgType) - func_types[i] = AssetIDType.Other; - // func_types[i] is correct, do not replace - } - } for (int i = 0; i < arguments.Length && i < func_types.Length; i++) arguments[i] = func_types[i]; return true; From 21088d85c2578ec3b2cc0ff59b86a9fe2e57a009 Mon Sep 17 00:00:00 2001 From: VladiStep Date: Sun, 7 Jan 2024 00:27:26 +0300 Subject: [PATCH 03/10] Remove duplicate/redundant built-in functions for Deltarune. --- .../Decompiler/AssetTypeResolver.cs | 101 ------------------ 1 file changed, 101 deletions(-) diff --git a/UndertaleModLib/Decompiler/AssetTypeResolver.cs b/UndertaleModLib/Decompiler/AssetTypeResolver.cs index a29c491ff..4321cf191 100644 --- a/UndertaleModLib/Decompiler/AssetTypeResolver.cs +++ b/UndertaleModLib/Decompiler/AssetTypeResolver.cs @@ -1248,110 +1248,13 @@ public static void InitializeTypes(UndertaleData data) // Deltarune Chapter 2 asset resolutions: // Seems to be x, y, measure of distance (maybe) - builtin_funcs["gml_Script_c_soundplay_wait"] = new[] { AssetIDType.Sound }; - builtin_funcs["gml_Script_snd_pitch_time"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_draw_sprite_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; builtin_funcs["c_soundplay_wait"] = new[] { AssetIDType.Sound }; builtin_funcs["snd_pitch_time"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other }; builtin_funcs["draw_sprite_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script__background_set"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite }; - builtin_funcs["gml_Script_c_addxy"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color }; - builtin_funcs["gml_Script_c_autowalk"] = new[] { AssetIDType.Boolean }; - builtin_funcs["gml_Script_c_fadeout"] = new[] { AssetIDType.Other }; - builtin_funcs["gml_Script_c_pan"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_c_pannable"] = new[] { AssetIDType.Boolean }; - builtin_funcs["gml_Script_c_panobj"] = new[] { AssetIDType.GameObject, AssetIDType.Other }; - builtin_funcs["gml_Script_c_panspeed"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_c_script_instance"] = new[] { AssetIDType.GameObject, AssetIDType.Script, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_c_script_instance_stop"] = new[] { AssetIDType.GameObject, AssetIDType.Script }; - builtin_funcs["gml_Script_c_setxy"] = new[] { AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_c_soundplay"] = new[] { AssetIDType.Sound }; - builtin_funcs["gml_Script_c_soundplay_x"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_c_sprite"] = new[] { AssetIDType.Sprite }; - builtin_funcs["gml_Script_c_stickto"] = new[] { AssetIDType.GameObject, AssetIDType.Other }; - builtin_funcs["gml_Script_c_wait"] = new[] { AssetIDType.Other }; - builtin_funcs["gml_Script_c_walkdirect"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_c_walkdirect_wait"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_d3d_set_fog_ch1"] = new[] { AssetIDType.Boolean, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_draw_background_ext_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other }; - builtin_funcs["gml_Script_draw_background_part_ext_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other }; - builtin_funcs["gml_Script_draw_background_tiled_ext_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other }; - builtin_funcs["gml_Script_draw_enable_alphablend_ch1"] = new[] { AssetIDType.Boolean }; - builtin_funcs["gml_Script_draw_enable_alphablend"] = new[] { AssetIDType.Boolean }; builtin_funcs["draw_enable_alphablend"] = new[] { AssetIDType.Boolean }; - builtin_funcs["gml_Script_draw_monster_body_part"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_draw_monster_body_part_ext"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other }; - builtin_funcs["gml_Script_draw_sprite_ext_centerscale"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other }; - builtin_funcs["gml_Script_draw_sprite_ext_flash"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other }; - builtin_funcs["gml_Script_draw_sprite_skew_ext_cute"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_draw_text_outline"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_i_ex"] = new[] { AssetIDType.GameObject }; - builtin_funcs["gml_Script_instance_create_ch1"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject }; - builtin_funcs["gml_Script_msgsetloc"] = new[] { AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_mus_loop"] = new[] { AssetIDType.Sound }; - builtin_funcs["gml_Script_mus_loop_ext"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_safe_delete"] = new[] { AssetIDType.GameObject }; - builtin_funcs["gml_Script_scr_84_debug"] = new[] { AssetIDType.Boolean }; - builtin_funcs["gml_Script_scr_act_charsprite"] = new[] { AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Boolean }; - builtin_funcs["gml_Script_scr_anim"] = new[] { AssetIDType.Sprite, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_anim_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_battle"] = new[] { AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_battle_marker"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite }; - builtin_funcs["gml_Script_scr_bullet_create"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject }; - builtin_funcs["gml_Script_scr_bulletspawner"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject }; - builtin_funcs["gml_Script_scr_caterpillar_facing_ch1"] = new[] { AssetIDType.Other }; - builtin_funcs["gml_Script_scr_custom_afterimage"] = new[] { AssetIDType.Sprite }; - builtin_funcs["gml_Script_scr_custom_afterimage_ext"] = new[] { AssetIDType.GameObject, AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_dark_marker"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite }; - builtin_funcs["gml_Script_scr_dark_marker_ch1"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite }; - builtin_funcs["gml_Script_scr_dark_marker_depth"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite }; - builtin_funcs["gml_Script_scr_debug_keycheck"] = new[] { AssetIDType.KeyboardKey }; - builtin_funcs["gml_Script_scr_draw_background_ps4_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_draw_outline_ext"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_draw_sprite_crop_ext"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_ds_list_write"] = new[] { AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_enemyblcon"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_following_afterimage"] = new[] { AssetIDType.GameObject, AssetIDType.GameObject }; - builtin_funcs["gml_Script_scr_forcefield"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Boolean }; - builtin_funcs["gml_Script_scr_fx_housesquare"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Color }; - builtin_funcs["gml_Script_scr_guardpeek"] = new[] { AssetIDType.GameObject }; - builtin_funcs["gml_Script_scr_marker"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite }; - builtin_funcs["gml_Script_scr_marker_ch1"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite }; - builtin_funcs["gml_Script_scr_mercyadd"] = new[] { AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_monster_add"] = new[] { AssetIDType.Other, AssetIDType.GameObject }; - builtin_funcs["gml_Script_scr_monster_change"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject }; - builtin_funcs["gml_Script_scr_move_to_point_over_time"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_pan_ch1"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_pan_to_obj"] = new[] { AssetIDType.GameObject, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_pan_to_obj_ch1"] = new[] { AssetIDType.GameObject, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_script_delayed"] = new[] { AssetIDType.Script, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_textsetup"] = new[] { AssetIDType.Font, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_textsetup_ch1"] = new[] { AssetIDType.Font, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_snd_is_playing"] = new[] { AssetIDType.Sound }; - builtin_funcs["gml_Script_snd_loop"] = new[] { AssetIDType.Sound }; - builtin_funcs["gml_Script_snd_loop_ch1"] = new[] { AssetIDType.Sound }; - builtin_funcs["gml_Script_snd_pitch"] = new[] { AssetIDType.Sound, AssetIDType.Other }; - builtin_funcs["gml_Script_snd_play"] = new[] { AssetIDType.Sound }; - builtin_funcs["gml_Script_snd_play_ch1"] = new[] { AssetIDType.Sound }; - builtin_funcs["gml_Script_snd_play_pitch"] = new[] { AssetIDType.Sound, AssetIDType.Other }; - builtin_funcs["gml_Script_snd_play_x"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_snd_stop"] = new[] { AssetIDType.Sound }; - builtin_funcs["gml_Script_snd_stop_ch1"] = new[] { AssetIDType.Sound }; - builtin_funcs["gml_Script_snd_volume"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_act_charsprite"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean }; - builtin_funcs["gml_Script_draw_sprite_part_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_draw_sprite_part_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; builtin_funcs["draw_sprite_part_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["scr_draw_sprite_part_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_draw_sprite_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["draw_sprite_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_draw_sprite_tiled_area"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Boolean }; - builtin_funcs["gml_Script_c_actorsetsprites"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Boolean }; - builtin_funcs["gml_Script_scr_marker_animated"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Other }; builtin_funcs["scr_marker_animated"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Other }; - builtin_funcs["gml_Script_c_jump_sprite"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Sprite }; - builtin_funcs["gml_Script_scr_dark_marker_animated"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Boolean }; builtin_funcs["scr_act_charsprite"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean }; builtin_funcs["scr_draw_sprite_tiled_area"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Boolean }; builtin_funcs["c_actorsetsprites"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Boolean }; @@ -1391,15 +1294,11 @@ public static void InitializeTypes(UndertaleData data) builtin_funcs["msgsetloc"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; builtin_funcs["mus_loop"] = new[] { AssetIDType.Sound }; builtin_funcs["mus_loop_ext"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["gml_Script_scr_bullet_inherit_ch1"] = new[] { AssetIDType.GameObject }; builtin_funcs["scr_bullet_inherit_ch1"] = new[] { AssetIDType.GameObject }; builtin_funcs["safe_delete"] = new[] { AssetIDType.GameObject }; builtin_funcs["scr_84_debug"] = new[] { AssetIDType.Boolean }; - builtin_funcs["gml_Script_texture_set_interpolation"] = new[] { AssetIDType.Boolean }; builtin_funcs["texture_set_interpolation"] = new[] { AssetIDType.Boolean }; - builtin_funcs["gml_Script_texture_set_interpolation_ch1"] = new[] { AssetIDType.Boolean }; builtin_funcs["texture_set_interpolation_ch1"] = new[] { AssetIDType.Boolean }; - builtin_funcs["scr_act_charsprite"] = new[] { AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Boolean }; builtin_funcs["scr_anim"] = new[] { AssetIDType.Sprite, AssetIDType.Other }; builtin_funcs["scr_anim_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other }; builtin_funcs["scr_battle"] = new[] { AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; From 3fe2bf72b2954230d447baa699626af30ef03c28 Mon Sep 17 00:00:00 2001 From: VladiStep Date: Sun, 7 Jan 2024 19:14:30 +0300 Subject: [PATCH 04/10] Fix some Deltarune functions arguments types. --- .../Decompiler/AssetTypeResolver.cs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/UndertaleModLib/Decompiler/AssetTypeResolver.cs b/UndertaleModLib/Decompiler/AssetTypeResolver.cs index 4321cf191..fd1bb7393 100644 --- a/UndertaleModLib/Decompiler/AssetTypeResolver.cs +++ b/UndertaleModLib/Decompiler/AssetTypeResolver.cs @@ -1250,16 +1250,15 @@ public static void InitializeTypes(UndertaleData data) builtin_funcs["c_soundplay_wait"] = new[] { AssetIDType.Sound }; builtin_funcs["snd_pitch_time"] = new[] { AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["draw_sprite_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - + builtin_funcs["draw_sprite_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Color }; builtin_funcs["draw_enable_alphablend"] = new[] { AssetIDType.Boolean }; - builtin_funcs["draw_sprite_part_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; + builtin_funcs["draw_sprite_part_ext_glow"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Color }; builtin_funcs["scr_marker_animated"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Other }; - builtin_funcs["scr_act_charsprite"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Boolean }; - builtin_funcs["scr_draw_sprite_tiled_area"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Boolean }; - builtin_funcs["c_actorsetsprites"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Boolean }; + builtin_funcs["scr_act_charsprite"] = new[] { AssetIDType.Other /* string */, AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Boolean }; + builtin_funcs["scr_draw_sprite_tiled_area"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other }; + builtin_funcs["c_actorsetsprites"] = new[] { AssetIDType.GameObject, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Boolean }; builtin_funcs["c_jump_sprite"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Sprite }; - builtin_funcs["scr_dark_marker_animated"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Boolean }; + builtin_funcs["scr_dark_marker_animated"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite, AssetIDType.Other }; builtin_funcs["_background_set"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite }; builtin_funcs["c_addxy"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color }; builtin_funcs["c_autowalk"] = new[] { AssetIDType.Boolean }; @@ -1268,7 +1267,7 @@ public static void InitializeTypes(UndertaleData data) builtin_funcs["c_pannable"] = new[] { AssetIDType.Boolean }; builtin_funcs["c_panobj"] = new[] { AssetIDType.GameObject, AssetIDType.Other }; builtin_funcs["c_panspeed"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["c_script_instance"] = new[] { AssetIDType.GameObject, AssetIDType.Script, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; + builtin_funcs["c_script_instance"] = new[] { AssetIDType.GameObject, AssetIDType.Script, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; // TODO: it's actually dynamic starting from the third argument builtin_funcs["c_script_instance_stop"] = new[] { AssetIDType.GameObject, AssetIDType.Script }; builtin_funcs["c_setxy"] = new[] { AssetIDType.Other, AssetIDType.Other }; builtin_funcs["c_soundplay"] = new[] { AssetIDType.Sound }; @@ -1288,7 +1287,7 @@ public static void InitializeTypes(UndertaleData data) builtin_funcs["draw_sprite_ext_centerscale"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other }; builtin_funcs["draw_sprite_ext_flash"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other }; builtin_funcs["draw_sprite_skew_ext_cute"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["draw_text_outline"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; + builtin_funcs["draw_text_outline"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other /* string */, AssetIDType.Color }; builtin_funcs["i_ex"] = new[] { AssetIDType.GameObject }; builtin_funcs["instance_create_ch1"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject }; builtin_funcs["msgsetloc"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; @@ -1306,7 +1305,7 @@ public static void InitializeTypes(UndertaleData data) builtin_funcs["scr_bullet_create"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject }; builtin_funcs["scr_bulletspawner"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject }; builtin_funcs["scr_caterpillar_facing_ch1"] = new[] { AssetIDType.Other }; - builtin_funcs["scr_custom_afterimage"] = new[] { AssetIDType.Sprite }; + builtin_funcs["scr_custom_afterimage"] = new[] { AssetIDType.GameObject }; builtin_funcs["scr_custom_afterimage_ext"] = new[] { AssetIDType.GameObject, AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; builtin_funcs["scr_dark_marker"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite }; builtin_funcs["scr_dark_marker_ch1"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite }; @@ -1314,7 +1313,7 @@ public static void InitializeTypes(UndertaleData data) builtin_funcs["scr_debug_keycheck"] = new[] { AssetIDType.KeyboardKey }; builtin_funcs["scr_draw_background_ps4_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other }; builtin_funcs["scr_draw_outline_ext"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other }; - builtin_funcs["scr_draw_sprite_crop_ext"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; + builtin_funcs["scr_draw_sprite_crop_ext"] = new[] { AssetIDType.Sprite, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Color, AssetIDType.Other }; builtin_funcs["scr_ds_list_write"] = new[] { AssetIDType.Other, AssetIDType.Other }; builtin_funcs["scr_enemyblcon"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; builtin_funcs["scr_following_afterimage"] = new[] { AssetIDType.GameObject, AssetIDType.GameObject }; @@ -1330,7 +1329,7 @@ public static void InitializeTypes(UndertaleData data) builtin_funcs["scr_pan_ch1"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; builtin_funcs["scr_pan_to_obj"] = new[] { AssetIDType.GameObject, AssetIDType.Other }; builtin_funcs["scr_pan_to_obj_ch1"] = new[] { AssetIDType.GameObject, AssetIDType.Other }; - builtin_funcs["scr_script_delayed"] = new[] { AssetIDType.Script, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; + builtin_funcs["scr_script_delayed"] = new[] { AssetIDType.Script, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; // TODO: it's actually dynamic starting from the third argument builtin_funcs["scr_textsetup"] = new[] { AssetIDType.Font, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; builtin_funcs["scr_textsetup_ch1"] = new[] { AssetIDType.Font, AssetIDType.Color, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other, AssetIDType.Sound, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; builtin_funcs["snd_is_playing"] = new[] { AssetIDType.Sound }; From f8f6fc769fc2fdcd5f68053f137e328d17be21bd Mon Sep 17 00:00:00 2001 From: VladiStep Date: Mon, 8 Jan 2024 00:31:12 +0300 Subject: [PATCH 05/10] Improve `AnnotateTypesForFunctionCall()` code. --- .../Decompiler/AssetTypeResolver.cs | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/UndertaleModLib/Decompiler/AssetTypeResolver.cs b/UndertaleModLib/Decompiler/AssetTypeResolver.cs index fd1bb7393..ed3b4d96e 100644 --- a/UndertaleModLib/Decompiler/AssetTypeResolver.cs +++ b/UndertaleModLib/Decompiler/AssetTypeResolver.cs @@ -372,17 +372,19 @@ internal static bool AnnotateTypesForFunctionCall(string function_name, AssetIDT Dictionary scriptArgs = context.GlobalContext.ScriptArgsCache; bool overloaded = false; + AssetIDType[] funcArgs; // Scripts overload builtins because in GMS2 some functions are just backwards-compatibility scripts - if (scriptArgs.ContainsKey(function_name) && scriptArgs[function_name] != null) + if (scriptArgs.TryGetValue(function_name, out funcArgs) && funcArgs is not null) { overloaded = true; - for (int i = 0; i < arguments.Length && i < scriptArgs[function_name].Length; i++) - arguments[i] = scriptArgs[function_name][i]; + for (int i = 0; i < arguments.Length && i < funcArgs.Length; i++) + arguments[i] = funcArgs[i]; } function_name = function_name.Replace("color", "colour", StringComparison.InvariantCulture); // Just GameMaker things... both are valid :o - if(context.GlobalContext.Data?.IsGameMaker2() ?? false) + bool isGMS2 = context.GlobalContext.Data?.IsGameMaker2() ?? false; + if (isGMS2) { // Backgrounds don't exist in GMS2 for (int i = 0; i < arguments.Length; i++) @@ -392,29 +394,30 @@ internal static bool AnnotateTypesForFunctionCall(string function_name, AssetIDT } } - if (builtin_funcs.ContainsKey(function_name)) + if (builtin_funcs.TryGetValue(function_name, out var funcTypes)) { - AssetIDType[] func_types = builtin_funcs[function_name]; - - if (context.GlobalContext.Data?.IsGameMaker2() ?? false) + if (isGMS2) { // Backgrounds don't exist in GMS2 - for (int i = 0; i < func_types.Length; i++) + for (int i = 0; i < funcTypes.Length; i++) { - if (func_types[i] == AssetIDType.Background) - func_types[i] = AssetIDType.Sprite; + if (funcTypes[i] == AssetIDType.Background) + funcTypes[i] = AssetIDType.Sprite; } } - for (int i = 0; i < arguments.Length && i < func_types.Length; i++) - arguments[i] = func_types[i]; + for (int i = 0; i < arguments.Length && i < funcTypes.Length; i++) + arguments[i] = funcTypes[i]; + return true; } + if (function_name == "script_execute") { // This needs a special case if (arguments.Length < 1) throw new Exception("Bad call to " + function_name + " with " + arguments.Length + " arguments (instead of at least 1)"); + arguments[0] = AssetIDType.Script; // Attempt to resolve the arguments of the script being called. @@ -433,7 +436,7 @@ internal static bool AnnotateTypesForFunctionCall(string function_name, AssetIDT if (script_id >= 0 && script_id < context.GlobalContext.Data.Scripts.Count) { var script = context.GlobalContext.Data.Scripts[script_id]; - AssetIDType[] args = new AssetIDType[arguments.Length-1]; + AssetIDType[] args = new AssetIDType[arguments.Length - 1]; AnnotateTypesForFunctionCall(script.Name.Content, args, context); Array.Copy(args, 0, arguments, 1, args.Length); return true; @@ -441,13 +444,16 @@ internal static bool AnnotateTypesForFunctionCall(string function_name, AssetIDT } } } - if (scriptArgs.ContainsKey(function_name) && scriptArgs[function_name] != null) + + if (funcArgs is not null) { - for (int i = 0; i < arguments.Length && i < scriptArgs[function_name].Length; i++) - arguments[1 + i] = scriptArgs[function_name][i]; + for (int i = 0; i < arguments.Length && i < funcArgs.Length; i++) + arguments[1 + i] = funcArgs[i]; } + return true; } + return overloaded; } From 740f1dfcecda4f98dac9536398d3985f04df7174 Mon Sep 17 00:00:00 2001 From: VladiStep Date: Mon, 8 Jan 2024 01:59:15 +0300 Subject: [PATCH 06/10] Various optimizations for `AssetTypeResolver` and `Decompiler`. --- .../Decompiler/AssetTypeResolver.cs | 73 ++++++++++--------- UndertaleModLib/Decompiler/Decompiler.cs | 68 +++++++++-------- 2 files changed, 74 insertions(+), 67 deletions(-) diff --git a/UndertaleModLib/Decompiler/AssetTypeResolver.cs b/UndertaleModLib/Decompiler/AssetTypeResolver.cs index ed3b4d96e..7a9e4d563 100644 --- a/UndertaleModLib/Decompiler/AssetTypeResolver.cs +++ b/UndertaleModLib/Decompiler/AssetTypeResolver.cs @@ -459,27 +459,29 @@ internal static bool AnnotateTypesForFunctionCall(string function_name, AssetIDT internal static AssetIDType AnnotateTypeForVariable(DecompileContext context, string variable_name) { + AssetIDType type; var overrides = GetTypeOverridesFor(context.TargetCode.Name.Content); - if (overrides.ContainsKey(variable_name)) - return overrides[variable_name]; + if (overrides.TryGetValue(variable_name, out type)) + return type; if (context.Object != null) { overrides = GetTypeOverridesFor(context.Object.Name.Content); - if (overrides.ContainsKey(variable_name)) - return overrides[variable_name]; + if (overrides.TryGetValue(variable_name, out type)) + return type; } + if (builtin_vars.TryGetValue(variable_name, out type)) + return type; - if (builtin_vars.ContainsKey(variable_name)) - return builtin_vars[variable_name]; return AssetIDType.Other; } internal static AssetIDType AnnotateTypeForScript(string script_name) { - if (return_types.ContainsKey(script_name)) - return return_types[script_name]; + if (return_types.TryGetValue(script_name, out var type)) + return type; + return AssetIDType.Other; } @@ -490,12 +492,13 @@ internal static Dictionary GetTypeOverridesFor(DecompileCon internal static Dictionary GetTypeOverridesFor(string code_entry_name) { - lock(builtin_var_overrides) + lock (builtin_var_overrides) { - if (!builtin_var_overrides.ContainsKey(code_entry_name)) - builtin_var_overrides.Add(code_entry_name, new Dictionary()); + Dictionary varOverride; + if (!builtin_var_overrides.TryGetValue(code_entry_name, out varOverride)) + builtin_var_overrides.Add(code_entry_name, varOverride = new()); - return builtin_var_overrides[code_entry_name]; + return varOverride; } } @@ -512,29 +515,29 @@ internal static void AddOverrideFor(string code_entry_name, string variable_name if (const_name.Length >= 1 && const_name[0] == '-') return null; // that is not a constant either - // By avoiding Enum.TryParse, we avoid exception spam in the console, and there isn't any speed loss. - if (Enum.IsDefined(typeof(OSType), const_name)) - return (int)Enum.Parse(typeof(OSType), const_name); - if (Enum.IsDefined(typeof(GamepadButton), const_name)) - return (int)Enum.Parse(typeof(GamepadButton), const_name); - if (Enum.IsDefined(typeof(MouseButton), const_name)) - return (int)Enum.Parse(typeof(MouseButton), const_name); - if (Enum.IsDefined(typeof(MouseCursor), const_name)) - return (int)Enum.Parse(typeof(MouseCursor), const_name); - if (Enum.IsDefined(typeof(HAlign), const_name)) - return (int)Enum.Parse(typeof(HAlign), const_name); - if (Enum.IsDefined(typeof(VAlign), const_name)) - return (int)Enum.Parse(typeof(VAlign), const_name); - if (Enum.IsDefined(typeof(GameSpeed), const_name)) - return (int)Enum.Parse(typeof(GameSpeed), const_name); - if (Enum.IsDefined(typeof(e__VW), const_name)) - return (int)Enum.Parse(typeof(e__VW), const_name); - if (Enum.IsDefined(typeof(e__BG), const_name)) - return (int)Enum.Parse(typeof(e__BG), const_name); - if (Enum.IsDefined(typeof(EventSubtypeKey), const_name)) - return Convert.ToInt32((uint)Enum.Parse(typeof(EventSubtypeKey), const_name)); - if (Enum.IsDefined(typeof(Enum_EventType), const_name)) - return (int)Enum.Parse(typeof(Enum_EventType), const_name); + object res; + if (Enum.TryParse(typeof(OSType), const_name, false, out res)) + return (int)res; + if (Enum.TryParse(typeof(GamepadButton), const_name, false, out res)) + return (int)res; + if (Enum.TryParse(typeof(MouseButton), const_name, false, out res)) + return (int)res; + if (Enum.TryParse(typeof(MouseCursor), const_name, false, out res)) + return (int)res; + if (Enum.TryParse(typeof(HAlign), const_name, false, out res)) + return (int)res; + if (Enum.TryParse(typeof(VAlign), const_name, false, out res)) + return (int)res; + if (Enum.TryParse(typeof(GameSpeed), const_name, false, out res)) + return (int)res; + if (Enum.TryParse(typeof(e__VW), const_name, false, out res)) + return (int)res; + if (Enum.TryParse(typeof(e__BG), const_name, false, out res)) + return (int)res; + if (Enum.TryParse(typeof(EventSubtypeKey), const_name, false, out res)) + return Convert.ToInt32((uint)res); + if (Enum.TryParse(typeof(Enum_EventType), const_name, false, out res)) + return (int)res; return null; } diff --git a/UndertaleModLib/Decompiler/Decompiler.cs b/UndertaleModLib/Decompiler/Decompiler.cs index d6eca4b5f..addfc7725 100644 --- a/UndertaleModLib/Decompiler/Decompiler.cs +++ b/UndertaleModLib/Decompiler/Decompiler.cs @@ -447,7 +447,7 @@ public override string ToString(DecompileContext context) case AssetIDType.ContextDependent: { var func = context.currentFunction; - if (func != null && (ContextualAssetResolver.resolvers?.ContainsKey(func.Function.Name.Content) ?? false)) + if (func != null && (ContextualAssetResolver.resolvers?.TryGetValue(func.Function.Name.Content, out var resolveFunc) ?? false)) { List actualArguments = new List(); foreach (var arg in func.Arguments) @@ -457,7 +457,7 @@ public override string ToString(DecompileContext context) else actualArguments.Add(arg); } - string result = ContextualAssetResolver.resolvers[func.Function.Name.Content](context, func, actualArguments.IndexOf(this), this); + string result = resolveFunc(context, func, actualArguments.IndexOf(this), this); if (result != null) return result; } @@ -473,8 +473,8 @@ public override string ToString(DecompileContext context) else // guaranteed to be an unsigned int. { uint vuint = (uint)vint; - if (Decompiler.ColorDictionary.ContainsKey(vuint)) - return Decompiler.ColorDictionary[vuint]; + if (Decompiler.ColorDictionary.TryGetValue(vuint, out var colorName)) + return colorName; else return (context.GlobalContext.Data?.IsGameMaker2() ?? false ? "0x" : "$") + formattable.ToString("X6", CultureInfo.InvariantCulture); // not a known color and not negative. } @@ -1212,14 +1212,14 @@ public override string ToString(DecompileContext context) { if (Value != null) { - if (AssetTypeResolver.return_types.ContainsKey(context.TargetCode.Name.Content)) - Value.DoTypePropagation(context, AssetTypeResolver.return_types[context.TargetCode.Name.Content]); + if (AssetTypeResolver.return_types.TryGetValue(context.TargetCode.Name.Content, out var type)) + Value.DoTypePropagation(context, type); if (context.GlobalContext.Data != null && !DecompileContext.GMS2_3) { // We might be decompiling a legacy script - resolve it's name UndertaleScript script = context.GlobalContext.Data.Scripts.FirstOrDefault(x => x.Code == context.TargetCode); - if (script != null && AssetTypeResolver.return_types.ContainsKey(script.Name.Content)) - Value.DoTypePropagation(context, AssetTypeResolver.return_types[script.Name.Content]); + if (script != null && AssetTypeResolver.return_types.TryGetValue(script.Name.Content, out type)) + Value.DoTypePropagation(context, type); } string cleanVal = Value.ToString(context); @@ -1494,7 +1494,7 @@ public override Statement CleanStatement(DecompileContext context, BlockHLStatem public override string ToString(DecompileContext context) { StringBuilder sb = new StringBuilder(); - if (context.Statements.ContainsKey(FunctionBodyEntryBlock.Address.Value)) + if (context.Statements.TryGetValue(FunctionBodyEntryBlock.Address.Value, out var statements)) { FunctionDefinition def; var oldDecompilingStruct = context.DecompilingStruct; @@ -1508,7 +1508,7 @@ public override string ToString(DecompileContext context) sb.Append("function"); if (IsStatement) { - sb.Append(" "); + sb.Append(' '); // For further optimization, we could *probably* create a dictionary that's just flipped KVPs (assuming there are no dup. values). // Doing so would save the need for LINQ and what-not. Not that big of an issue, but still an option. @@ -1531,11 +1531,11 @@ public override string ToString(DecompileContext context) gotFuncName = true; } } - if(!gotFuncName) + if (!gotFuncName) sb.Append((context.Statements[0].Last() as AssignmentStatement).Destination.Var.Name.Content); } } - sb.Append("("); + sb.Append('('); for (int i = 0; i < FunctionBodyCodeEntry.ArgumentsCount; ++i) { if (i != 0) @@ -1550,12 +1550,11 @@ public override string ToString(DecompileContext context) sb.Append(Function.Name.Content); } - var statements = context.Statements[FunctionBodyEntryBlock.Address.Value]; int numNotReturn = statements.FindAll(stmt => !(stmt is ReturnStatement)).Count; if (numNotReturn > 0 || Subtype != FunctionType.Struct) { - sb.Append("\n"); + sb.Append('\n'); sb.Append(context.Indentation); sb.Append("{\n"); context.IndentationLevel++; @@ -1583,24 +1582,24 @@ public override string ToString(DecompileContext context) if (def?.Function == Function) { //sb.Append("// Error decompiling function: function contains its own declaration???\n"); - sb.Append("\n"); + sb.Append('\n'); break; } else { sb.Append(stmt.ToString(context)); if (Subtype == FunctionType.Struct && count < numNotReturn) - sb.Append(","); + sb.Append(','); } - sb.Append("\n"); + sb.Append('\n'); } context.DecompilingStruct = oldDecompilingStruct; context.ArgumentReplacements = oldReplacements; context.IndentationLevel--; sb.Append(context.Indentation); - sb.Append("}"); + sb.Append('}'); if(!oldDecompilingStruct) - sb.Append("\n"); + sb.Append('\n'); } else sb.Append("{}"); @@ -1609,6 +1608,7 @@ public override string ToString(DecompileContext context) { sb.Append(Function.Name.Content); } + return sb.ToString(); } @@ -1779,11 +1779,13 @@ internal override AssetIDType DoTypePropagation(DecompileContext context, AssetI Decompiler.DecompileFromBlock(childContext, blocks, blocks[0]); Decompiler.DoTypePropagation(childContext, blocks); // TODO: This should probably put suggestedType through the "return" statement at the other end } - context.GlobalContext.ScriptArgsCache[funcName] = new AssetIDType[15]; + + var typesArr = new AssetIDType[15]; + context.GlobalContext.ScriptArgsCache[funcName] = typesArr; for (int i = 0; i < 15; i++) { var v = childContext.assetTypes.Where((x) => x.Key.Name.Content == "argument" + i); - context.GlobalContext.ScriptArgsCache[funcName][i] = v.Any() ? v.First().Value : AssetIDType.Other; + typesArr[i] = v.Any() ? v.First().Value : AssetIDType.Other; } } catch (Exception e) @@ -1989,7 +1991,9 @@ internal override AssetIDType DoTypePropagation(DecompileContext context, AssetI e?.DoTypePropagation(context, AssetIDType.Other); } - AssetIDType current = context.assetTypes.ContainsKey(Var) ? context.assetTypes[Var] : AssetIDType.Other; + AssetIDType current; + if (!context.assetTypes.TryGetValue(Var, out current)) + current = AssetIDType.Other; if (current == AssetIDType.Other && suggestedType != AssetIDType.Other) current = suggestedType; AssetIDType builtinSuggest = AssetTypeResolver.AnnotateTypeForVariable(context, Var.Name.Content); @@ -2805,16 +2809,16 @@ public static Dictionary DecompileFlowGraph(UndertaleCode code, Lis foreach (var instr in code.Instructions) { - if (blockByAddress.ContainsKey(instr.Address)) + if (blockByAddress.TryGetValue(instr.Address, out var block)) { if (currentBlock != null) { currentBlock.conditionalExit = false; - currentBlock.nextBlockTrue = blockByAddress[instr.Address]; - currentBlock.nextBlockFalse = blockByAddress[instr.Address]; - blockByAddress[instr.Address].entryPoints.Add(currentBlock); + currentBlock.nextBlockTrue = block; + currentBlock.nextBlockFalse = block; + block.entryPoints.Add(currentBlock); } - currentBlock = blockByAddress[instr.Address]; + currentBlock = block; } if (currentBlock == null) @@ -3670,9 +3674,10 @@ private static BlockHLStatement HLDecompileBlocks(DecompileContext context, ref caseExpr = cmp.Argument2; } - if (!caseEntries.ContainsKey(block.nextBlockTrue)) - caseEntries.Add(block.nextBlockTrue, new List()); - caseEntries[block.nextBlockTrue].Add(caseExpr); + List list; + if (!caseEntries.TryGetValue(block.nextBlockTrue, out list)) + caseEntries.Add(block.nextBlockTrue, list = new()); + list.Add(caseExpr); if (!block.conditionalExit) { @@ -3726,9 +3731,8 @@ private static BlockHLStatement HLDecompileBlocks(DecompileContext context, ref { // This is the default-case meet-point if it is b. uint instructionId = ((uint)block.Address + 1 + (uint)breakInstruction.JumpOffset); - if (!blocks.ContainsKey(instructionId)) + if (!blocks.TryGetValue(instructionId, out var switchEnd)) Debug.Fail("Switch statement default: Bad target [" + block.Address + ", " + breakInstruction.JumpOffset + "]: " + breakInstruction.ToString()); - Block switchEnd = blocks[instructionId]; Block start = meetPoint; defaultCase.Block = HLDecompileBlocks(context, ref start, blocks, loops, reverseDominators, alreadyVisited, currentLoop, switchEnd, switchEnd, false, depth + 1); From 7177e2073a50b42e095abbe30eae62d12c247bc5 Mon Sep 17 00:00:00 2001 From: VladiStep Date: Mon, 8 Jan 2024 16:18:40 +0300 Subject: [PATCH 07/10] Fix type arguments for "scr_battle()". --- UndertaleModLib/Decompiler/AssetTypeResolver.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/UndertaleModLib/Decompiler/AssetTypeResolver.cs b/UndertaleModLib/Decompiler/AssetTypeResolver.cs index 7a9e4d563..2648c653a 100644 --- a/UndertaleModLib/Decompiler/AssetTypeResolver.cs +++ b/UndertaleModLib/Decompiler/AssetTypeResolver.cs @@ -545,7 +545,6 @@ internal static void AddOverrideFor(string code_entry_name, string variable_name // Properly initializes per-project/game public static void InitializeTypes(UndertaleData data) { - ContextualAssetResolver.Initialize(data); return_types = new Dictionary(); @@ -1309,7 +1308,7 @@ public static void InitializeTypes(UndertaleData data) builtin_funcs["texture_set_interpolation_ch1"] = new[] { AssetIDType.Boolean }; builtin_funcs["scr_anim"] = new[] { AssetIDType.Sprite, AssetIDType.Other }; builtin_funcs["scr_anim_ch1"] = new[] { AssetIDType.Sprite, AssetIDType.Other }; - builtin_funcs["scr_battle"] = new[] { AssetIDType.Other, AssetIDType.Boolean, AssetIDType.Other, AssetIDType.Other, AssetIDType.Other }; + builtin_funcs["scr_battle"] = new[] { AssetIDType.Other, AssetIDType.Boolean, AssetIDType.GameObject, AssetIDType.GameObject, AssetIDType.GameObject }; builtin_funcs["scr_battle_marker"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.Sprite }; builtin_funcs["scr_bullet_create"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject }; builtin_funcs["scr_bulletspawner"] = new[] { AssetIDType.Other, AssetIDType.Other, AssetIDType.GameObject }; From 0e94511a9bfb461fd93710154eb8a465af4b7a5b Mon Sep 17 00:00:00 2001 From: VladiStep Date: Mon, 8 Jan 2024 17:16:01 +0300 Subject: [PATCH 08/10] Dictionary-related optimizations for `ContextualAssetResolver`. --- .../Decompiler/ContextualAssetResolver.cs | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/UndertaleModLib/Decompiler/ContextualAssetResolver.cs b/UndertaleModLib/Decompiler/ContextualAssetResolver.cs index 91954f59d..048e09370 100644 --- a/UndertaleModLib/Decompiler/ContextualAssetResolver.cs +++ b/UndertaleModLib/Decompiler/ContextualAssetResolver.cs @@ -81,10 +81,11 @@ public static void Initialize(UndertaleData data) if (type == Enum_EventType.ev_keypress || type == Enum_EventType.ev_keyrelease) type = Enum_EventType.ev_keyboard; - if (!event_subtypes.ContainsKey(type)) - event_subtypes[type] = new Dictionary(); + Dictionary subtypes; + if (!event_subtypes.TryGetValue(type, out subtypes)) + event_subtypes[type] = (subtypes = new Dictionary()); - return event_subtypes[type]; + return subtypes; }; // This is going to get bulky really quickly @@ -145,11 +146,10 @@ public static void Initialize(UndertaleData data) if (key != null) return key; } - else if (subtypes.ContainsKey(type)) + else if (subtypes.TryGetValue(type, out var mappings)) { - var mappings = subtypes[type]; - if (mappings.ContainsKey(val)) - return mappings[val]; + if (mappings.TryGetValue(val, out var str)) + return str; } } @@ -209,7 +209,10 @@ public static void Initialize(UndertaleData data) if (val == null) return null; - return blend_modes.ContainsKey(val.Value) ? blend_modes[val.Value] : null; + if (blend_modes.TryGetValue(val.Value, out var mode)) + return mode; + + return null; } }, { "gpu_set_blendmode_ext", (context, func, index, self) => @@ -218,7 +221,10 @@ public static void Initialize(UndertaleData data) if (val == null) return null; - return blend_modes.ContainsKey(val.Value) ? blend_modes[val.Value] : null; + if (blend_modes.TryGetValue(val.Value, out var mode)) + return mode; + + return null; } }, { "__view_set", (context, func, index, self) => @@ -252,6 +258,7 @@ public static void Initialize(UndertaleData data) return "true"; } break; } + return null; } }, From 87c7e088c9923ef496ef6f32d12c5556f3b8b7e9 Mon Sep 17 00:00:00 2001 From: VladiStep Date: Tue, 9 Jan 2024 16:10:51 +0300 Subject: [PATCH 09/10] Argument types related changes: 1) Remove unused properties of `FunctionInfo`. 2) Add a support for a minimal argument count (-3 = at least 2 args). 3) Update argument count for some entries in `BuiltinList.Functions`. 4) A minor optimization for `Parser.ParseTokens()`. --- UndertaleModLib/Compiler/BuiltinList.cs | 513 ++++++++++++------------ UndertaleModLib/Compiler/Parser.cs | 31 +- 2 files changed, 274 insertions(+), 270 deletions(-) diff --git a/UndertaleModLib/Compiler/BuiltinList.cs b/UndertaleModLib/Compiler/BuiltinList.cs index 7330949a5..01555f92a 100644 --- a/UndertaleModLib/Compiler/BuiltinList.cs +++ b/UndertaleModLib/Compiler/BuiltinList.cs @@ -56,8 +56,6 @@ public VariableInfo(BuiltinList list, bool canGet, bool canSet, string setterFun public class FunctionInfo { public int ArgumentCount; - public bool IsFirstParamInstance = false; - public bool IsFirstParamOther = false; public int ID; public FunctionClassification Classification = FunctionClassification.None; @@ -72,21 +70,6 @@ public FunctionInfo(BuiltinList list, int argumentCount, FunctionClassification Classification = classification; // Maybe handle this differently? (look UndertaleFunction.cs) } - - public FunctionInfo(BuiltinList list, int argumentCount, bool isFirstParamInstance) - { - ArgumentCount = argumentCount; - IsFirstParamInstance = isFirstParamInstance; - ID = list.CurrentID++; - } - - public FunctionInfo(BuiltinList list, int argumentCount, bool isFirstParamInstance, bool isFirstParamOther) - { - ArgumentCount = argumentCount; - IsFirstParamInstance = isFirstParamInstance; - IsFirstParamOther = isFirstParamOther; - ID = list.CurrentID++; - } } public class AccessorInfo @@ -136,24 +119,29 @@ public BuiltinList(UndertaleData data) public void Initialize(UndertaleData data) { - // Functions + // Functions. + // If the argument count is less that -1, then it's a minimal count + // (-2 = at least 1 argument). Functions = new Dictionary(); - Functions["matrix_get"] = new FunctionInfo(this, 1); - Functions["matrix_set"] = new FunctionInfo(this, 2); - Functions["matrix_build"] = new FunctionInfo(this, 9); - Functions["matrix_build_lookat"] = new FunctionInfo(this, 9); - Functions["matrix_build_identity"] = new FunctionInfo(this, 0); - Functions["matrix_build_projection_ortho"] = new FunctionInfo(this, 4); - Functions["matrix_build_projection_perspective"] = new FunctionInfo(this, 4); - Functions["matrix_build_projection_perspective_fov"] = new FunctionInfo(this, 4); - Functions["matrix_multiply"] = new FunctionInfo(this, 2); - Functions["matrix_transform_vertex"] = new FunctionInfo(this, 4); - Functions["matrix_stack_push"] = new FunctionInfo(this, -1); - Functions["matrix_stack_pop"] = new FunctionInfo(this, 0); - Functions["matrix_stack_set"] = new FunctionInfo(this, 1); - Functions["matrix_stack_clear"] = new FunctionInfo(this, 0); - Functions["matrix_stack_top"] = new FunctionInfo(this, 0); - Functions["matrix_stack_is_empty"] = new FunctionInfo(this, 0); + if (data?.IsVersionAtLeast(2, 0, 4) == true) + { + Functions["matrix_get"] = new FunctionInfo(this, 1); + Functions["matrix_set"] = new FunctionInfo(this, 2); + Functions["matrix_build"] = new FunctionInfo(this, 9); + Functions["matrix_build_lookat"] = new FunctionInfo(this, 9); + Functions["matrix_build_identity"] = new FunctionInfo(this, 0); + Functions["matrix_build_projection_ortho"] = new FunctionInfo(this, 4); + Functions["matrix_build_projection_perspective"] = new FunctionInfo(this, 4); + Functions["matrix_build_projection_perspective_fov"] = new FunctionInfo(this, 4); + Functions["matrix_multiply"] = new FunctionInfo(this, 2); + Functions["matrix_transform_vertex"] = new FunctionInfo(this, 4); + Functions["matrix_stack_push"] = new FunctionInfo(this, 1); + Functions["matrix_stack_pop"] = new FunctionInfo(this, 0); + Functions["matrix_stack_set"] = new FunctionInfo(this, 1); + Functions["matrix_stack_clear"] = new FunctionInfo(this, 0); + Functions["matrix_stack_top"] = new FunctionInfo(this, 0); + Functions["matrix_stack_is_empty"] = new FunctionInfo(this, 0); + } if (data?.GeneralInfo?.Major < 2) { Functions["d3d_start"] = new FunctionInfo(this, 0); @@ -247,7 +235,7 @@ public void Initialize(UndertaleData data) if (data?.GeneralInfo?.Major < 2) { Functions["action_path_old"] = new FunctionInfo(this, 3); - Functions["action_set_sprite"] = new FunctionInfo(this, 2, true); + Functions["action_set_sprite"] = new FunctionInfo(this, 2); Functions["action_draw_font"] = new FunctionInfo(this, 1); Functions["action_draw_font_old"] = new FunctionInfo(this, 6); Functions["action_fill_color"] = new FunctionInfo(this, 1); @@ -256,38 +244,38 @@ public void Initialize(UndertaleData data) Functions["action_line_colour"] = new FunctionInfo(this, 1); Functions["action_highscore"] = new FunctionInfo(this, 0); Functions["action_set_relative"] = new FunctionInfo(this, 1); - Functions["action_move"] = new FunctionInfo(this, 2, true); - Functions["action_set_motion"] = new FunctionInfo(this, 2, true); - Functions["action_set_hspeed"] = new FunctionInfo(this, 1, true); - Functions["action_set_vspeed"] = new FunctionInfo(this, 1, true); - Functions["action_set_gravity"] = new FunctionInfo(this, 2, true); - Functions["action_set_friction"] = new FunctionInfo(this, 1, true); - Functions["action_move_point"] = new FunctionInfo(this, 3, true); - Functions["action_move_to"] = new FunctionInfo(this, 2, true); - Functions["action_move_start"] = new FunctionInfo(this, 0, true); - Functions["action_move_random"] = new FunctionInfo(this, 2, true); - Functions["action_snap"] = new FunctionInfo(this, 2, true); - Functions["action_wrap"] = new FunctionInfo(this, 1, true); - Functions["action_reverse_xdir"] = new FunctionInfo(this, 0, true); - Functions["action_reverse_ydir"] = new FunctionInfo(this, 0, true); - Functions["action_move_contact"] = new FunctionInfo(this, 3, true); - Functions["action_bounce"] = new FunctionInfo(this, 2, true); - Functions["action_path"] = new FunctionInfo(this, 4, true); - Functions["action_path_end"] = new FunctionInfo(this, 0, true); - Functions["action_path_position"] = new FunctionInfo(this, 1, true); - Functions["action_path_speed"] = new FunctionInfo(this, 1, true); - Functions["action_linear_step"] = new FunctionInfo(this, 4, true); - Functions["action_potential_step"] = new FunctionInfo(this, 4, true); - Functions["action_kill_object"] = new FunctionInfo(this, 0, true); - Functions["action_create_object"] = new FunctionInfo(this, 3, true); - Functions["action_create_object_motion"] = new FunctionInfo(this, 5, true); - Functions["action_create_object_random"] = new FunctionInfo(this, 6, true); - Functions["action_change_object"] = new FunctionInfo(this, 2, true); - Functions["action_kill_position"] = new FunctionInfo(this, 2, true); - Functions["action_sprite_set"] = new FunctionInfo(this, 3, true); - Functions["action_sprite_transform"] = new FunctionInfo(this, 4, true); - Functions["action_sprite_color"] = new FunctionInfo(this, 2, true); - Functions["action_sprite_colour"] = new FunctionInfo(this, 2, true); + Functions["action_move"] = new FunctionInfo(this, 2); + Functions["action_set_motion"] = new FunctionInfo(this, 2); + Functions["action_set_hspeed"] = new FunctionInfo(this, 1); + Functions["action_set_vspeed"] = new FunctionInfo(this, 1); + Functions["action_set_gravity"] = new FunctionInfo(this, 2); + Functions["action_set_friction"] = new FunctionInfo(this, 1); + Functions["action_move_point"] = new FunctionInfo(this, 3); + Functions["action_move_to"] = new FunctionInfo(this, 2); + Functions["action_move_start"] = new FunctionInfo(this, 0); + Functions["action_move_random"] = new FunctionInfo(this, 2); + Functions["action_snap"] = new FunctionInfo(this, 2); + Functions["action_wrap"] = new FunctionInfo(this, 1); + Functions["action_reverse_xdir"] = new FunctionInfo(this, 0); + Functions["action_reverse_ydir"] = new FunctionInfo(this, 0); + Functions["action_move_contact"] = new FunctionInfo(this, 3); + Functions["action_bounce"] = new FunctionInfo(this, 2); + Functions["action_path"] = new FunctionInfo(this, 4); + Functions["action_path_end"] = new FunctionInfo(this, 0); + Functions["action_path_position"] = new FunctionInfo(this, 1); + Functions["action_path_speed"] = new FunctionInfo(this, 1); + Functions["action_linear_step"] = new FunctionInfo(this, 4); + Functions["action_potential_step"] = new FunctionInfo(this, 4); + Functions["action_kill_object"] = new FunctionInfo(this, 0); + Functions["action_create_object"] = new FunctionInfo(this, 3); + Functions["action_create_object_motion"] = new FunctionInfo(this, 5); + Functions["action_create_object_random"] = new FunctionInfo(this, 6); + Functions["action_change_object"] = new FunctionInfo(this, 2); + Functions["action_kill_position"] = new FunctionInfo(this, 2); + Functions["action_sprite_set"] = new FunctionInfo(this, 3); + Functions["action_sprite_transform"] = new FunctionInfo(this, 4); + Functions["action_sprite_color"] = new FunctionInfo(this, 2); + Functions["action_sprite_colour"] = new FunctionInfo(this, 2); Functions["action_sound"] = new FunctionInfo(this, 2); Functions["action_end_sound"] = new FunctionInfo(this, 1); Functions["action_if_sound"] = new FunctionInfo(this, 1); @@ -297,15 +285,15 @@ public void Initialize(UndertaleData data) Functions["action_next_room"] = new FunctionInfo(this, 0); Functions["action_if_previous_room"] = new FunctionInfo(this, 0); Functions["action_if_next_room"] = new FunctionInfo(this, 0); - Functions["action_set_alarm"] = new FunctionInfo(this, 2, true); + Functions["action_set_alarm"] = new FunctionInfo(this, 2); Functions["action_sleep"] = new FunctionInfo(this, 2); - Functions["action_set_timeline"] = new FunctionInfo(this, 2, true); - Functions["action_timeline_set"] = new FunctionInfo(this, 4, true); - Functions["action_timeline_start"] = new FunctionInfo(this, 0, true); - Functions["action_timeline_stop"] = new FunctionInfo(this, 0, true); - Functions["action_timeline_pause"] = new FunctionInfo(this, 0, true); - Functions["action_set_timeline_position"] = new FunctionInfo(this, 1, true); - Functions["action_set_timeline_speed"] = new FunctionInfo(this, 1, true); + Functions["action_set_timeline"] = new FunctionInfo(this, 2); + Functions["action_timeline_set"] = new FunctionInfo(this, 4); + Functions["action_timeline_start"] = new FunctionInfo(this, 0); + Functions["action_timeline_stop"] = new FunctionInfo(this, 0); + Functions["action_timeline_pause"] = new FunctionInfo(this, 0); + Functions["action_set_timeline_position"] = new FunctionInfo(this, 1); + Functions["action_set_timeline_speed"] = new FunctionInfo(this, 1); Functions["action_message"] = new FunctionInfo(this, 1); Functions["action_show_info"] = new FunctionInfo(this, 0); Functions["action_show_video"] = new FunctionInfo(this, 3); @@ -316,31 +304,31 @@ public void Initialize(UndertaleData data) Functions["action_replace_sprite"] = new FunctionInfo(this, 3); Functions["action_replace_sound"] = new FunctionInfo(this, 2); Functions["action_replace_background"] = new FunctionInfo(this, 2); - Functions["action_if_empty"] = new FunctionInfo(this, 3, true); - Functions["action_if_collision"] = new FunctionInfo(this, 3, true); + Functions["action_if_empty"] = new FunctionInfo(this, 3); + Functions["action_if_collision"] = new FunctionInfo(this, 3); Functions["action_if"] = new FunctionInfo(this, 1); Functions["action_if_number"] = new FunctionInfo(this, 3); - Functions["action_if_object"] = new FunctionInfo(this, 3, true); + Functions["action_if_object"] = new FunctionInfo(this, 3); Functions["action_if_question"] = new FunctionInfo(this, 1); Functions["action_if_dice"] = new FunctionInfo(this, 1); Functions["action_if_mouse"] = new FunctionInfo(this, 1); - Functions["action_if_aligned"] = new FunctionInfo(this, 2, true); - Functions["action_execute_script"] = new FunctionInfo(this, 6, true); - Functions["action_inherited"] = new FunctionInfo(this, 0, true, true); + Functions["action_if_aligned"] = new FunctionInfo(this, 2); + Functions["action_execute_script"] = new FunctionInfo(this, 6); + Functions["action_inherited"] = new FunctionInfo(this, 0); Functions["action_if_variable"] = new FunctionInfo(this, 3); - Functions["action_draw_variable"] = new FunctionInfo(this, 3, true); + Functions["action_draw_variable"] = new FunctionInfo(this, 3); Functions["action_set_score"] = new FunctionInfo(this, 1); Functions["action_if_score"] = new FunctionInfo(this, 2); - Functions["action_draw_score"] = new FunctionInfo(this, 3, true); + Functions["action_draw_score"] = new FunctionInfo(this, 3); Functions["action_highscore_show"] = new FunctionInfo(this, 11); Functions["action_highscore_clear"] = new FunctionInfo(this, 0); Functions["action_set_life"] = new FunctionInfo(this, 1); Functions["action_if_life"] = new FunctionInfo(this, 2); - Functions["action_draw_life"] = new FunctionInfo(this, 3, true); - Functions["action_draw_life_images"] = new FunctionInfo(this, 3, true); - Functions["action_set_health"] = new FunctionInfo(this, 1, true); - Functions["action_if_health"] = new FunctionInfo(this, 2, true); - Functions["action_draw_health"] = new FunctionInfo(this, 6, true); + Functions["action_draw_life"] = new FunctionInfo(this, 3); + Functions["action_draw_life_images"] = new FunctionInfo(this, 3); + Functions["action_set_health"] = new FunctionInfo(this, 1); + Functions["action_if_health"] = new FunctionInfo(this, 2); + Functions["action_draw_health"] = new FunctionInfo(this, 6); Functions["action_set_caption"] = new FunctionInfo(this, 6); Functions["action_partsyst_create"] = new FunctionInfo(this, 1); Functions["action_partsyst_destroy"] = new FunctionInfo(this, 0); @@ -366,23 +354,23 @@ public void Initialize(UndertaleData data) Functions["action_set_cursor"] = new FunctionInfo(this, 2); Functions["action_webpage"] = new FunctionInfo(this, 1); Functions["action_splash_web"] = new FunctionInfo(this, 1); - Functions["action_draw_sprite"] = new FunctionInfo(this, 4, true); - Functions["action_draw_background"] = new FunctionInfo(this, 4, true); - Functions["action_draw_text"] = new FunctionInfo(this, 3, true); - Functions["action_draw_text_transformed"] = new FunctionInfo(this, 6, true); - Functions["action_draw_rectangle"] = new FunctionInfo(this, 5, true); - Functions["action_draw_gradient_hor"] = new FunctionInfo(this, 6, true); - Functions["action_draw_gradient_vert"] = new FunctionInfo(this, 6, true); - Functions["action_draw_ellipse"] = new FunctionInfo(this, 5, true); - Functions["action_draw_ellipse_gradient"] = new FunctionInfo(this, 6, true); - Functions["action_draw_line"] = new FunctionInfo(this, 4, true); - Functions["action_draw_arrow"] = new FunctionInfo(this, 5, true); + Functions["action_draw_sprite"] = new FunctionInfo(this, 4); + Functions["action_draw_background"] = new FunctionInfo(this, 4); + Functions["action_draw_text"] = new FunctionInfo(this, 3); + Functions["action_draw_text_transformed"] = new FunctionInfo(this, 6); + Functions["action_draw_rectangle"] = new FunctionInfo(this, 5); + Functions["action_draw_gradient_hor"] = new FunctionInfo(this, 6); + Functions["action_draw_gradient_vert"] = new FunctionInfo(this, 6); + Functions["action_draw_ellipse"] = new FunctionInfo(this, 5); + Functions["action_draw_ellipse_gradient"] = new FunctionInfo(this, 6); + Functions["action_draw_line"] = new FunctionInfo(this, 4); + Functions["action_draw_arrow"] = new FunctionInfo(this, 5); Functions["action_color"] = new FunctionInfo(this, 1); Functions["action_colour"] = new FunctionInfo(this, 1); Functions["action_font"] = new FunctionInfo(this, 2); Functions["action_fullscreen"] = new FunctionInfo(this, 1); Functions["action_snapshot"] = new FunctionInfo(this, 1); - Functions["action_effect"] = new FunctionInfo(this, 6, true); + Functions["action_effect"] = new FunctionInfo(this, 6); } Functions["ds_set_precision"] = new FunctionInfo(this, 1); Functions["ds_exists"] = new FunctionInfo(this, 2); @@ -392,30 +380,30 @@ public void Initialize(UndertaleData data) Functions["ds_stack_copy"] = new FunctionInfo(this, 2); Functions["ds_stack_size"] = new FunctionInfo(this, 1); Functions["ds_stack_empty"] = new FunctionInfo(this, 1); - Functions["ds_stack_push"] = new FunctionInfo(this, -1); + Functions["ds_stack_push"] = new FunctionInfo(this, -3); Functions["ds_stack_pop"] = new FunctionInfo(this, 1); Functions["ds_stack_top"] = new FunctionInfo(this, 1); Functions["ds_stack_write"] = new FunctionInfo(this, 1); - Functions["ds_stack_read"] = new FunctionInfo(this, -1); + Functions["ds_stack_read"] = new FunctionInfo(this, -3); Functions["ds_queue_create"] = new FunctionInfo(this, 0); Functions["ds_queue_destroy"] = new FunctionInfo(this, 1); Functions["ds_queue_clear"] = new FunctionInfo(this, 1); Functions["ds_queue_copy"] = new FunctionInfo(this, 2); Functions["ds_queue_size"] = new FunctionInfo(this, 1); Functions["ds_queue_empty"] = new FunctionInfo(this, 1); - Functions["ds_queue_enqueue"] = new FunctionInfo(this, -1); + Functions["ds_queue_enqueue"] = new FunctionInfo(this, -3); Functions["ds_queue_dequeue"] = new FunctionInfo(this, 1); Functions["ds_queue_head"] = new FunctionInfo(this, 1); Functions["ds_queue_tail"] = new FunctionInfo(this, 1); Functions["ds_queue_write"] = new FunctionInfo(this, 1); - Functions["ds_queue_read"] = new FunctionInfo(this, -1); + Functions["ds_queue_read"] = new FunctionInfo(this, -3); Functions["ds_list_create"] = new FunctionInfo(this, 0); Functions["ds_list_destroy"] = new FunctionInfo(this, 1); Functions["ds_list_clear"] = new FunctionInfo(this, 1); Functions["ds_list_copy"] = new FunctionInfo(this, 2); Functions["ds_list_size"] = new FunctionInfo(this, 1); Functions["ds_list_empty"] = new FunctionInfo(this, 1); - Functions["ds_list_add"] = new FunctionInfo(this, -1); + Functions["ds_list_add"] = new FunctionInfo(this, -3); Functions["ds_list_insert"] = new FunctionInfo(this, 3); Functions["ds_list_replace"] = new FunctionInfo(this, 3); Functions["ds_list_delete"] = new FunctionInfo(this, 2); @@ -426,7 +414,7 @@ public void Initialize(UndertaleData data) Functions["ds_list_sort"] = new FunctionInfo(this, 2); Functions["ds_list_shuffle"] = new FunctionInfo(this, 1); Functions["ds_list_write"] = new FunctionInfo(this, 1); - Functions["ds_list_read"] = new FunctionInfo(this, -1); + Functions["ds_list_read"] = new FunctionInfo(this, -3); Functions["ds_list_set"] = new FunctionInfo(this, 3); Functions["ds_list_set_pre"] = new FunctionInfo(this, 3); Functions["ds_list_set_post"] = new FunctionInfo(this, 3); @@ -450,7 +438,7 @@ public void Initialize(UndertaleData data) Functions["ds_map_find_first"] = new FunctionInfo(this, 1); Functions["ds_map_find_last"] = new FunctionInfo(this, 1); Functions["ds_map_write"] = new FunctionInfo(this, 1); - Functions["ds_map_read"] = new FunctionInfo(this, -1); + Functions["ds_map_read"] = new FunctionInfo(this, -3); Functions["ds_map_secure_save"] = new FunctionInfo(this, 2); Functions["ds_map_secure_load"] = new FunctionInfo(this, 1); Functions["ds_map_secure_load_buffer"] = new FunctionInfo(this, 1); @@ -461,7 +449,7 @@ public void Initialize(UndertaleData data) Functions["ds_priority_create"] = new FunctionInfo(this, 0); Functions["ds_priority_destroy"] = new FunctionInfo(this, 1); Functions["ds_priority_clear"] = new FunctionInfo(this, 1); - Functions["ds_priority_copy"] = new FunctionInfo(this, 2, true); + Functions["ds_priority_copy"] = new FunctionInfo(this, 2); Functions["ds_priority_size"] = new FunctionInfo(this, 1); Functions["ds_priority_empty"] = new FunctionInfo(this, 1); Functions["ds_priority_add"] = new FunctionInfo(this, 3); @@ -473,7 +461,7 @@ public void Initialize(UndertaleData data) Functions["ds_priority_delete_max"] = new FunctionInfo(this, 1); Functions["ds_priority_find_max"] = new FunctionInfo(this, 1); Functions["ds_priority_write"] = new FunctionInfo(this, 1); - Functions["ds_priority_read"] = new FunctionInfo(this, -1); + Functions["ds_priority_read"] = new FunctionInfo(this, -3); Functions["ds_grid_create"] = new FunctionInfo(this, 2); Functions["ds_grid_destroy"] = new FunctionInfo(this, 1); Functions["ds_grid_copy"] = new FunctionInfo(this, 2); @@ -510,7 +498,7 @@ public void Initialize(UndertaleData data) Functions["ds_grid_value_disk_y"] = new FunctionInfo(this, 5); Functions["ds_grid_shuffle"] = new FunctionInfo(this, 1); Functions["ds_grid_write"] = new FunctionInfo(this, 1); - Functions["ds_grid_read"] = new FunctionInfo(this, -1); + Functions["ds_grid_read"] = new FunctionInfo(this, -3); Functions["ds_grid_sort"] = new FunctionInfo(this, 3); Functions["ds_grid_set_pre"] = new FunctionInfo(this, 4); Functions["ds_grid_set_post"] = new FunctionInfo(this, 4); @@ -585,37 +573,40 @@ public void Initialize(UndertaleData data) Functions["json_decode"] = new FunctionInfo(this, 1); Functions["zip_unzip"] = new FunctionInfo(this, 2); Functions["load_csv"] = new FunctionInfo(this, 1); - Functions["move_random"] = new FunctionInfo(this, 2, true); - Functions["place_free"] = new FunctionInfo(this, 2, true); - Functions["place_empty"] = new FunctionInfo(this, -1, true); - Functions["place_meeting"] = new FunctionInfo(this, 3, true); - Functions["place_snapped"] = new FunctionInfo(this, 2, true); - Functions["move_snap"] = new FunctionInfo(this, 2, true); - Functions["move_towards_point"] = new FunctionInfo(this, 3, true); - Functions["move_contact"] = new FunctionInfo(this, 1, true); - Functions["move_contact_solid"] = new FunctionInfo(this, 2, true); - Functions["move_contact_all"] = new FunctionInfo(this, 2, true); - Functions["move_outside_solid"] = new FunctionInfo(this, 2, true); - Functions["move_outside_all"] = new FunctionInfo(this, 2, true); - Functions["move_bounce"] = new FunctionInfo(this, 1, true); - Functions["move_bounce_solid"] = new FunctionInfo(this, 1, true); - Functions["move_bounce_all"] = new FunctionInfo(this, 1, true); - Functions["move_wrap"] = new FunctionInfo(this, 3, true); - Functions["motion_set"] = new FunctionInfo(this, 2, true); - Functions["motion_add"] = new FunctionInfo(this, 2, true); - Functions["distance_to_point"] = new FunctionInfo(this, 2, true); - Functions["distance_to_object"] = new FunctionInfo(this, 1, true); - Functions["path_start"] = new FunctionInfo(this, 4, true); - Functions["path_end"] = new FunctionInfo(this, 0, true); - Functions["mp_linear_step"] = new FunctionInfo(this, 4, true); - Functions["mp_linear_path"] = new FunctionInfo(this, 5, true); - Functions["mp_linear_step_object"] = new FunctionInfo(this, 4, true); - Functions["mp_linear_path_object"] = new FunctionInfo(this, 5, true); - Functions["mp_potential_settings"] = new FunctionInfo(this, 4, true); - Functions["mp_potential_step"] = new FunctionInfo(this, 4, true); - Functions["mp_potential_path"] = new FunctionInfo(this, 6, true); - Functions["mp_potential_step_object"] = new FunctionInfo(this, 4, true); - Functions["mp_potential_path_object"] = new FunctionInfo(this, 6, true); + Functions["move_random"] = new FunctionInfo(this, 2); + Functions["place_free"] = new FunctionInfo(this, 2); + if (data?.IsVersionAtLeast(2, 2, 2) == true) + Functions["place_empty"] = new FunctionInfo(this, -3); + else + Functions["place_empty"] = new FunctionInfo(this, 2); + Functions["place_meeting"] = new FunctionInfo(this, 3); + Functions["place_snapped"] = new FunctionInfo(this, 2); + Functions["move_snap"] = new FunctionInfo(this, 2); + Functions["move_towards_point"] = new FunctionInfo(this, 3); + Functions["move_contact"] = new FunctionInfo(this, 1); + Functions["move_contact_solid"] = new FunctionInfo(this, 2); + Functions["move_contact_all"] = new FunctionInfo(this, 2); + Functions["move_outside_solid"] = new FunctionInfo(this, 2); + Functions["move_outside_all"] = new FunctionInfo(this, 2); + Functions["move_bounce"] = new FunctionInfo(this, 1); + Functions["move_bounce_solid"] = new FunctionInfo(this, 1); + Functions["move_bounce_all"] = new FunctionInfo(this, 1); + Functions["move_wrap"] = new FunctionInfo(this, 3); + Functions["motion_set"] = new FunctionInfo(this, 2); + Functions["motion_add"] = new FunctionInfo(this, 2); + Functions["distance_to_point"] = new FunctionInfo(this, 2); + Functions["distance_to_object"] = new FunctionInfo(this, 1); + Functions["path_start"] = new FunctionInfo(this, 4); + Functions["path_end"] = new FunctionInfo(this, 0); + Functions["mp_linear_step"] = new FunctionInfo(this, 4); + Functions["mp_linear_path"] = new FunctionInfo(this, 5); + Functions["mp_linear_step_object"] = new FunctionInfo(this, 4); + Functions["mp_linear_path_object"] = new FunctionInfo(this, 5); + Functions["mp_potential_settings"] = new FunctionInfo(this, 4); + Functions["mp_potential_step"] = new FunctionInfo(this, 4); + Functions["mp_potential_path"] = new FunctionInfo(this, 6); + Functions["mp_potential_step_object"] = new FunctionInfo(this, 4); + Functions["mp_potential_path_object"] = new FunctionInfo(this, 6); Functions["mp_grid_create"] = new FunctionInfo(this, 6); Functions["mp_grid_destroy"] = new FunctionInfo(this, 1); Functions["mp_grid_clear_all"] = new FunctionInfo(this, 1); @@ -624,60 +615,58 @@ public void Initialize(UndertaleData data) Functions["mp_grid_add_cell"] = new FunctionInfo(this, 3); Functions["mp_grid_get_cell"] = new FunctionInfo(this, 3); Functions["mp_grid_add_rectangle"] = new FunctionInfo(this, 5); - Functions["mp_grid_add_instances"] = new FunctionInfo(this, 3, true); - Functions["mp_grid_path"] = new FunctionInfo(this, 7, true); + Functions["mp_grid_add_instances"] = new FunctionInfo(this, 3); + Functions["mp_grid_path"] = new FunctionInfo(this, 7); Functions["mp_grid_draw"] = new FunctionInfo(this, 1); Functions["mp_grid_to_ds_grid"] = new FunctionInfo(this, 2); - Functions["collision_point"] = new FunctionInfo(this, 5, true); - Functions["collision_point_list"] = new FunctionInfo(this, 7, true); - Functions["collision_rectangle"] = new FunctionInfo(this, 7, true); - Functions["collision_rectangle_list"] = new FunctionInfo(this, 9, true); - Functions["collision_circle"] = new FunctionInfo(this, 6, true); - Functions["collision_circle_list"] = new FunctionInfo(this, 8, true); - Functions["collision_ellipse"] = new FunctionInfo(this, 7, true); - Functions["collision_ellipse_list"] = new FunctionInfo(this, 9, true); - Functions["collision_line"] = new FunctionInfo(this, 7, true); - Functions["collision_line_list"] = new FunctionInfo(this, 9, true); - Functions["collision_shape"] = new FunctionInfo(this, 9, true); - Functions["point_in_rectangle"] = new FunctionInfo(this, 6, false); - Functions["point_in_triangle"] = new FunctionInfo(this, 8, false); - Functions["point_in_circle"] = new FunctionInfo(this, 5, false); - Functions["rectangle_in_rectangle"] = new FunctionInfo(this, 8, false); - Functions["rectangle_in_triangle"] = new FunctionInfo(this, 10, false); - Functions["rectangle_in_circle"] = new FunctionInfo(this, 7, false); + Functions["collision_point"] = new FunctionInfo(this, 5); + Functions["collision_point_list"] = new FunctionInfo(this, 7); + Functions["collision_rectangle"] = new FunctionInfo(this, 7); + Functions["collision_rectangle_list"] = new FunctionInfo(this, 9); + Functions["collision_circle"] = new FunctionInfo(this, 6); + Functions["collision_circle_list"] = new FunctionInfo(this, 8); + Functions["collision_ellipse"] = new FunctionInfo(this, 7); + Functions["collision_ellipse_list"] = new FunctionInfo(this, 9); + Functions["collision_line"] = new FunctionInfo(this, 7); + Functions["collision_line_list"] = new FunctionInfo(this, 9); + Functions["collision_shape"] = new FunctionInfo(this, 9); + Functions["point_in_rectangle"] = new FunctionInfo(this, 6); + Functions["point_in_triangle"] = new FunctionInfo(this, 8); + Functions["point_in_circle"] = new FunctionInfo(this, 5); + Functions["rectangle_in_rectangle"] = new FunctionInfo(this, 8); + Functions["rectangle_in_triangle"] = new FunctionInfo(this, 10); + Functions["rectangle_in_circle"] = new FunctionInfo(this, 7); Functions["instance_find"] = new FunctionInfo(this, 2); Functions["instance_exists"] = new FunctionInfo(this, 1); Functions["instance_number"] = new FunctionInfo(this, 1); Functions["instance_position"] = new FunctionInfo(this, 3); Functions["instance_position_list"] = new FunctionInfo(this, 5); - Functions["instance_nearest"] = new FunctionInfo(this, 3, true); - Functions["instance_furthest"] = new FunctionInfo(this, 3, true); - Functions["instance_place"] = new FunctionInfo(this, 3, true); - Functions["instance_place_list"] = new FunctionInfo(this, 5, true); + Functions["instance_nearest"] = new FunctionInfo(this, 3); + Functions["instance_furthest"] = new FunctionInfo(this, 3); + Functions["instance_place"] = new FunctionInfo(this, 3); + Functions["instance_place_list"] = new FunctionInfo(this, 5); if (data?.GeneralInfo?.Major < 2) - { Functions["instance_create"] = new FunctionInfo(this, 3); - } Functions["instance_create_depth"] = new FunctionInfo(this, 4); Functions["instance_create_layer"] = new FunctionInfo(this, 4); - Functions["instance_copy"] = new FunctionInfo(this, 1, true); - Functions["instance_change"] = new FunctionInfo(this, 2, true); - Functions["instance_destroy"] = new FunctionInfo(this, -1, true); - Functions["instance_sprite"] = new FunctionInfo(this, 1, true); - Functions["position_empty"] = new FunctionInfo(this, 2, true); - Functions["position_meeting"] = new FunctionInfo(this, 3, true); - Functions["position_destroy"] = new FunctionInfo(this, 2, true); - Functions["position_change"] = new FunctionInfo(this, 4, true); - Functions["instance_id_get"] = new FunctionInfo(this, 1, true); - Functions["instance_deactivate_all"] = new FunctionInfo(this, 1, true); - Functions["instance_deactivate_object"] = new FunctionInfo(this, 1, true); - Functions["instance_deactivate_region"] = new FunctionInfo(this, 6, true); - Functions["instance_deactivate_region_special"] = new FunctionInfo(this, 8, true); - Functions["instance_deactivate_layer"] = new FunctionInfo(this, 1, true); - Functions["instance_activate_all"] = new FunctionInfo(this, 0, true); - Functions["instance_activate_object"] = new FunctionInfo(this, 1, true); - Functions["instance_activate_region"] = new FunctionInfo(this, 5, true); - Functions["instance_activate_layer"] = new FunctionInfo(this, 1, true); + Functions["instance_copy"] = new FunctionInfo(this, 1); + Functions["instance_change"] = new FunctionInfo(this, 2); + Functions["instance_destroy"] = new FunctionInfo(this, -1); + Functions["instance_sprite"] = new FunctionInfo(this, 1); + Functions["position_empty"] = new FunctionInfo(this, 2); + Functions["position_meeting"] = new FunctionInfo(this, 3); + Functions["position_destroy"] = new FunctionInfo(this, 2); + Functions["position_change"] = new FunctionInfo(this, 4); + Functions["instance_id_get"] = new FunctionInfo(this, 1); + Functions["instance_deactivate_all"] = new FunctionInfo(this, 1); + Functions["instance_deactivate_object"] = new FunctionInfo(this, 1); + Functions["instance_deactivate_region"] = new FunctionInfo(this, 6); + Functions["instance_deactivate_region_special"] = new FunctionInfo(this, 8); + Functions["instance_deactivate_layer"] = new FunctionInfo(this, 1); + Functions["instance_activate_all"] = new FunctionInfo(this, 0); + Functions["instance_activate_object"] = new FunctionInfo(this, 1); + Functions["instance_activate_region"] = new FunctionInfo(this, 5); + Functions["instance_activate_layer"] = new FunctionInfo(this, 1); Functions["room_goto"] = new FunctionInfo(this, 1); Functions["room_goto_previous"] = new FunctionInfo(this, 0); Functions["room_goto_next"] = new FunctionInfo(this, 0); @@ -695,7 +684,7 @@ public void Initialize(UndertaleData data) Functions["display_get_orientation"] = new FunctionInfo(this, 0); Functions["display_get_gui_width"] = new FunctionInfo(this, 0); Functions["display_get_gui_height"] = new FunctionInfo(this, 0); - Functions["display_reset"] = new FunctionInfo(this, 2, true); + Functions["display_reset"] = new FunctionInfo(this, 2); Functions["display_mouse_get_x"] = new FunctionInfo(this, 0); Functions["display_mouse_get_y"] = new FunctionInfo(this, 0); Functions["display_mouse_set"] = new FunctionInfo(this, 2); @@ -881,19 +870,19 @@ public void Initialize(UndertaleData data) Functions["draw_text_ext_colour"] = new FunctionInfo(this, 10); Functions["draw_text_ext_transformed_colour"] = new FunctionInfo(this, 13); Functions["shader_enable_corner_id"] = new FunctionInfo(this, 1); - Functions["draw_self"] = new FunctionInfo(this, 0, true); - Functions["draw_sprite"] = new FunctionInfo(this, 4, true); - Functions["draw_sprite_pos"] = new FunctionInfo(this, 11, true); - Functions["draw_shape"] = new FunctionInfo(this, 12, true); - Functions["draw_shape_string"] = new FunctionInfo(this, 11, true); - Functions["draw_sprite_ext"] = new FunctionInfo(this, 9, true); - Functions["draw_sprite_stretched"] = new FunctionInfo(this, 6, true); - Functions["draw_sprite_stretched_ext"] = new FunctionInfo(this, 8, true); - Functions["draw_sprite_part"] = new FunctionInfo(this, 8, true); - Functions["draw_sprite_part_ext"] = new FunctionInfo(this, 12, true); - Functions["draw_sprite_general"] = new FunctionInfo(this, 16, true); - Functions["draw_sprite_tiled"] = new FunctionInfo(this, 4, true); - Functions["draw_sprite_tiled_ext"] = new FunctionInfo(this, 8, true); + Functions["draw_self"] = new FunctionInfo(this, 0); + Functions["draw_sprite"] = new FunctionInfo(this, 4); + Functions["draw_sprite_pos"] = new FunctionInfo(this, 11); + Functions["draw_shape"] = new FunctionInfo(this, 12); + Functions["draw_shape_string"] = new FunctionInfo(this, 11); + Functions["draw_sprite_ext"] = new FunctionInfo(this, 9); + Functions["draw_sprite_stretched"] = new FunctionInfo(this, 6); + Functions["draw_sprite_stretched_ext"] = new FunctionInfo(this, 8); + Functions["draw_sprite_part"] = new FunctionInfo(this, 8); + Functions["draw_sprite_part_ext"] = new FunctionInfo(this, 12); + Functions["draw_sprite_general"] = new FunctionInfo(this, 16); + Functions["draw_sprite_tiled"] = new FunctionInfo(this, 4); + Functions["draw_sprite_tiled_ext"] = new FunctionInfo(this, 8); if (data?.GeneralInfo?.Major < 2) { Functions["draw_background"] = new FunctionInfo(this, 3); @@ -1102,7 +1091,7 @@ public void Initialize(UndertaleData data) Functions["array_set_2D_post"] = new FunctionInfo(this, 4); Functions["array_get_2D"] = new FunctionInfo(this, 3); Functions["array_equals"] = new FunctionInfo(this, 2); - Functions["array_create"] = new FunctionInfo(this, -1); + Functions["array_create"] = new FunctionInfo(this, -2); Functions["array_copy"] = new FunctionInfo(this, 5); if (data?.IsVersionAtLeast(2, 3) == true) { @@ -1178,7 +1167,7 @@ public void Initialize(UndertaleData data) Functions["real"] = new FunctionInfo(this, 1); Functions["bool"] = new FunctionInfo(this, 1); if (data?.IsVersionAtLeast(2022, 11) == true) - Functions["string"] = new FunctionInfo(this, -1); + Functions["string"] = new FunctionInfo(this, -2); else Functions["string"] = new FunctionInfo(this, 1); Functions["int64"] = new FunctionInfo(this, 1); @@ -1212,12 +1201,12 @@ public void Initialize(UndertaleData data) Functions["point_direction"] = new FunctionInfo(this, 4); Functions["lengthdir_x"] = new FunctionInfo(this, 2); Functions["lengthdir_y"] = new FunctionInfo(this, 2); - Functions["event_inherited"] = new FunctionInfo(this, 0, true, true); - Functions["event_perform"] = new FunctionInfo(this, 2, true); - Functions["event_user"] = new FunctionInfo(this, 1, true); - Functions["event_perform_object"] = new FunctionInfo(this, 3, true); - Functions["external_define"] = new FunctionInfo(this, -1); - Functions["external_call"] = new FunctionInfo(this, -1); + Functions["event_inherited"] = new FunctionInfo(this, 0); + Functions["event_perform"] = new FunctionInfo(this, 2); + Functions["event_user"] = new FunctionInfo(this, 1); + Functions["event_perform_object"] = new FunctionInfo(this, 3); + Functions["external_define"] = new FunctionInfo(this, -6); + Functions["external_call"] = new FunctionInfo(this, -2); Functions["external_free"] = new FunctionInfo(this, 1); Functions["external_define0"] = new FunctionInfo(this, 3); Functions["external_call0"] = new FunctionInfo(this, 1); @@ -1244,8 +1233,8 @@ public void Initialize(UndertaleData data) Functions["show_debug_message"] = new FunctionInfo(this, 1); Functions["show_debug_overlay"] = new FunctionInfo(this, 1); Functions["debug_event"] = new FunctionInfo(this, 1); - Functions["alarm_get"] = new FunctionInfo(this, 1, true); - Functions["alarm_set"] = new FunctionInfo(this, 2, true); + Functions["alarm_get"] = new FunctionInfo(this, 1); + Functions["alarm_set"] = new FunctionInfo(this, 2); Functions["clipboard_has_text"] = new FunctionInfo(this, 0); Functions["clipboard_set_text"] = new FunctionInfo(this, 1); Functions["clipboard_get_text"] = new FunctionInfo(this, 0); @@ -1422,8 +1411,8 @@ public void Initialize(UndertaleData data) Functions["background_get_preload"] = new FunctionInfo(this, 1); Functions["background_set_alpha_from_background"] = new FunctionInfo(this, 2); Functions["background_create_from_surface"] = new FunctionInfo(this, 7); - Functions["background_create_color"] = new FunctionInfo(this, 3, true); - Functions["background_create_colour"] = new FunctionInfo(this, 3, true); + Functions["background_create_color"] = new FunctionInfo(this, 3); + Functions["background_create_colour"] = new FunctionInfo(this, 3); Functions["background_create_gradient"] = new FunctionInfo(this, 5); Functions["background_add"] = new FunctionInfo(this, 3); Functions["background_replace"] = new FunctionInfo(this, 4); @@ -1564,7 +1553,7 @@ public void Initialize(UndertaleData data) Functions["font_delete"] = new FunctionInfo(this, 1); Functions["script_exists"] = new FunctionInfo(this, 1); Functions["script_get_name"] = new FunctionInfo(this, 1); - Functions["script_execute"] = new FunctionInfo(this, -1); + Functions["script_execute"] = new FunctionInfo(this, -2); Functions["path_name"] = new FunctionInfo(this, 1); Functions["path_exists"] = new FunctionInfo(this, 1); Functions["path_get_name"] = new FunctionInfo(this, 1); @@ -1642,7 +1631,7 @@ public void Initialize(UndertaleData data) if (data?.GeneralInfo?.Major < 2) { Functions["room_set_background"] = new FunctionInfo(this, 12); - Functions["room_set_view"] = new FunctionInfo(this, 16, true); + Functions["room_set_view"] = new FunctionInfo(this, 16); } if (data?.GeneralInfo?.Major >= 2) { @@ -1725,12 +1714,10 @@ public void Initialize(UndertaleData data) Functions["ads_event_preload"] = new FunctionInfo(this, 1); Functions["shop_leave_rating"] = new FunctionInfo(this, 4); Functions["analytics_event"] = new FunctionInfo(this, 1); - Functions["analytics_event_ext"] = new FunctionInfo(this, -1); + Functions["analytics_event_ext"] = new FunctionInfo(this, -4); Functions["ads_set_reward_callback"] = new FunctionInfo(this, 1); if (data?.GeneralInfo?.Major < 2) - { Functions["draw_enable_alphablend"] = new FunctionInfo(this, 1); - } Functions["draw_texture_flush"] = new FunctionInfo(this, 0); Functions["draw_flush"] = new FunctionInfo(this, 0); if (data?.GeneralInfo?.Major >= 2) @@ -1930,25 +1917,25 @@ public void Initialize(UndertaleData data) Functions["physics_fixture_set_polygon_shape"] = new FunctionInfo(this, 1); Functions["physics_fixture_set_chain_shape"] = new FunctionInfo(this, 2); Functions["physics_fixture_add_point"] = new FunctionInfo(this, 3); - Functions["physics_fixture_bind"] = new FunctionInfo(this, 2, true); - Functions["physics_fixture_bind_ext"] = new FunctionInfo(this, 4, true); + Functions["physics_fixture_bind"] = new FunctionInfo(this, 2); + Functions["physics_fixture_bind_ext"] = new FunctionInfo(this, 4); Functions["physics_fixture_delete"] = new FunctionInfo(this, 1); - Functions["physics_apply_force"] = new FunctionInfo(this, 4, true); - Functions["physics_apply_impulse"] = new FunctionInfo(this, 4, true); - Functions["physics_apply_angular_impulse"] = new FunctionInfo(this, 1, true); - Functions["physics_apply_local_force"] = new FunctionInfo(this, 4, true); - Functions["physics_apply_local_impulse"] = new FunctionInfo(this, 4, true); - Functions["physics_apply_torque"] = new FunctionInfo(this, 1, true); - Functions["physics_mass_properties"] = new FunctionInfo(this, 4, true); - Functions["physics_draw_debug"] = new FunctionInfo(this, 0, true); - Functions["physics_test_overlap"] = new FunctionInfo(this, 4, true); - Functions["physics_remove_fixture"] = new FunctionInfo(this, 2, false); - Functions["physics_get_friction"] = new FunctionInfo(this, 1, true); - Functions["physics_get_density"] = new FunctionInfo(this, 1, true); - Functions["physics_get_restitution"] = new FunctionInfo(this, 1, true); - Functions["physics_set_friction"] = new FunctionInfo(this, 2, true); - Functions["physics_set_density"] = new FunctionInfo(this, 2, true); - Functions["physics_set_restitution"] = new FunctionInfo(this, 2, true); + Functions["physics_apply_force"] = new FunctionInfo(this, 4); + Functions["physics_apply_impulse"] = new FunctionInfo(this, 4); + Functions["physics_apply_angular_impulse"] = new FunctionInfo(this, 1); + Functions["physics_apply_local_force"] = new FunctionInfo(this, 4); + Functions["physics_apply_local_impulse"] = new FunctionInfo(this, 4); + Functions["physics_apply_torque"] = new FunctionInfo(this, 1); + Functions["physics_mass_properties"] = new FunctionInfo(this, 4); + Functions["physics_draw_debug"] = new FunctionInfo(this, 0); + Functions["physics_test_overlap"] = new FunctionInfo(this, 4); + Functions["physics_remove_fixture"] = new FunctionInfo(this, 2); + Functions["physics_get_friction"] = new FunctionInfo(this, 1); + Functions["physics_get_density"] = new FunctionInfo(this, 1); + Functions["physics_get_restitution"] = new FunctionInfo(this, 1); + Functions["physics_set_friction"] = new FunctionInfo(this, 2); + Functions["physics_set_density"] = new FunctionInfo(this, 2); + Functions["physics_set_restitution"] = new FunctionInfo(this, 2); Functions["physics_joint_enable_motor"] = new FunctionInfo(this, 2); Functions["physics_joint_get_value"] = new FunctionInfo(this, 2); Functions["physics_joint_set_value"] = new FunctionInfo(this, 3); @@ -2059,8 +2046,8 @@ public void Initialize(UndertaleData data) Functions["winphone_tile_wide_content"] = new FunctionInfo(this, 2); Functions["winphone_tile_cycle_images"] = new FunctionInfo(this, -1); Functions["winphone_tile_small_background_image"] = new FunctionInfo(this, 1); - Functions["gml_release_mode"] = new FunctionInfo(this, 1, false); - Functions["gml_pragma"] = new FunctionInfo(this, -1, false); + Functions["gml_release_mode"] = new FunctionInfo(this, 1); + Functions["gml_pragma"] = new FunctionInfo(this, -1); Functions["buffer_create"] = new FunctionInfo(this, 3); Functions["buffer_delete"] = new FunctionInfo(this, 1); Functions["buffer_get_type"] = new FunctionInfo(this, 1); @@ -2258,24 +2245,24 @@ public void Initialize(UndertaleData data) Functions["push_get_first_local_notification"] = new FunctionInfo(this, 1); Functions["push_get_next_local_notification"] = new FunctionInfo(this, 1); Functions["push_cancel_local_notification"] = new FunctionInfo(this, 1); - Functions["skeleton_animation_set"] = new FunctionInfo(this, 1, true); - Functions["skeleton_animation_get"] = new FunctionInfo(this, 0, true); - Functions["skeleton_animation_mix"] = new FunctionInfo(this, 3, true); - Functions["skeleton_animation_set_ext"] = new FunctionInfo(this, 2, true); - Functions["skeleton_animation_get_ext"] = new FunctionInfo(this, 1, true); - Functions["skeleton_animation_get_duration"] = new FunctionInfo(this, 1, true); - Functions["skeleton_animation_get_frames"] = new FunctionInfo(this, 1, true); - Functions["skeleton_animation_clear"] = new FunctionInfo(this, 1, true); - Functions["skeleton_skin_set"] = new FunctionInfo(this, 1, true); - Functions["skeleton_skin_get"] = new FunctionInfo(this, 0, true); - Functions["skeleton_attachment_set"] = new FunctionInfo(this, 2, true); - Functions["skeleton_attachment_get"] = new FunctionInfo(this, 1, true); - Functions["skeleton_attachment_create"] = new FunctionInfo(this, 8, true); - Functions["skeleton_collision_draw_set"] = new FunctionInfo(this, 1, true); - Functions["skeleton_bone_data_get"] = new FunctionInfo(this, 2, true); - Functions["skeleton_bone_data_set"] = new FunctionInfo(this, 2, true); - Functions["skeleton_bone_state_get"] = new FunctionInfo(this, 2, true); - Functions["skeleton_bone_state_set"] = new FunctionInfo(this, 2, true); + Functions["skeleton_animation_set"] = new FunctionInfo(this, 1); + Functions["skeleton_animation_get"] = new FunctionInfo(this, 0); + Functions["skeleton_animation_mix"] = new FunctionInfo(this, 3); + Functions["skeleton_animation_set_ext"] = new FunctionInfo(this, 2); + Functions["skeleton_animation_get_ext"] = new FunctionInfo(this, 1); + Functions["skeleton_animation_get_duration"] = new FunctionInfo(this, 1); + Functions["skeleton_animation_get_frames"] = new FunctionInfo(this, 1); + Functions["skeleton_animation_clear"] = new FunctionInfo(this, 1); + Functions["skeleton_skin_set"] = new FunctionInfo(this, 1); + Functions["skeleton_skin_get"] = new FunctionInfo(this, 0); + Functions["skeleton_attachment_set"] = new FunctionInfo(this, 2); + Functions["skeleton_attachment_get"] = new FunctionInfo(this, 1); + Functions["skeleton_attachment_create"] = new FunctionInfo(this, 8); + Functions["skeleton_collision_draw_set"] = new FunctionInfo(this, 1); + Functions["skeleton_bone_data_get"] = new FunctionInfo(this, 2); + Functions["skeleton_bone_data_set"] = new FunctionInfo(this, 2); + Functions["skeleton_bone_state_get"] = new FunctionInfo(this, 2); + Functions["skeleton_bone_state_set"] = new FunctionInfo(this, 2); Functions["draw_skeleton"] = new FunctionInfo(this, 11); Functions["draw_skeleton_time"] = new FunctionInfo(this, 11); Functions["draw_skeleton_instance"] = new FunctionInfo(this, 11); @@ -2283,14 +2270,14 @@ public void Initialize(UndertaleData data) Functions["skeleton_animation_list"] = new FunctionInfo(this, 2); Functions["skeleton_skin_list"] = new FunctionInfo(this, 2); Functions["skeleton_slot_data"] = new FunctionInfo(this, 2); - Functions["skeleton_animation_get_frame"] = new FunctionInfo(this, 1, true); - Functions["skeleton_animation_set_frame"] = new FunctionInfo(this, 2, true); - Functions["skeleton_get_minmax"] = new FunctionInfo(this, 0, true); - Functions["skeleton_get_num_bounds"] = new FunctionInfo(this, 0, true); - Functions["skeleton_get_bounds"] = new FunctionInfo(this, 1, true); - Functions["yyg_player_run"] = new FunctionInfo(this, 4, true); - Functions["yyg_player_restarted"] = new FunctionInfo(this, 0, false); - Functions["yyg_player_launch_args"] = new FunctionInfo(this, 0, false); + Functions["skeleton_animation_get_frame"] = new FunctionInfo(this, 1); + Functions["skeleton_animation_set_frame"] = new FunctionInfo(this, 2); + Functions["skeleton_get_minmax"] = new FunctionInfo(this, 0); + Functions["skeleton_get_num_bounds"] = new FunctionInfo(this, 0); + Functions["skeleton_get_bounds"] = new FunctionInfo(this, 1); + Functions["yyg_player_run"] = new FunctionInfo(this, 4); + Functions["yyg_player_restarted"] = new FunctionInfo(this, 0); + Functions["yyg_player_launch_args"] = new FunctionInfo(this, 0); Functions["extension_stubfunc_real"] = new FunctionInfo(this, -1); Functions["extension_stubfunc_string"] = new FunctionInfo(this, -1); Functions["ps4_share_screenshot_enable"] = new FunctionInfo(this, 1); @@ -2613,8 +2600,8 @@ public void Initialize(UndertaleData data) Functions["tilemap_get_cell_x_at_pixel"] = new FunctionInfo(this, 3); Functions["tilemap_get_cell_y_at_pixel"] = new FunctionInfo(this, 3); Functions["tilemap_clear"] = new FunctionInfo(this, 2); - Functions["draw_tilemap"] = new FunctionInfo(this, 3, true); - Functions["draw_tile"] = new FunctionInfo(this, 5, true); + Functions["draw_tilemap"] = new FunctionInfo(this, 3); + Functions["draw_tile"] = new FunctionInfo(this, 5); Functions["tilemap_set_global_mask"] = new FunctionInfo(this, 1); Functions["tilemap_get_global_mask"] = new FunctionInfo(this, 0); Functions["tilemap_set_mask"] = new FunctionInfo(this, 2); diff --git a/UndertaleModLib/Compiler/Parser.cs b/UndertaleModLib/Compiler/Parser.cs index 4211ffdcc..500c5a503 100644 --- a/UndertaleModLib/Compiler/Parser.cs +++ b/UndertaleModLib/Compiler/Parser.cs @@ -433,6 +433,7 @@ public static Statement ParseTokens(CompileContext context, List to { // Basic initialization remainingStageOne.Clear(); + remainingStageOne.EnsureCapacity(tokens.Count); ErrorMessages.Clear(); context.LocalVars.Clear(); if ((context.Data?.GeneralInfo?.BytecodeVersion ?? 15) >= 15) @@ -446,7 +447,7 @@ public static Statement ParseTokens(CompileContext context, List to tokens.Add(new Lexer.Token(TokenKind.EOF)); // Run first parse stage- basic abstraction into functions and constants - List firstPass = new List(); + List firstPass = new(tokens.Count); bool chainedVariableReference = false; for (int i = 0; i < tokens.Count; i++) @@ -485,7 +486,8 @@ public static Statement ParseTokens(CompileContext context, List to try { val = Convert.ToInt64(t.Content.Substring(t.Content[0] == '$' ? 1 : 2), 16); - } catch (Exception) + } + catch (Exception) { ReportCodeError("Invalid hex literal.", t, false); constant = new ExpressionConstant(0); @@ -1044,11 +1046,26 @@ private static Statement ParseFunctionCall(CompileContext context, bool expressi if (EnsureTokenKind(TokenKind.CloseParen) == null) return null; // Check for proper argument count, at least for builtins - if (context.BuiltInList.Functions.TryGetValue(s.Text, out FunctionInfo fi) && - fi.ArgumentCount != -1 && result.Children.Count != fi.ArgumentCount) - ReportCodeError(string.Format("Function {0} expects {1} arguments, got {2}.", - s.Text, fi.ArgumentCount, result.Children.Count) - , s.Token, false); + if (context.BuiltInList.Functions.TryGetValue(s.Text, out FunctionInfo fi)) + { + if (fi.ArgumentCount < -1) + { + // -2 = at least 1 argument + int minArgCount = -fi.ArgumentCount - 1; + if (result.Children.Count < minArgCount) + { + ReportCodeError(string.Format("Function {0} expects at least {1} arguments, got {2}.", + s.Text, minArgCount, result.Children.Count), + s.Token, false); + } + } + else if (fi.ArgumentCount != -1 && result.Children.Count != fi.ArgumentCount) + { + ReportCodeError(string.Format("Function {0} expects {1} arguments, got {2}.", + s.Text, fi.ArgumentCount, result.Children.Count), + s.Token, false); + } + } return result; } From 43d50152de9f5d2de1258735880a0c23c37f251d Mon Sep 17 00:00:00 2001 From: VladiStep Date: Tue, 9 Jan 2024 17:15:41 +0300 Subject: [PATCH 10/10] Add a "Check built-in functions arguments count" setting. --- UndertaleModLib/Compiler/BuiltinList.cs | 2 + UndertaleModLib/Compiler/Parser.cs | 39 ++++++++++---- UndertaleModTool/MainWindow.xaml.cs | 1 + UndertaleModTool/Settings.cs | 1 + UndertaleModTool/Windows/SettingsWindow.xaml | 52 ++++++++++--------- .../Windows/SettingsWindow.xaml.cs | 12 +++++ 6 files changed, 72 insertions(+), 35 deletions(-) diff --git a/UndertaleModLib/Compiler/BuiltinList.cs b/UndertaleModLib/Compiler/BuiltinList.cs index 01555f92a..95637085e 100644 --- a/UndertaleModLib/Compiler/BuiltinList.cs +++ b/UndertaleModLib/Compiler/BuiltinList.cs @@ -103,6 +103,8 @@ public class BuiltinList public Dictionary Accessors1D = null; public Dictionary Accessors2D = null; + public static bool CheckBuiltinFuncArgCount { get; set; } = true; + public int CurrentID = 0; public int ArgumentID = 0; public int Argument0ID = 0; diff --git a/UndertaleModLib/Compiler/Parser.cs b/UndertaleModLib/Compiler/Parser.cs index 500c5a503..8e6788574 100644 --- a/UndertaleModLib/Compiler/Parser.cs +++ b/UndertaleModLib/Compiler/Parser.cs @@ -397,19 +397,31 @@ private static void ReportCodeError(string msg, Lexer.Token context, bool synchr { if (context != null) { - if (msg.EndsWith(".", StringComparison.InvariantCulture)) - msg = msg.Remove(msg.Length - 1); + int firstDotIndex = msg.IndexOf('.'); + if (firstDotIndex != -1) + msg = msg.Remove(firstDotIndex, 1); if (context.Location != null) { - msg += string.Format(" around line {0}, column {1}", context.Location.Line, context.Location.Column); - } else if (context.Kind == TokenKind.EOF) + string s = string.Format(" around line {0}, column {1}", context.Location.Line, context.Location.Column); + msg = msg.Insert(firstDotIndex, s); + firstDotIndex += s.Length; + } + else if (context.Kind == TokenKind.EOF) { - msg += " around EOF (end of file)"; + msg = msg.Insert(firstDotIndex, " around EOF (end of file)"); + firstDotIndex += " around EOF (end of file)".Length; } - if (context.Content != null && context.Content.Length > 0) - msg += " (" + context.Content + ")"; - ReportCodeError(msg + ".", synchronize); + if (context.Content != null && context.Content.Length > 0) + { + string s = " (" + context.Content + ')'; + msg = msg.Insert(firstDotIndex, s); + firstDotIndex += s.Length; + } + + msg = msg.Insert(firstDotIndex, "."); + + ReportCodeError(msg, synchronize); } else { @@ -1046,7 +1058,8 @@ private static Statement ParseFunctionCall(CompileContext context, bool expressi if (EnsureTokenKind(TokenKind.CloseParen) == null) return null; // Check for proper argument count, at least for builtins - if (context.BuiltInList.Functions.TryGetValue(s.Text, out FunctionInfo fi)) + if (BuiltinList.CheckBuiltinFuncArgCount + && context.BuiltInList.Functions.TryGetValue(s.Text, out FunctionInfo fi)) { if (fi.ArgumentCount < -1) { @@ -1054,14 +1067,18 @@ private static Statement ParseFunctionCall(CompileContext context, bool expressi int minArgCount = -fi.ArgumentCount - 1; if (result.Children.Count < minArgCount) { - ReportCodeError(string.Format("Function {0} expects at least {1} arguments, got {2}.", + ReportCodeError(string.Format("Function {0} expects at least {1} arguments, got {2}.\n\n" + + "If you sure that's a mistake, then you can disable built-in functions " + + "argument count checking in the settings.", s.Text, minArgCount, result.Children.Count), s.Token, false); } } else if (fi.ArgumentCount != -1 && result.Children.Count != fi.ArgumentCount) { - ReportCodeError(string.Format("Function {0} expects {1} arguments, got {2}.", + ReportCodeError(string.Format("Function {0} expects {1} arguments, got {2}.\n\n" + + "If you sure that's a mistake, then you can disable built-in functions " + + "argument count checking in the settings.", s.Text, fi.ArgumentCount, result.Children.Count), s.Token, false); } diff --git a/UndertaleModTool/MainWindow.xaml.cs b/UndertaleModTool/MainWindow.xaml.cs index df1d9ad3b..a8907b3ac 100644 --- a/UndertaleModTool/MainWindow.xaml.cs +++ b/UndertaleModTool/MainWindow.xaml.cs @@ -471,6 +471,7 @@ private async void Window_Loaded(object sender, RoutedEventArgs e) RunGMSDebuggerItem.Visibility = Settings.Instance.ShowDebuggerOption ? Visibility.Visible : Visibility.Collapsed; + UndertaleModLib.Compiler.BuiltinList.CheckBuiltinFuncArgCount = Settings.Instance.CheckBuiltinFunctionsArgCount; } public Dictionary childFiles = new Dictionary(); diff --git a/UndertaleModTool/Settings.cs b/UndertaleModTool/Settings.cs index bb8d26825..c2c79b8f1 100644 --- a/UndertaleModTool/Settings.cs +++ b/UndertaleModTool/Settings.cs @@ -53,6 +53,7 @@ public class Settings public bool EnableDarkMode { get; set; } = false; public bool ShowDebuggerOption { get; set; } = false; + public bool CheckBuiltinFunctionsArgCount { get; set; } = true; public static Settings Instance; diff --git a/UndertaleModTool/Windows/SettingsWindow.xaml b/UndertaleModTool/Windows/SettingsWindow.xaml index dcdc08c04..33316d307 100644 --- a/UndertaleModTool/Windows/SettingsWindow.xaml +++ b/UndertaleModTool/Windows/SettingsWindow.xaml @@ -37,6 +37,7 @@ + @@ -60,41 +61,44 @@ - + + + + - - + + - + - - - + + + - - - + + + - - - + + + - + - + - - + + - - + + - - + + - - Open application data folder - Update app to latest commit + + Open application data folder + Update app to latest commit diff --git a/UndertaleModTool/Windows/SettingsWindow.xaml.cs b/UndertaleModTool/Windows/SettingsWindow.xaml.cs index f0d7aa257..04f18633f 100644 --- a/UndertaleModTool/Windows/SettingsWindow.xaml.cs +++ b/UndertaleModTool/Windows/SettingsWindow.xaml.cs @@ -209,6 +209,18 @@ public static bool ShowDebuggerOption } } + public static bool CheckBuiltinFunctionsArgCount + { + get => Settings.Instance.CheckBuiltinFunctionsArgCount; + set + { + Settings.Instance.CheckBuiltinFunctionsArgCount = value; + Settings.Save(); + + UndertaleModLib.Compiler.BuiltinList.CheckBuiltinFuncArgCount = value; + } + } + public bool UpdateButtonEnabled { get => UpdateAppButton.IsEnabled;